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 <afs/param.h>
11 #include <afsconfig.h>
15 #include <sys/types.h>
22 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV)
31 /* some forward reference dcls */
32 static afs_int32 ktime_ParseDate();
35 /* some date parsing routines */
42 static char *day[] = {
52 /* free token list returned by parseLine */
53 static LocalFreeTokens(alist)
54 register struct token *alist; {
55 register struct token *nlist;
56 for(; alist; alist = nlist) {
66 if (x == 0 || x == ' ' || x == '\t' || x== '\n') return 1;
70 static LocalParseLine(aline, alist)
72 struct token **alist; {
76 struct token *first, *last;
77 register struct token *ttok;
80 inToken = 0; /* not copying token chars at start */
81 first = (struct token *) 0;
82 last = (struct token *) 0;
85 if (tc == 0 || space(tc)) { /* terminating null gets us in here, too */
87 inToken = 0; /* end of this token */
89 ttok = (struct token *) malloc(sizeof(struct token));
90 ttok->next = (struct token *) 0;
91 ttok->key = (char *) malloc(strlen(tbuffer)+1);
92 strcpy(ttok->key, tbuffer);
98 if (!first) first = ttok;
102 /* an alpha character */
107 if (tptr - tbuffer >= sizeof(tbuffer)) return -1; /* token too long */
111 /* last token flushed 'cause space(0) --> true */
112 if (last) last->next = (struct token *) 0;
119 /* keyword database for periodic date parsing */
120 static struct ptemp {
134 "wednesday", 0x10003,
148 * atime - time in seconds (Unix std)
150 * return value - ptr to time in text form. Ptr is to a static string.
153 char *ktime_DateOf(atime)
155 static char tbuffer[30];
157 tp=ctime((time_t *)&atime);
160 tbuffer[24] = 0; /* get rid of new line */
163 strcpy(tbuffer, "BAD TIME");
167 afs_int32 ktime_Str2int32(astr)
172 bzero(&tk, sizeof(tk));
173 if ( ParseTime(&tk, astr) )
174 return (-1); /* syntax error */
176 return ((tk.hour*60 + tk.min)*60 + tk.sec);
180 * parse 12:33:12 or 12:33 or 12 into ktime structure
182 * astr - ptr to string to be parsed
183 * ak - ptr to struct for return value.
185 * 0 - ak holds parsed time.
186 * -1 - error in format
189 static ParseTime(ak, astr)
191 register struct ktime *ak; {
197 field = 0; /* 0=hour, 1=min, 2=sec */
200 ak->mask |= (KTIME_HOUR | KTIME_MIN | KTIME_SEC);
203 if (tc == 0 || tc == ':') {
215 else if (!isdigit(tc)) return -1; /* syntax error */
222 if (ak->hour >= 24 || ak->min >= 60 || ak->sec >= 60) return -1;
226 /* ktime_ParsePeriodic
227 * Parses periodic date of form
228 * now | never | at [time spec] | every [time spec]
229 * where [time spec] is a ktime string.
231 * adate - string to be parsed
232 * ak - ptr to structure for returned ktime
234 * 0 - parsed ktime in ak
235 * -1 - specification error
238 /* -1 means error, 0 means now, otherwise returns time of next event */
239 int ktime_ParsePeriodic(adate, ak)
240 register struct ktime *ak;
243 register afs_int32 code;
246 bzero(ak, sizeof(*ak));
247 code = LocalParseLine(adate, &tt);
249 for(;tt;tt=tt->next) {
250 /* look at each token */
251 if (strcmp(tt->key, "now") == 0) {
252 ak->mask |= KTIME_NOW;
255 if (strcmp(tt->key, "never") == 0) {
256 ak->mask |= KTIME_NEVER;
259 if (strcmp(tt->key, "at") == 0) continue;
260 if (strcmp(tt->key, "every") == 0) continue;
261 if (isdigit(tt->key[0])) {
263 code = ParseTime(ak, tt->key);
267 /* otherwise use keyword table */
268 for(tp = ptkeys;; tp++) {
269 if (tp->key == (char *) 0) {
272 if (strcmp(tp->key, tt->key) == 0) break;
274 /* now look at tp->value to see what we've got */
275 if ((tp->value>>16) == 1) {
277 ak->mask |= KTIME_DAY;
278 ak->day = tp->value & 0xff;
280 if ((tp->value >> 16) == 2) {
282 if ((tp->value & 0xff) == 1) {
284 if (!(ak->mask & KTIME_HOUR)) return -1;
285 if (ak->hour < 12) ak->hour += 12;
287 else if (ak->hour != 12) return -1;
290 /* am is almost a noop, except that we map 12:01 am to 0:01 */
291 if (ak->hour > 12) return -1;
292 if (ak->hour == 12) ak->hour = 0;
299 /* ktime_DisplayString
300 * Display ktime structure as English that could go into the ktime parser
302 * aparm - ktime to be converted to string
303 * astring - ptr to string, for the result
305 * 0 - astring contains ktime string.
307 ktime_DisplayString(aparm, astring)
308 register char *astring;
309 struct ktime *aparm; {
312 if (aparm->mask & KTIME_NEVER) {
313 strcpy(astring, "never");
316 else if (aparm->mask & KTIME_NOW) {
317 strcpy(astring, "now");
321 strcpy(astring, "at");
322 if (aparm->mask & KTIME_DAY) {
323 strcat(astring, " ");
324 strcat(astring, day[aparm->day]);
326 if (aparm->mask & KTIME_HOUR) {
327 if (aparm->hour > 12)
328 sprintf(tempString, " %d", aparm->hour-12);
329 else if (aparm->hour == 0)
330 strcpy(tempString, " 12");
332 sprintf(tempString, " %d", aparm->hour);
333 strcat(astring, tempString);
335 if (aparm->mask & KTIME_MIN) {
336 sprintf(tempString, ":%02d", aparm->min);
337 strcat(astring, tempString);
339 if ((aparm->mask & KTIME_SEC) && aparm->sec != 0) {
340 sprintf(tempString, ":%02d", aparm->sec);
341 strcat(astring, tempString);
343 if (aparm->mask & KTIME_HOUR) {
344 if (aparm->hour >= 12)
345 strcat(astring, " pm");
347 strcat(astring, " am");
353 /* get next time that matches ktime structure after time afrom */
354 afs_int32 ktime_next(aktime, afrom)
356 struct ktime *aktime; {
357 /* try by guessing from now */
359 time_t start; /* time to start looking */
360 time_t probe; /* a placeholder to use for advancing day to day */
361 time_t time_next; /* actual UTC time of probe, with time of day set */
363 struct ktime_date tdate;
365 start = afrom + time(0); /* time to start search */
366 tmask = aktime->mask;
368 /* handle some special cases */
369 if (tmask & KTIME_NEVER) return 0x7fffffff;
370 if (tmask & KTIME_NOW) return 0;
372 /* Use probe to fill in members of *tsp. Add 23 hours each iteration until
373 time_next is correct. Only add 23 hrs to avoid skipping spring
374 daylight savings time day */
375 for (probe=start;;probe += (23*3600)) {
376 tsp = localtime(&probe); /* find out what UTC time "probe" is */
378 tdate.year = tsp->tm_year;
379 tdate.month = tsp->tm_mon+1;
380 tdate.day = tsp->tm_mday;
381 tdate.mask = KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY |
382 KTIMEDATE_HOUR | KTIMEDATE_MIN | KTIMEDATE_SEC;
383 tdate.hour = aktime->hour; /* edit in our changes */
384 tdate.min = aktime->min;
385 tdate.sec = aktime->sec;
386 time_next = ktime_InterpretDate(&tdate); /* Convert back to UTC time */
387 if (time_next < start) continue; /* "probe" time is already past */
388 if ((tmask & KTIME_DAY) == 0) /* don't care about day, we're done */
390 tsp = localtime(&time_next);
391 if (tsp->tm_wday == aktime->day) break; /* day matches, we're done */
397 /* compare date in both formats, and return as in strcmp */
398 static KTimeCmp(aktime, atm)
399 register struct ktime *aktime;
400 register struct tm *atm; {
401 register afs_int32 tmask;
403 /* don't compare day of the week, since we can't tell the
404 order in a cyclical set. Caller must check for equality, if
406 tmask = aktime->mask;
407 if (tmask & KTIME_HOUR) {
408 if (aktime->hour > atm->tm_hour) return 1;
409 if (aktime->hour < atm->tm_hour) return -1;
411 if (tmask & KTIME_MIN) {
412 if (aktime->min > atm->tm_min) return 1;
413 if (aktime->min < atm->tm_min) return -1;
415 if (tmask & KTIME_SEC) {
416 if (aktime->sec > atm->tm_sec) return 1;
417 if (aktime->sec < atm->tm_sec) return -1;
422 /* compare date in both formats, and return as in strcmp */
423 static KDateCmp(akdate, atm)
424 register struct ktime_date *akdate;
425 register struct tm *atm; {
426 if (akdate->year > atm->tm_year) return 1;
427 if (akdate->year < atm->tm_year) return -1;
428 if (akdate->month > atm->tm_mon) return 1;
429 if (akdate->month < atm->tm_mon) return -1;
430 if (akdate->day > atm->tm_mday) return 1;
431 if (akdate->day < atm->tm_mday) return -1;
432 if (akdate->mask & KTIMEDATE_HOUR) {
433 if (akdate->hour > atm->tm_hour) return 1;
434 if (akdate->hour < atm->tm_hour) return -1;
436 if (akdate->mask & KTIMEDATE_MIN) {
437 if (akdate->min > atm->tm_min) return 1;
438 if (akdate->min < atm->tm_min) return -1;
440 if (akdate->mask & KTIMEDATE_SEC) {
441 if (akdate->sec > atm->tm_sec) return 1;
442 if (akdate->sec < atm->tm_sec) return -1;
448 * Converts a ktime date string into an afs_int32
450 * adate - ktime date string
451 * aint32 - ptr to afs_int32
453 * 0 - aint32 contains converted date.
456 afs_int32 ktime_DateToInt32(adate, aint32)
459 struct ktime_date tdate;
460 register afs_int32 code;
462 /* parse the date into a ktime_date structure */
463 code = ktime_ParseDate(adate, &tdate);
464 if (code) return code; /* failed to parse */
466 code = ktime_InterpretDate(&tdate); /* interpret date as seconds since 1970 */
467 *aint32 = code; /* return it */
468 return 0; /* and declare no errors */
472 * parse date string into ktime_date structure
474 * adate - string to be parsed
475 * akdate - ptr to ktime_date for result
477 * 0 - akdate contains converted date
478 * -1 - parsing failure
481 static afs_int32 ktime_ParseDate(adate, akdate)
483 struct ktime_date *akdate; {
485 afs_int32 month, day, year, hour, min, sec;
489 lcstring (never, adate, sizeof(never));
490 if (strcmp (never, "never") == 0) akdate->mask = KTIMEDATE_NEVER;
491 else if (strcmp (never, "now") == 0) akdate->mask = KTIMEDATE_NOW;
492 else akdate->mask = 0;
493 if (akdate->mask) return 0;
496 code = sscanf(adate, "%d / %d / %d %d : %d : %d%1s",
497 &month, &day, &year, &hour, &min, &sec, &c);
500 code = sscanf(adate, "%d / %d / %d %d : %d%1s",
501 &month, &day, &year, &hour, &min, &c);
504 code = sscanf(adate, "%d / %d / %d%1s", &month, &day, &year, &c);
512 (month < 1) || (month > 12) ||
513 (day < 1) || (day > 31) || /* more or less */
514 (hour < 0) || (hour > 23) ||
515 (min < 0) || (min > 59) ||
516 (sec < 0) || (sec > 59))
519 if (year < 69) year += 100; /* make 1/1/1 => Jan 1, 2001 */
520 else if (year >= 1900) year -= 1900; /* allow 1/1/2001 to work */
521 else if (year > 99) return -2; /* only allow 2 or 4 digit years */
523 akdate->mask = KTIMEDATE_YEAR | KTIMEDATE_MONTH | KTIMEDATE_DAY |
524 KTIMEDATE_HOUR | KTIMEDATE_MIN | KTIMEDATE_SEC;
527 akdate->month = month;
533 /* done successfully */
537 /* get useful error message to print about date input format */
538 char *ktime_GetDateUsage() {
539 return "date format is 'mm/dd/yy [hh:mm]', using a 24 hour clock";
543 /* ktime_InterpretDate
544 * Converts ktime_date to an afs_int32
546 * akdate - date to be converted/interpreted
548 * returns KTIMEDATE_NEVERDATE - if never flag was set, or
549 * date converted to afs_int32.
552 afs_int32 ktime_InterpretDate(struct ktime_date *akdate)
554 register afs_uint32 tresult;
555 register afs_uint32 tbit;
557 register struct tm *tsp;
559 if (akdate->mask & KTIMEDATE_NOW) return time(0);
560 if (akdate->mask & KTIMEDATE_NEVER) return KTIMEDATE_NEVERDATE;
562 tbit = 1<<30; /* start off at max signed result */
563 tresult = 0; /* result to return */
565 temp = tresult + tbit; /* see if adding this bit keeps us < akdate */
566 tsp = localtime(&temp);
569 if (tsp->tm_mon == 0) {
574 if (KDateCmp(akdate, tsp) >= 0) {
575 /* if temp still represents earlier than date than we're searching
576 * for, add in bit as increment, otherwise use old value and look
577 * for smaller increment */
580 tbit = tbit >> 1; /* try next bit */