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