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