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