2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
16 #include <sys/types.h>
35 /* some date parsing routines */
42 static char *day[] = {
52 /* free token list returned by parseLine */
55 LocalFreeTokens(alist)
56 register struct token *alist;
58 register struct token *nlist;
59 for (; alist; alist = nlist) {
71 if (x == 0 || x == ' ' || x == '\t' || x == '\n')
78 LocalParseLine(char *aline, struct token **alist)
81 register char *tptr = NULL;
83 struct token *first, *last;
84 register struct token *ttok;
87 inToken = 0; /* not copying token chars at start */
92 if (tc == 0 || space(tc)) { /* terminating null gets us in here, too */
94 inToken = 0; /* end of this token */
96 ttok = (struct token *)malloc(sizeof(struct token));
98 ttok->key = (char *)malloc(strlen(tbuffer) + 1);
99 strcpy(ttok->key, tbuffer);
109 /* an alpha character */
114 if (tptr - tbuffer >= sizeof(tbuffer))
115 return -1; /* token too long */
119 /* last token flushed 'cause space(0) --> true */
128 /* keyword database for periodic date parsing */
129 static struct ptemp {
141 "sunday", 0x10000,}, {
142 "monday", 0x10001,}, {
143 "tuesday", 0x10002,}, {
144 "wednesday", 0x10003,}, {
145 "thursday", 0x10004,}, {
147 "friday", 0x10005,}, {
148 "saturday", 0x10006,}, {
158 * atime - time in seconds (Unix std)
160 * return value - ptr to time in text form. Ptr is to a static string.
164 ktime_DateOf(afs_int32 atime)
166 static char tbuffer[30];
172 tbuffer[24] = 0; /* get rid of new line */
174 strcpy(tbuffer, "BAD TIME");
179 * parse 12:33:12 or 12:33 or 12 into ktime structure
181 * astr - ptr to string to be parsed
182 * ak - ptr to struct for return value.
184 * 0 - ak holds parsed time.
185 * -1 - error in format
189 ParseTime(register struct ktime *ak, register char *astr)
196 field = 0; /* 0=hour, 1=min, 2=sec */
199 ak->mask |= (KTIME_HOUR | KTIME_MIN | KTIME_SEC);
202 if (tc == 0 || tc == ':') {
214 } else if (!isdigit(tc))
215 return -1; /* syntax error */
222 if (ak->hour >= 24 || ak->min >= 60 || ak->sec >= 60)
228 ktime_Str2int32(register char *astr)
232 memset(&tk, 0, sizeof(tk));
233 if (ParseTime(&tk, astr))
234 return (-1); /* syntax error */
236 return ((tk.hour * 60 + tk.min) * 60 + tk.sec);
239 /* ktime_ParsePeriodic
240 * Parses periodic date of form
241 * now | never | at [time spec] | every [time spec]
242 * where [time spec] is a ktime string.
244 * adate - string to be parsed
245 * ak - ptr to structure for returned ktime
247 * 0 - parsed ktime in ak
248 * -1 - specification error
251 /* -1 means error, 0 means now, otherwise returns time of next event */
253 ktime_ParsePeriodic(char *adate, register struct ktime *ak)
256 register afs_int32 code;
259 memset(ak, 0, sizeof(*ak));
260 code = LocalParseLine(adate, &tt);
263 for (; tt; tt = tt->next) {
264 /* look at each token */
265 if (strcmp(tt->key, "now") == 0) {
266 ak->mask |= KTIME_NOW;
269 if (strcmp(tt->key, "never") == 0) {
270 ak->mask |= KTIME_NEVER;
273 if (strcmp(tt->key, "at") == 0)
275 if (strcmp(tt->key, "every") == 0)
277 if (isdigit(tt->key[0])) {
279 code = ParseTime(ak, tt->key);
284 /* otherwise use keyword table */
285 for (tp = ptkeys;; tp++) {
286 if (tp->key == NULL) {
289 if (strcmp(tp->key, tt->key) == 0)
292 /* now look at tp->value to see what we've got */
293 if ((tp->value >> 16) == 1) {
295 ak->mask |= KTIME_DAY;
296 ak->day = tp->value & 0xff;
298 if ((tp->value >> 16) == 2) {
300 if ((tp->value & 0xff) == 1) {
302 if (!(ak->mask & KTIME_HOUR))
307 else if (ak->hour != 12)
310 /* am is almost a noop, except that we map 12:01 am to 0:01 */
321 /* ktime_DisplayString
322 * Display ktime structure as English that could go into the ktime parser
324 * aparm - ktime to be converted to string
325 * astring - ptr to string, for the result
327 * 0 - astring contains ktime string.
330 ktime_DisplayString(struct ktime *aparm, register char *astring)
334 if (aparm->mask & KTIME_NEVER) {
335 strcpy(astring, "never");
337 } else if (aparm->mask & KTIME_NOW) {
338 strcpy(astring, "now");
341 strcpy(astring, "at");
342 if (aparm->mask & KTIME_DAY) {
343 strcat(astring, " ");
344 strcat(astring, day[aparm->day]);
346 if (aparm->mask & KTIME_HOUR) {
347 if (aparm->hour > 12)
348 sprintf(tempString, " %d", aparm->hour - 12);
349 else if (aparm->hour == 0)
350 strcpy(tempString, " 12");
352 sprintf(tempString, " %d", aparm->hour);
353 strcat(astring, tempString);
355 if (aparm->mask & KTIME_MIN) {
356 sprintf(tempString, ":%02d", aparm->min);
357 strcat(astring, tempString);
359 if ((aparm->mask & KTIME_SEC) && aparm->sec != 0) {
360 sprintf(tempString, ":%02d", aparm->sec);
361 strcat(astring, tempString);
363 if (aparm->mask & KTIME_HOUR) {
364 if (aparm->hour >= 12)
365 strcat(astring, " pm");
367 strcat(astring, " am");
373 /* get next time that matches ktime structure after time afrom */
375 ktime_next(struct ktime * aktime, afs_int32 afrom)
377 /* try by guessing from now */
379 time_t start; /* time to start looking */
380 time_t probe; /* a placeholder to use for advancing day to day */
381 time_t time_next; /* actual UTC time of probe, with time of day set */
383 struct ktime_date tdate;
385 start = afrom + time(0); /* time to start search */
386 tmask = aktime->mask;
388 /* handle some special cases */
389 if (tmask & KTIME_NEVER)
391 if (tmask & KTIME_NOW)
394 /* Use probe to fill in members of *tsp. Add 23 hours each iteration until
395 * time_next is correct. Only add 23 hrs to avoid skipping spring
396 * daylight savings time day */
397 for (probe = start;; probe += (23 * 3600)) {
398 tsp = localtime(&probe); /* find out what UTC time "probe" is */
400 tdate.year = tsp->tm_year;
401 tdate.month = tsp->tm_mon + 1;
402 tdate.day = tsp->tm_mday;
404 KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR
405 | KTIMEDATE_MIN | KTIMEDATE_SEC;
406 tdate.hour = aktime->hour; /* edit in our changes */
407 tdate.min = aktime->min;
408 tdate.sec = aktime->sec;
409 time_next = ktime_InterpretDate(&tdate); /* Convert back to UTC time */
410 if (time_next < start)
411 continue; /* "probe" time is already past */
412 if ((tmask & KTIME_DAY) == 0) /* don't care about day, we're done */
414 tsp = localtime(&time_next);
415 if (tsp->tm_wday == aktime->day)
416 break; /* day matches, we're done */
422 /* compare date in both formats, and return as in strcmp */
425 KTimeCmp(register struct ktime *aktime, register struct tm *atm)
427 register afs_int32 tmask;
429 /* don't compare day of the week, since we can't tell the
430 * order in a cyclical set. Caller must check for equality, if
432 tmask = aktime->mask;
433 if (tmask & KTIME_HOUR) {
434 if (aktime->hour > atm->tm_hour)
436 if (aktime->hour < atm->tm_hour)
439 if (tmask & KTIME_MIN) {
440 if (aktime->min > atm->tm_min)
442 if (aktime->min < atm->tm_min)
445 if (tmask & KTIME_SEC) {
446 if (aktime->sec > atm->tm_sec)
448 if (aktime->sec < atm->tm_sec)
455 /* compare date in both formats, and return as in strcmp */
457 KDateCmp(register struct ktime_date *akdate, register struct tm *atm)
459 if (akdate->year > atm->tm_year)
461 if (akdate->year < atm->tm_year)
463 if (akdate->month > atm->tm_mon)
465 if (akdate->month < atm->tm_mon)
467 if (akdate->day > atm->tm_mday)
469 if (akdate->day < atm->tm_mday)
471 if (akdate->mask & KTIMEDATE_HOUR) {
472 if (akdate->hour > atm->tm_hour)
474 if (akdate->hour < atm->tm_hour)
477 if (akdate->mask & KTIMEDATE_MIN) {
478 if (akdate->min > atm->tm_min)
480 if (akdate->min < atm->tm_min)
483 if (akdate->mask & KTIMEDATE_SEC) {
484 if (akdate->sec > atm->tm_sec)
486 if (akdate->sec < atm->tm_sec)
493 * parse date string into ktime_date structure
495 * adate - string to be parsed
496 * akdate - ptr to ktime_date for result
498 * 0 - akdate contains converted date
499 * -1 - parsing failure
503 ktime_ParseDate(char *adate, struct ktime_date *akdate)
506 afs_int32 month, day2, year, hour, min, sec;
510 lcstring(never, adate, sizeof(never));
511 if (strcmp(never, "never") == 0)
512 akdate->mask = KTIMEDATE_NEVER;
513 else if (strcmp(never, "now") == 0)
514 akdate->mask = KTIMEDATE_NOW;
520 /* Old ambiguous mm/dd/yy hh:mm:ss format */
523 sscanf(adate, "%d / %d / %d %d : %d : %d%1s", &month, &day2, &year,
524 &hour, &min, &sec, &c[0]);
528 sscanf(adate, "%d / %d / %d %d : %d%1s", &month, &day2, &year,
533 sscanf(adate, "%d / %d / %d%1s", &month, &day2, &year, &c[0]);
540 /* New ISO 8601 (subset) format */
543 hour = min = sec = 0;
545 sscanf(adate, "%d-%d-%d %d:%d:%d%1s", &year, &month, &day2,
546 &hour, &min, &sec, c);
547 if (code != 3 && code != 5 && code != 6)
554 if ((year < 0) || (month < 1) || (month > 12) || (day2 < 1) || (day2 > 31) || /* more or less */
555 (hour < 0) || (hour > 23) || (min < 0) || (min > 59) || (sec < 0)
560 year += 100; /* make 1/1/1 => Jan 1, 2001 */
561 else if (year >= 1900)
562 year -= 1900; /* allow 1/1/2001 to work */
564 return -2; /* only allow 2 or 4 digit years */
567 KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY | KTIMEDATE_HOUR |
568 KTIMEDATE_MIN | KTIMEDATE_SEC;
571 akdate->month = month;
577 /* done successfully */
582 * Converts a ktime date string into an afs_int32
584 * adate - ktime date string
585 * aint32 - ptr to afs_int32
587 * 0 - aint32 contains converted date.
591 ktime_DateToInt32(char *adate, afs_int32 * aint32)
593 struct ktime_date tdate;
598 if (sscanf(adate, "%lu%1s", &l, c) == 1 && l > 200000000)
601 /* parse the date into a ktime_date structure */
602 code = ktime_ParseDate(adate, &tdate);
604 return code; /* failed to parse */
605 *aint32 = ktime_InterpretDate(&tdate); /* interpret as seconds since 1970 */
611 /* get useful error message to print about date input format */
613 ktime_GetDateUsage(void)
615 return "date format is '(yyyy-mm-dd | mm/dd/yy) [hh:mm]', using a 24 hour clock";
619 /* ktime_InterpretDate
620 * Converts ktime_date to an afs_int32
622 * akdate - date to be converted/interpreted
624 * returns KTIMEDATE_NEVERDATE - if never flag was set, or
625 * date converted to afs_int32.
629 ktime_InterpretDate(struct ktime_date * akdate)
631 register afs_uint32 tresult;
632 register afs_uint32 tbit;
634 register struct tm *tsp;
636 if (akdate->mask & KTIMEDATE_NOW)
638 if (akdate->mask & KTIMEDATE_NEVER)
639 return KTIMEDATE_NEVERDATE;
641 tbit = 1 << 30; /* start off at max signed result */
642 tresult = 0; /* result to return */
644 temp = tresult + tbit; /* see if adding this bit keeps us < akdate */
645 tsp = localtime(&temp);
648 if (tsp->tm_mon == 0) {
653 if (KDateCmp(akdate, tsp) >= 0) {
654 /* if temp still represents earlier than date than we're searching
655 * for, add in bit as increment, otherwise use old value and look
656 * for smaller increment */
659 tbit = tbit >> 1; /* try next bit */