2
* $Id: arjtypes.c,v 1.7 2004/06/18 16:19:37 andrew_belov Exp $
3
* ---------------------------------------------------------------------------
4
* This module provides some multiplatform property types which cover both DOS
5
* (as internally involved in ARJ) and UNIX requirements.
13
DEBUGHDR(__FILE__) /* Debug information block */
15
/* Timestamp macros */
17
#define get_tx(m,d,h,n) (((unsigned long)(m)<<21)+((unsigned long)(d)<<16)+((unsigned long)(h)<<11)+((n)<<5))
18
#define get_tstamp(y,m,d,h,n,s) ((((unsigned long)((y)-1980))<<25)+get_tx((m),(d),(h),(n))+((s)/2))
20
#define ts_year(ts) ((unsigned int)(((ts)>>25)&0x7f)+1980)
21
#define ts_month(ts) ((unsigned int)((ts)>>21)&0x0f) /* 1..12 means Jan..Dec */
22
#define ts_day(ts) ((unsigned int)((ts)>>16)&0x1f) /* 1..31 means 1st..31st */
23
#define ts_hour(ts) ((unsigned int)((ts)>>11)&0x1f)
24
#define ts_min(ts) ((unsigned int)((ts)>>5)&0x3f)
25
#define ts_sec(ts) ((unsigned int)(((ts)&0x1f)*2))
27
static char monthdays[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
29
/* Q&D helper macro */
31
#define is_unix(host_os) (host_os==OS_UNIX||host_os==OS_NEXT)
33
/* Timestamp storage structures */
36
static char time_list_format[]="%04u-%02u-%02u %02u:%02u:%02u";
43
/* Parses a file mode specifier from the archive. The idea is to allow
44
creation of DOS attributes under UNIX, but no features exist for
45
DOS->UNIX conversion. */
47
void fm_store(struct file_mode *dest, int host_os, int mode)
49
if(host_os==OS_SPECIAL)
50
dest->dos=dest->native=mode;
55
if(is_unix(OS)&&!(mode&FATTR_IWUSR))
56
dest->dos|=FATTR_RDONLY;
58
else /* Assume a DOS-style system */
59
dest->dos=dest->native=mode;
62
/* Retrieves a native file mode corresponding to the host OS */
64
unsigned int fm_native(struct file_mode *fm, int host_os)
66
return(is_unix(host_os)?fm->native:fm->dos);
73
/* Returns 1 if there's a leap year */
75
static int isleapyear(int year)
86
/* A "home-brew" implemenation of localtime(). This is for cases when we have a TZ_VAR
90
static struct tm *ununixtime(unsigned long *pt)
91
#define localtime(t) ununixtime((unsigned long *)(t))
93
unsigned long unixtime, timepart;
94
int year, month, mdays;
100
timepart=unixtime%86400L; unixtime/=86400L;
101
for(year=1970; 365+isleapyear(year)<=unixtime; year++)
102
unixtime-=365+isleapyear(year);
103
stm.tm_year=year-1900;
108
mdays=isleapyear(year)?29:28;
110
mdays=monthdays[month];
111
if(mdays>unixtime||month==11)
117
stm.tm_mday=unixtime+1;
118
stm.tm_hour=timepart/3600;
119
stm.tm_min=timepart/60%60;
120
stm.tm_sec=timepart%60;
125
/* Converts a UNIX timestamp to the DOS style */
127
static unsigned long ts_unix2dos(const long ts)
131
stm=localtime((time_t*)&ts);
132
return(get_tstamp(stm->tm_year+1900, stm->tm_mon+1, stm->tm_mday,
133
stm->tm_hour, stm->tm_min, stm->tm_sec));
136
/* Creates a Unix timestamp from the given date and time */
138
static unsigned long mk_unixtime(int y, int m, int d, int hh, int mm, int ss)
142
/* Clash with NetBSD/x86-64 patch: leaving rc as unsigned long still permits
143
to escape the year 2038 problem in favor of year 2106 problem, while a
144
dedicated time_t structure can be expected as a 64-bit value on relevant
145
platforms -- ASR fix 25/01/2004 */
150
long shiftd1, shiftd2;
158
/* The following piece of code is rather paranoid in 16/32-bit world, where the
159
timestamps are limited to year 2108. */
160
#if defined(__32BIT__)||defined(TILED)
176
u=1096+(y-1973)/4*1461L+((y-1973)%4)*365L;
181
u+=(int)monthdays[i-1];
185
rc=86400*(unsigned long)(u+d-1)+(unsigned long)hh*3600+(unsigned long)mm*60+(unsigned long)ss;
186
/* If we have to use the timezone variable, do it now */
194
debug_assert(stm!=NULL); /* LIBCS.DLL returns NULL for unixtime beyond
196
tzshift=(long)stm->tm_hour*3600+(long)stm->tm_min*60;
197
shiftd1=stm->tm_mday;
199
debug_assert(stm!=NULL);
200
shiftd2=stm->tm_mday;
201
/* Local time overruns GMT, add 24 hours for safety */
202
if(shiftd1<shiftd2&&shiftd1==1&&shiftd2>=28)
204
else if(shiftd1>shiftd2&&shiftd1>=28&&shiftd2==1)
206
else if(shiftd1>shiftd2)
208
else if(shiftd1<shiftd2)
210
tzshift-=(long)stm->tm_hour*3600+(long)stm->tm_min*60;
213
/* Fix the timezone if it does not roll over the zero */
214
return((tzshift>0&&rc<tzshift)?rc:rc-tzshift);
217
/* Converts a DOS timestamp to the UNIX representation */
219
static unsigned long ts_dos2unix(unsigned long ts)
221
unsigned int y, m, d, hh, mm, ss;
231
/* TODO: These assertions must be replaced by run-time check for incorrect timestamps
232
like 31/15/2063 or 00/00/1980, since month array is 1...12 only. */
234
debug_assert(m>=1&&m<=12);
235
debug_assert(d>=1&&d<=31);
237
return(mk_unixtime(y, m, d, hh, mm, ss));
240
/* Stores a timestamp */
242
void ts_store(struct timestamp *dest, int host_os, unsigned long value)
244
if(host_os==OS_SPECIAL)
245
dest->dos=dest->unixtime=value;
246
else if(is_unix(host_os))
248
dest->unixtime=value;
249
dest->dos=ts_unix2dos(value);
254
dest->unixtime=ts_dos2unix(value);
258
/* Retrieves a native timestamp corresponding to the host OS */
260
unsigned long ts_native(struct timestamp *ts, int host_os)
262
return(is_unix(host_os)?ts->unixtime:ts->dos);
265
/* Compares two timestamps */
267
int ts_cmp(struct timestamp *ts1, struct timestamp *ts2)
269
unsigned long tsn1, tsn2;
271
tsn1=ts_native(ts1, OS);
272
tsn2=ts_native(ts2, OS);
281
#if SFX_LEVEL>=ARJ||defined(REARJ)
283
/* Produces an ARJ timestamp from the given date */
285
void make_timestamp(struct timestamp *dest, int y, int m, int d, int hh, int mm, int ss)
287
dest->unixtime=mk_unixtime(y, m, d, hh, mm, ss);
288
dest->dos=ts_unix2dos(dest->unixtime);
293
#if SFX_LEVEL>=ARJSFX
295
/* Restores the given timestamp to character form */
297
void timestamp_to_str(char *str, struct timestamp *ts)
301
stm=localtime((time_t *)&ts->unixtime);
302
/* Workaround for a MS C v 7.0 CRT bug */
303
#if TARGET==DOS&&COMPILER==MSC&&_MSC_VER==700
304
if(stm->tm_year<70) /* 31 -> 101 */
307
sprintf(str, time_list_format, stm->tm_year+1900, stm->tm_mon+1, stm->tm_mday,
308
stm->tm_hour, stm->tm_min, stm->tm_sec);