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