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