time_t-pointer-conversions-20040908
[openafs.git] / src / util / ktime.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <sys/types.h>
17 #include <stdio.h>
18 #include <time.h>
19 #include <ctype.h>
20 #ifdef AFS_NT40_ENV
21 #include <malloc.h>
22 #endif
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #else
26 #ifdef HAVE_STRINGS_H
27 #include <strings.h>
28 #endif
29 #endif
30 #include <stdlib.h>
31 #include "afsutil.h"
32
33 #include "ktime.h"
34
35 /* some date parsing routines */
36
37 struct token {
38     struct token *next;
39     char *key;
40 };
41
42 static char *day[] = {
43     "sun",
44     "mon",
45     "tue",
46     "wed",
47     "thu",
48     "fri",
49     "sat"
50 };
51
52 /* free token list returned by parseLine */
53 #ifdef undef
54 static
55 LocalFreeTokens(alist)
56      register struct token *alist;
57 {
58     register struct token *nlist;
59     for (; alist; alist = nlist) {
60         nlist = alist->next;
61         free(alist->key);
62         free(alist);
63     }
64     return 0;
65 }
66 #endif
67
68 static int
69 space(int x)
70 {
71     if (x == 0 || x == ' ' || x == '\t' || x == '\n')
72         return 1;
73     else
74         return 0;
75 }
76
77 static int
78 LocalParseLine(char *aline, struct token **alist)
79 {
80     char tbuffer[256];
81     register char *tptr = NULL;
82     int inToken;
83     struct token *first, *last;
84     register struct token *ttok;
85     register int tc;
86
87     inToken = 0;                /* not copying token chars at start */
88     first = NULL;
89     last = NULL;
90     while (1) {
91         tc = *aline++;
92         if (tc == 0 || space(tc)) {     /* terminating null gets us in here, too */
93             if (inToken) {
94                 inToken = 0;    /* end of this token */
95                 *tptr++ = 0;
96                 ttok = (struct token *)malloc(sizeof(struct token));
97                 ttok->next = NULL;
98                 ttok->key = (char *)malloc(strlen(tbuffer) + 1);
99                 strcpy(ttok->key, tbuffer);
100                 if (last) {
101                     last->next = ttok;
102                     last = ttok;
103                 } else
104                     last = ttok;
105                 if (!first)
106                     first = ttok;
107             }
108         } else {
109             /* an alpha character */
110             if (!inToken) {
111                 tptr = tbuffer;
112                 inToken = 1;
113             }
114             if (tptr - tbuffer >= sizeof(tbuffer))
115                 return -1;      /* token too long */
116             *tptr++ = tc;
117         }
118         if (tc == 0) {
119             /* last token flushed 'cause space(0) --> true */
120             if (last)
121                 last->next = NULL;
122             *alist = first;
123             return 0;
124         }
125     }
126 }
127
128 /* keyword database for periodic date parsing */
129 static struct ptemp {
130     char *key;
131     afs_int32 value;
132 } ptkeys[] = {
133     {
134     "sun", 0x10000,}, {
135     "mon", 0x10001,}, {
136     "tue", 0x10002,}, {
137     "wed", 0x10003,}, {
138     "thu", 0x10004,}, {
139     "fri", 0x10005,}, {
140     "sat", 0x10006,}, {
141     "sunday", 0x10000,}, {
142     "monday", 0x10001,}, {
143     "tuesday", 0x10002,}, {
144     "wednesday", 0x10003,}, {
145     "thursday", 0x10004,}, {
146     "thur", 0x10004,}, {
147     "friday", 0x10005,}, {
148     "saturday", 0x10006,}, {
149     "am", 0x20000,}, {
150     "pm", 0x20001,}, {
151     "a.m.", 0x20000,}, {
152     "p.m.", 0x20001,}, {
153     0, 0,}
154 };
155
156 /* ktime_DateOf
157  * entry:
158  *      atime - time in seconds (Unix std)
159  * exit:
160  *      return value - ptr to time in text form. Ptr is to a static string.
161  */
162
163 char *
164 ktime_DateOf(afs_int32 atime)
165 {
166     static char tbuffer[30];
167     register char *tp;
168     time_t t = atime;
169     tp = ctime(&t);
170     if (tp) {
171         strcpy(tbuffer, tp);
172         tbuffer[24] = 0;        /* get rid of new line */
173     } else
174         strcpy(tbuffer, "BAD TIME");
175     return tbuffer;
176 }
177
178 /* ParseTime
179  *      parse 12:33:12 or 12:33 or 12 into ktime structure
180  * entry:
181  *      astr - ptr to string to be parsed
182  *      ak - ptr to struct for return value.
183  * exit:
184  *      0 - ak holds parsed time.
185  *      -1 - error in format
186  */
187
188 static int
189 ParseTime(register struct ktime *ak, register char *astr)
190 {
191     int field;
192     afs_int32 temp;
193     register char *tp;
194     register int tc;
195
196     field = 0;                  /* 0=hour, 1=min, 2=sec */
197     temp = 0;
198
199     ak->mask |= (KTIME_HOUR | KTIME_MIN | KTIME_SEC);
200     for (tp = astr;;) {
201         tc = *tp++;
202         if (tc == 0 || tc == ':') {
203             if (field == 0)
204                 ak->hour = temp;
205             else if (field == 1)
206                 ak->min = temp;
207             else if (field == 2)
208                 ak->sec = temp;
209             temp = 0;
210             field++;
211             if (tc == 0)
212                 break;
213             continue;
214         } else if (!isdigit(tc))
215             return -1;          /* syntax error */
216         else {
217             /* digit */
218             temp *= 10;
219             temp += tc - '0';
220         }
221     }
222     if (ak->hour >= 24 || ak->min >= 60 || ak->sec >= 60)
223         return -1;
224     return 0;
225 }
226
227 afs_int32
228 ktime_Str2int32(register char *astr)
229 {
230     struct ktime tk;
231
232     memset(&tk, 0, sizeof(tk));
233     if (ParseTime(&tk, astr))
234         return (-1);            /* syntax error */
235
236     return ((tk.hour * 60 + tk.min) * 60 + tk.sec);
237 }
238
239 /* ktime_ParsePeriodic
240  *      Parses periodic date of form
241  *              now | never | at [time spec] | every [time spec]
242  *      where [time spec] is a ktime string.
243  * entry:
244  *      adate - string to be parsed
245  *      ak - ptr to structure for returned ktime
246  * exit:
247  *      0 - parsed ktime in ak
248  *      -1 - specification error
249  */
250
251 /* -1 means error, 0 means now, otherwise returns time of next event */
252 int
253 ktime_ParsePeriodic(char *adate, register struct ktime *ak)
254 {
255     struct token *tt;
256     register afs_int32 code;
257     struct ptemp *tp;
258
259     memset(ak, 0, sizeof(*ak));
260     code = LocalParseLine(adate, &tt);
261     if (code)
262         return -1;
263     for (; tt; tt = tt->next) {
264         /* look at each token */
265         if (strcmp(tt->key, "now") == 0) {
266             ak->mask |= KTIME_NOW;
267             return 0;
268         }
269         if (strcmp(tt->key, "never") == 0) {
270             ak->mask |= KTIME_NEVER;
271             return 0;
272         }
273         if (strcmp(tt->key, "at") == 0)
274             continue;
275         if (strcmp(tt->key, "every") == 0)
276             continue;
277         if (isdigit(tt->key[0])) {
278             /* parse a time */
279             code = ParseTime(ak, tt->key);
280             if (code)
281                 return -1;
282             continue;
283         }
284         /* otherwise use keyword table */
285         for (tp = ptkeys;; tp++) {
286             if (tp->key == NULL) {
287                 return -1;
288             }
289             if (strcmp(tp->key, tt->key) == 0)
290                 break;
291         }
292         /* now look at tp->value to see what we've got */
293         if ((tp->value >> 16) == 1) {
294             /* a day */
295             ak->mask |= KTIME_DAY;
296             ak->day = tp->value & 0xff;
297         }
298         if ((tp->value >> 16) == 2) {
299             /* am or pm token */
300             if ((tp->value & 0xff) == 1) {
301                 /* pm */
302                 if (!(ak->mask & KTIME_HOUR))
303                     return -1;
304                 if (ak->hour < 12)
305                     ak->hour += 12;
306                 /* 12 is 12 PM */
307                 else if (ak->hour != 12)
308                     return -1;
309             } else {
310                 /* am is almost a noop, except that we map 12:01 am to 0:01 */
311                 if (ak->hour > 12)
312                     return -1;
313                 if (ak->hour == 12)
314                     ak->hour = 0;
315             }
316         }
317     }
318     return 0;
319 }
320
321 /* ktime_DisplayString
322  *      Display ktime structure as English that could go into the ktime parser
323  * entry:
324  *      aparm - ktime to be converted to string
325  *      astring - ptr to string, for the result
326  * exit:
327  *      0 - astring contains ktime string.
328  */
329 int
330 ktime_DisplayString(struct ktime *aparm, register char *astring)
331 {
332     char tempString[50];
333
334     if (aparm->mask & KTIME_NEVER) {
335         strcpy(astring, "never");
336         return 0;
337     } else if (aparm->mask & KTIME_NOW) {
338         strcpy(astring, "now");
339         return 0;
340     } else {
341         strcpy(astring, "at");
342         if (aparm->mask & KTIME_DAY) {
343             strcat(astring, " ");
344             strcat(astring, day[aparm->day]);
345         }
346         if (aparm->mask & KTIME_HOUR) {
347             if (aparm->hour > 12)
348                 sprintf(tempString, " %d", aparm->hour - 12);
349             else if (aparm->hour == 0)
350                 strcpy(tempString, " 12");
351             else
352                 sprintf(tempString, " %d", aparm->hour);
353             strcat(astring, tempString);
354         }
355         if (aparm->mask & KTIME_MIN) {
356             sprintf(tempString, ":%02d", aparm->min);
357             strcat(astring, tempString);
358         }
359         if ((aparm->mask & KTIME_SEC) && aparm->sec != 0) {
360             sprintf(tempString, ":%02d", aparm->sec);
361             strcat(astring, tempString);
362         }
363         if (aparm->mask & KTIME_HOUR) {
364             if (aparm->hour >= 12)
365                 strcat(astring, " pm");
366             else
367                 strcat(astring, " am");
368         }
369     }
370     return 0;
371 }
372
373 /* get next time that matches ktime structure after time afrom */
374 afs_int32
375 ktime_next(struct ktime * aktime, afs_int32 afrom)
376 {
377     /* try by guessing from now */
378     struct tm *tsp;
379     time_t start;               /* time to start looking */
380     time_t probe;               /* a placeholder to use for advancing day to day */
381     time_t time_next;           /* actual UTC time of probe, with time of day set */
382     afs_int32 tmask;
383     struct ktime_date tdate;
384
385     start = afrom + time(0);    /* time to start search */
386     tmask = aktime->mask;
387
388     /* handle some special cases */
389     if (tmask & KTIME_NEVER)
390         return 0x7fffffff;
391     if (tmask & KTIME_NOW)
392         return 0;
393
394     /* Use probe to fill in members of *tsp. Add 23 hours each iteration until 
395      * time_next is correct. Only add 23 hrs to avoid skipping spring 
396      * daylight savings time day */
397     for (probe = start;; probe += (23 * 3600)) {
398         tsp = localtime(&probe);        /* find out what UTC time "probe" is */
399
400         tdate.year = tsp->tm_year;
401         tdate.month = tsp->tm_mon + 1;
402         tdate.day = tsp->tm_mday;
403         tdate.mask =
404             KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR
405             | KTIMEDATE_MIN | KTIMEDATE_SEC;
406         tdate.hour = aktime->hour;      /* edit in our changes */
407         tdate.min = aktime->min;
408         tdate.sec = aktime->sec;
409         time_next = ktime_InterpretDate(&tdate);        /* Convert back to UTC time */
410         if (time_next < start)
411             continue;           /* "probe" time is already past */
412         if ((tmask & KTIME_DAY) == 0)   /* don't care about day, we're done */
413             break;
414         tsp = localtime(&time_next);
415         if (tsp->tm_wday == aktime->day)
416             break;              /* day matches, we're done */
417     }
418     return time_next;
419 }
420
421
422 /* compare date in both formats, and return as in strcmp */
423 #ifdef undef
424 static int
425 KTimeCmp(register struct ktime *aktime, register struct tm *atm)
426 {
427     register afs_int32 tmask;
428
429     /* don't compare day of the week, since we can't tell the
430      * order in a cyclical set.  Caller must check for equality, if
431      * she cares */
432     tmask = aktime->mask;
433     if (tmask & KTIME_HOUR) {
434         if (aktime->hour > atm->tm_hour)
435             return 1;
436         if (aktime->hour < atm->tm_hour)
437             return -1;
438     }
439     if (tmask & KTIME_MIN) {
440         if (aktime->min > atm->tm_min)
441             return 1;
442         if (aktime->min < atm->tm_min)
443             return -1;
444     }
445     if (tmask & KTIME_SEC) {
446         if (aktime->sec > atm->tm_sec)
447             return 1;
448         if (aktime->sec < atm->tm_sec)
449             return -1;
450     }
451     return 0;
452 }
453 #endif
454
455 /* compare date in both formats, and return as in strcmp */
456 static int
457 KDateCmp(register struct ktime_date *akdate, register struct tm *atm)
458 {
459     if (akdate->year > atm->tm_year)
460         return 1;
461     if (akdate->year < atm->tm_year)
462         return -1;
463     if (akdate->month > atm->tm_mon)
464         return 1;
465     if (akdate->month < atm->tm_mon)
466         return -1;
467     if (akdate->day > atm->tm_mday)
468         return 1;
469     if (akdate->day < atm->tm_mday)
470         return -1;
471     if (akdate->mask & KTIMEDATE_HOUR) {
472         if (akdate->hour > atm->tm_hour)
473             return 1;
474         if (akdate->hour < atm->tm_hour)
475             return -1;
476     }
477     if (akdate->mask & KTIMEDATE_MIN) {
478         if (akdate->min > atm->tm_min)
479             return 1;
480         if (akdate->min < atm->tm_min)
481             return -1;
482     }
483     if (akdate->mask & KTIMEDATE_SEC) {
484         if (akdate->sec > atm->tm_sec)
485             return 1;
486         if (akdate->sec < atm->tm_sec)
487             return -1;
488     }
489     return 0;
490 }
491
492 /* ktime_ParseDate
493  *      parse date string into ktime_date structure
494  * entry:
495  *      adate - string to be parsed
496  *      akdate - ptr to ktime_date for result
497  * exit:
498  *      0 - akdate contains converted date
499  *      -1 - parsing failure
500  */
501
502 static afs_int32
503 ktime_ParseDate(char *adate, struct ktime_date *akdate)
504 {
505     int code;
506     afs_int32 month, day2, year, hour, min, sec;
507     char never[7];
508     char c[2];
509
510     lcstring(never, adate, sizeof(never));
511     if (strcmp(never, "never") == 0)
512         akdate->mask = KTIMEDATE_NEVER;
513     else if (strcmp(never, "now") == 0)
514         akdate->mask = KTIMEDATE_NOW;
515     else
516         akdate->mask = 0;
517     if (akdate->mask)
518         return 0;
519
520
521     code =
522         sscanf(adate, "%d / %d / %d %d : %d : %d%1s", &month, &day2, &year,
523                &hour, &min, &sec, &c[0]);
524     if (code != 6) {
525         sec = 0;
526         code =
527             sscanf(adate, "%d / %d / %d %d : %d%1s", &month, &day2, &year,
528                    &hour, &min, &c[0]);
529         if (code != 5) {
530             hour = min = 0;
531             code = sscanf(adate, "%d / %d / %d%1s", &month, &day2, &year, &c[0]);
532             if (code != 3) {
533                 return -1;
534             }
535         }
536     }
537
538     if ((year < 0) || (month < 1) || (month > 12) || (day2 < 1) || (day2 > 31) ||       /* more or less */
539         (hour < 0) || (hour > 23) || (min < 0) || (min > 59) || (sec < 0)
540         || (sec > 59))
541         return -2;
542
543     if (year < 69)
544         year += 100;            /* make 1/1/1 => Jan 1, 2001 */
545     else if (year >= 1900)
546         year -= 1900;           /* allow 1/1/2001 to work */
547     else if (year > 99)
548         return -2;              /* only allow 2 or 4 digit years */
549
550     akdate->mask =
551         KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR |
552         KTIMEDATE_MIN | KTIMEDATE_SEC;
553
554     akdate->year = year;
555     akdate->month = month;
556     akdate->day = day2;
557     akdate->hour = hour;
558     akdate->min = min;
559     akdate->sec = sec;
560
561     /* done successfully */
562     return 0;
563 }
564
565 /* ktime_DateToInt32
566  *      Converts a ktime date string into an afs_int32
567  * entry:
568  *      adate - ktime date string
569  *      aint32 - ptr to afs_int32
570  * exit:
571  *      0 - aint32 contains converted date.
572  */
573
574 afs_int32
575 ktime_DateToInt32(char *adate, afs_int32 * aint32)
576 {
577     struct ktime_date tdate;
578     register afs_int32 code;
579
580     /* parse the date into a ktime_date structure */
581     code = ktime_ParseDate(adate, &tdate);
582     if (code)
583         return code;            /* failed to parse */
584
585     code = ktime_InterpretDate(&tdate); /* interpret as seconds since 1970 */
586     *aint32 = code;             /* return it */
587     return 0;                   /* and declare no errors */
588 }
589
590 /* get useful error message to print about date input format */
591 char *
592 ktime_GetDateUsage(void)
593 {
594     return "date format is 'mm/dd/yy [hh:mm]', using a 24 hour clock";
595 }
596
597
598 /* ktime_InterpretDate
599  *      Converts ktime_date to an afs_int32
600  * entry:
601  *      akdate - date to be converted/interpreted
602  * exit:
603  *      returns KTIMEDATE_NEVERDATE - if never flag was set, or
604  *      date converted to afs_int32.
605  */
606
607 afs_int32
608 ktime_InterpretDate(struct ktime_date * akdate)
609 {
610     register afs_uint32 tresult;
611     register afs_uint32 tbit;
612     time_t temp;
613     register struct tm *tsp;
614
615     if (akdate->mask & KTIMEDATE_NOW)
616         return time(0);
617     if (akdate->mask & KTIMEDATE_NEVER)
618         return KTIMEDATE_NEVERDATE;
619
620     tbit = 1 << 30;             /* start off at max signed result */
621     tresult = 0;                /* result to return */
622     while (tbit > 0) {
623         temp = tresult + tbit;  /* see if adding this bit keeps us < akdate */
624         tsp = localtime(&temp);
625         tsp->tm_mon++;
626 #ifdef notdef
627         if (tsp->tm_mon == 0) {
628             tsp->tm_mon = 12;
629             tsp->tm_year--;
630         }
631 #endif
632         if (KDateCmp(akdate, tsp) >= 0) {
633             /* if temp still represents earlier than date than we're searching
634              * for, add in bit as increment, otherwise use old value and look
635              * for smaller increment */
636             tresult = temp;
637         }
638         tbit = tbit >> 1;       /* try next bit */
639     }
640
641     return tresult;
642 }