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