reindent-20030715
[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     tp = ctime((time_t *) & atime);
169     if (tp) {
170         strcpy(tbuffer, tp);
171         tbuffer[24] = 0;        /* get rid of new line */
172     } else
173         strcpy(tbuffer, "BAD TIME");
174     return tbuffer;
175 }
176
177 /* ParseTime
178  *      parse 12:33:12 or 12:33 or 12 into ktime structure
179  * entry:
180  *      astr - ptr to string to be parsed
181  *      ak - ptr to struct for return value.
182  * exit:
183  *      0 - ak holds parsed time.
184  *      -1 - error in format
185  */
186
187 static int
188 ParseTime(register struct ktime *ak, register char *astr)
189 {
190     int field;
191     afs_int32 temp;
192     register char *tp;
193     register int tc;
194
195     field = 0;                  /* 0=hour, 1=min, 2=sec */
196     temp = 0;
197
198     ak->mask |= (KTIME_HOUR | KTIME_MIN | KTIME_SEC);
199     for (tp = astr;;) {
200         tc = *tp++;
201         if (tc == 0 || tc == ':') {
202             if (field == 0)
203                 ak->hour = temp;
204             else if (field == 1)
205                 ak->min = temp;
206             else if (field == 2)
207                 ak->sec = temp;
208             temp = 0;
209             field++;
210             if (tc == 0)
211                 break;
212             continue;
213         } else if (!isdigit(tc))
214             return -1;          /* syntax error */
215         else {
216             /* digit */
217             temp *= 10;
218             temp += tc - '0';
219         }
220     }
221     if (ak->hour >= 24 || ak->min >= 60 || ak->sec >= 60)
222         return -1;
223     return 0;
224 }
225
226 afs_int32
227 ktime_Str2int32(register char *astr)
228 {
229     struct ktime tk;
230
231     memset(&tk, 0, sizeof(tk));
232     if (ParseTime(&tk, astr))
233         return (-1);            /* syntax error */
234
235     return ((tk.hour * 60 + tk.min) * 60 + tk.sec);
236 }
237
238 /* ktime_ParsePeriodic
239  *      Parses periodic date of form
240  *              now | never | at [time spec] | every [time spec]
241  *      where [time spec] is a ktime string.
242  * entry:
243  *      adate - string to be parsed
244  *      ak - ptr to structure for returned ktime
245  * exit:
246  *      0 - parsed ktime in ak
247  *      -1 - specification error
248  */
249
250 /* -1 means error, 0 means now, otherwise returns time of next event */
251 int
252 ktime_ParsePeriodic(char *adate, register struct ktime *ak)
253 {
254     struct token *tt;
255     register afs_int32 code;
256     struct ptemp *tp;
257
258     memset(ak, 0, sizeof(*ak));
259     code = LocalParseLine(adate, &tt);
260     if (code)
261         return -1;
262     for (; tt; tt = tt->next) {
263         /* look at each token */
264         if (strcmp(tt->key, "now") == 0) {
265             ak->mask |= KTIME_NOW;
266             return 0;
267         }
268         if (strcmp(tt->key, "never") == 0) {
269             ak->mask |= KTIME_NEVER;
270             return 0;
271         }
272         if (strcmp(tt->key, "at") == 0)
273             continue;
274         if (strcmp(tt->key, "every") == 0)
275             continue;
276         if (isdigit(tt->key[0])) {
277             /* parse a time */
278             code = ParseTime(ak, tt->key);
279             if (code)
280                 return -1;
281             continue;
282         }
283         /* otherwise use keyword table */
284         for (tp = ptkeys;; tp++) {
285             if (tp->key == NULL) {
286                 return -1;
287             }
288             if (strcmp(tp->key, tt->key) == 0)
289                 break;
290         }
291         /* now look at tp->value to see what we've got */
292         if ((tp->value >> 16) == 1) {
293             /* a day */
294             ak->mask |= KTIME_DAY;
295             ak->day = tp->value & 0xff;
296         }
297         if ((tp->value >> 16) == 2) {
298             /* am or pm token */
299             if ((tp->value & 0xff) == 1) {
300                 /* pm */
301                 if (!(ak->mask & KTIME_HOUR))
302                     return -1;
303                 if (ak->hour < 12)
304                     ak->hour += 12;
305                 /* 12 is 12 PM */
306                 else if (ak->hour != 12)
307                     return -1;
308             } else {
309                 /* am is almost a noop, except that we map 12:01 am to 0:01 */
310                 if (ak->hour > 12)
311                     return -1;
312                 if (ak->hour == 12)
313                     ak->hour = 0;
314             }
315         }
316     }
317     return 0;
318 }
319
320 /* ktime_DisplayString
321  *      Display ktime structure as English that could go into the ktime parser
322  * entry:
323  *      aparm - ktime to be converted to string
324  *      astring - ptr to string, for the result
325  * exit:
326  *      0 - astring contains ktime string.
327  */
328 int
329 ktime_DisplayString(struct ktime *aparm, register char *astring)
330 {
331     char tempString[50];
332
333     if (aparm->mask & KTIME_NEVER) {
334         strcpy(astring, "never");
335         return 0;
336     } else if (aparm->mask & KTIME_NOW) {
337         strcpy(astring, "now");
338         return 0;
339     } else {
340         strcpy(astring, "at");
341         if (aparm->mask & KTIME_DAY) {
342             strcat(astring, " ");
343             strcat(astring, day[aparm->day]);
344         }
345         if (aparm->mask & KTIME_HOUR) {
346             if (aparm->hour > 12)
347                 sprintf(tempString, " %d", aparm->hour - 12);
348             else if (aparm->hour == 0)
349                 strcpy(tempString, " 12");
350             else
351                 sprintf(tempString, " %d", aparm->hour);
352             strcat(astring, tempString);
353         }
354         if (aparm->mask & KTIME_MIN) {
355             sprintf(tempString, ":%02d", aparm->min);
356             strcat(astring, tempString);
357         }
358         if ((aparm->mask & KTIME_SEC) && aparm->sec != 0) {
359             sprintf(tempString, ":%02d", aparm->sec);
360             strcat(astring, tempString);
361         }
362         if (aparm->mask & KTIME_HOUR) {
363             if (aparm->hour >= 12)
364                 strcat(astring, " pm");
365             else
366                 strcat(astring, " am");
367         }
368     }
369     return 0;
370 }
371
372 /* get next time that matches ktime structure after time afrom */
373 afs_int32
374 ktime_next(struct ktime * aktime, afs_int32 afrom)
375 {
376     /* try by guessing from now */
377     struct tm *tsp;
378     time_t start;               /* time to start looking */
379     time_t probe;               /* a placeholder to use for advancing day to day */
380     time_t time_next;           /* actual UTC time of probe, with time of day set */
381     afs_int32 tmask;
382     struct ktime_date tdate;
383
384     start = afrom + time(0);    /* time to start search */
385     tmask = aktime->mask;
386
387     /* handle some special cases */
388     if (tmask & KTIME_NEVER)
389         return 0x7fffffff;
390     if (tmask & KTIME_NOW)
391         return 0;
392
393     /* Use probe to fill in members of *tsp. Add 23 hours each iteration until 
394      * time_next is correct. Only add 23 hrs to avoid skipping spring 
395      * daylight savings time day */
396     for (probe = start;; probe += (23 * 3600)) {
397         tsp = localtime(&probe);        /* find out what UTC time "probe" is */
398
399         tdate.year = tsp->tm_year;
400         tdate.month = tsp->tm_mon + 1;
401         tdate.day = tsp->tm_mday;
402         tdate.mask =
403             KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR
404             | KTIMEDATE_MIN | KTIMEDATE_SEC;
405         tdate.hour = aktime->hour;      /* edit in our changes */
406         tdate.min = aktime->min;
407         tdate.sec = aktime->sec;
408         time_next = ktime_InterpretDate(&tdate);        /* Convert back to UTC time */
409         if (time_next < start)
410             continue;           /* "probe" time is already past */
411         if ((tmask & KTIME_DAY) == 0)   /* don't care about day, we're done */
412             break;
413         tsp = localtime(&time_next);
414         if (tsp->tm_wday == aktime->day)
415             break;              /* day matches, we're done */
416     }
417     return time_next;
418 }
419
420
421 /* compare date in both formats, and return as in strcmp */
422 #ifdef undef
423 static int
424 KTimeCmp(register struct ktime *aktime, register struct tm *atm)
425 {
426     register afs_int32 tmask;
427
428     /* don't compare day of the week, since we can't tell the
429      * order in a cyclical set.  Caller must check for equality, if
430      * she cares */
431     tmask = aktime->mask;
432     if (tmask & KTIME_HOUR) {
433         if (aktime->hour > atm->tm_hour)
434             return 1;
435         if (aktime->hour < atm->tm_hour)
436             return -1;
437     }
438     if (tmask & KTIME_MIN) {
439         if (aktime->min > atm->tm_min)
440             return 1;
441         if (aktime->min < atm->tm_min)
442             return -1;
443     }
444     if (tmask & KTIME_SEC) {
445         if (aktime->sec > atm->tm_sec)
446             return 1;
447         if (aktime->sec < atm->tm_sec)
448             return -1;
449     }
450     return 0;
451 }
452 #endif
453
454 /* compare date in both formats, and return as in strcmp */
455 static int
456 KDateCmp(register struct ktime_date *akdate, register struct tm *atm)
457 {
458     if (akdate->year > atm->tm_year)
459         return 1;
460     if (akdate->year < atm->tm_year)
461         return -1;
462     if (akdate->month > atm->tm_mon)
463         return 1;
464     if (akdate->month < atm->tm_mon)
465         return -1;
466     if (akdate->day > atm->tm_mday)
467         return 1;
468     if (akdate->day < atm->tm_mday)
469         return -1;
470     if (akdate->mask & KTIMEDATE_HOUR) {
471         if (akdate->hour > atm->tm_hour)
472             return 1;
473         if (akdate->hour < atm->tm_hour)
474             return -1;
475     }
476     if (akdate->mask & KTIMEDATE_MIN) {
477         if (akdate->min > atm->tm_min)
478             return 1;
479         if (akdate->min < atm->tm_min)
480             return -1;
481     }
482     if (akdate->mask & KTIMEDATE_SEC) {
483         if (akdate->sec > atm->tm_sec)
484             return 1;
485         if (akdate->sec < atm->tm_sec)
486             return -1;
487     }
488     return 0;
489 }
490
491 /* ktime_ParseDate
492  *      parse date string into ktime_date structure
493  * entry:
494  *      adate - string to be parsed
495  *      akdate - ptr to ktime_date for result
496  * exit:
497  *      0 - akdate contains converted date
498  *      -1 - parsing failure
499  */
500
501 static afs_int32
502 ktime_ParseDate(char *adate, struct ktime_date *akdate)
503 {
504     int code;
505     afs_int32 month, day, year, hour, min, sec;
506     char never[7];
507     char c;
508
509     lcstring(never, adate, sizeof(never));
510     if (strcmp(never, "never") == 0)
511         akdate->mask = KTIMEDATE_NEVER;
512     else if (strcmp(never, "now") == 0)
513         akdate->mask = KTIMEDATE_NOW;
514     else
515         akdate->mask = 0;
516     if (akdate->mask)
517         return 0;
518
519
520     code =
521         sscanf(adate, "%d / %d / %d %d : %d : %d%1s", &month, &day, &year,
522                &hour, &min, &sec, &c);
523     if (code != 6) {
524         sec = 0;
525         code =
526             sscanf(adate, "%d / %d / %d %d : %d%1s", &month, &day, &year,
527                    &hour, &min, &c);
528         if (code != 5) {
529             hour = min = 0;
530             code = sscanf(adate, "%d / %d / %d%1s", &month, &day, &year, &c);
531             if (code != 3) {
532                 return -1;
533             }
534         }
535     }
536
537     if ((year < 0) || (month < 1) || (month > 12) || (day < 1) || (day > 31) || /* more or less */
538         (hour < 0) || (hour > 23) || (min < 0) || (min > 59) || (sec < 0)
539         || (sec > 59))
540         return -2;
541
542     if (year < 69)
543         year += 100;            /* make 1/1/1 => Jan 1, 2001 */
544     else if (year >= 1900)
545         year -= 1900;           /* allow 1/1/2001 to work */
546     else if (year > 99)
547         return -2;              /* only allow 2 or 4 digit years */
548
549     akdate->mask =
550         KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR |
551         KTIMEDATE_MIN | KTIMEDATE_SEC;
552
553     akdate->year = year;
554     akdate->month = month;
555     akdate->day = day;
556     akdate->hour = hour;
557     akdate->min = min;
558     akdate->sec = sec;
559
560     /* done successfully */
561     return 0;
562 }
563
564 /* ktime_DateToInt32
565  *      Converts a ktime date string into an afs_int32
566  * entry:
567  *      adate - ktime date string
568  *      aint32 - ptr to afs_int32
569  * exit:
570  *      0 - aint32 contains converted date.
571  */
572
573 afs_int32
574 ktime_DateToInt32(char *adate, afs_int32 * aint32)
575 {
576     struct ktime_date tdate;
577     register afs_int32 code;
578
579     /* parse the date into a ktime_date structure */
580     code = ktime_ParseDate(adate, &tdate);
581     if (code)
582         return code;            /* failed to parse */
583
584     code = ktime_InterpretDate(&tdate); /* interpret as seconds since 1970 */
585     *aint32 = code;             /* return it */
586     return 0;                   /* and declare no errors */
587 }
588
589 /* get useful error message to print about date input format */
590 char *
591 ktime_GetDateUsage(void)
592 {
593     return "date format is 'mm/dd/yy [hh:mm]', using a 24 hour clock";
594 }
595
596
597 /* ktime_InterpretDate
598  *      Converts ktime_date to an afs_int32
599  * entry:
600  *      akdate - date to be converted/interpreted
601  * exit:
602  *      returns KTIMEDATE_NEVERDATE - if never flag was set, or
603  *      date converted to afs_int32.
604  */
605
606 afs_int32
607 ktime_InterpretDate(struct ktime_date * akdate)
608 {
609     register afs_uint32 tresult;
610     register afs_uint32 tbit;
611     time_t temp;
612     register struct tm *tsp;
613
614     if (akdate->mask & KTIMEDATE_NOW)
615         return time(0);
616     if (akdate->mask & KTIMEDATE_NEVER)
617         return KTIMEDATE_NEVERDATE;
618
619     tbit = 1 << 30;             /* start off at max signed result */
620     tresult = 0;                /* result to return */
621     while (tbit > 0) {
622         temp = tresult + tbit;  /* see if adding this bit keeps us < akdate */
623         tsp = localtime(&temp);
624         tsp->tm_mon++;
625 #ifdef notdef
626         if (tsp->tm_mon == 0) {
627             tsp->tm_mon = 12;
628             tsp->tm_year--;
629         }
630 #endif
631         if (KDateCmp(akdate, tsp) >= 0) {
632             /* if temp still represents earlier than date than we're searching
633              * for, add in bit as increment, otherwise use old value and look
634              * for smaller increment */
635             tresult = temp;
636         }
637         tbit = tbit >> 1;       /* try next bit */
638     }
639
640     return tresult;
641 }