1 /* time.c
2 Routines to deal with UUCP time strings.
3
4 Copyright (C) 1991, 1992 Ian Lance Taylor
5
6 This file is part of the Taylor UUCP package.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 The author of the program may be contacted at ian@airs.com or
23 c/o AIRS, P.O. Box 520, Waltham, MA 02254.
24
25 $Log: time.c,v $
26 Revision 1.12 1992/03/17 00:32:40 ian
27 Cast argument to qttime_parse
28
29 Revision 1.11 1992/03/09 05:08:16 ian
30 Added status for wrong time to call, not used if system can't be called
31
32 Revision 1.10 1992/03/08 01:52:34 ian
33 Removed extraneous semicolons
34
35 Revision 1.9 1992/03/07 02:56:30 ian
36 Rewrote time routines
37
38 Revision 1.8 1992/02/08 03:54:18 ian
39 Include <string.h> only in <uucp.h>, added 1992 copyright
40
41 Revision 1.7 1992/01/11 17:30:10 ian
42 John Antypas: use memcpy instead of relying on structure assignment
43
44 Revision 1.6 1991/12/29 04:04:18 ian
45 Added a bunch of extern definitions
46
47 Revision 1.5 1991/12/22 20:57:57 ian
48 Added externs for strcasecmp or strncasecmp
49
50 Revision 1.4 1991/09/19 02:22:44 ian
51 Chip Salzenberg's patch to allow ";retrytime" at the end of a time string
52
53 Revision 1.3 1991/09/12 05:04:44 ian
54 Wrong sense of comparison in btime_low_grade
55
56 Revision 1.2 1991/09/11 16:59:00 ian
57 fcheck_time and btime_low_grade looped endlessly on unusual grades
58
59 Revision 1.1 1991/09/10 19:40:31 ian
60 Initial revision
61
62 */
63
64 #include "uucp.h"
65
66 #if USE_RCS_ID
67 char time_rcsid[] = "$Id: time.c,v 1.12 1992/03/17 00:32:40 ian Rel $";
68 #endif
69
70 #include <ctype.h>
71
72 #if HAVE_TIME_H
73 #include <time.h>
74 #endif
75
76 #if ! HAVE_TIME_T
77 #if HAVE_SYS_TIME_T
78 #include <sys/types.h>
79 #endif /* HAVE_SYS_TIME_T */
80 #endif /* ! HAVE_TIME_T */
81
82 #include "uutime.h"
83
84 /* External functions. */
85 extern int strncasecmp ();
86 extern time_t time ();
87 extern struct tm *localtime ();
88
89 /* Local functions. */
90
91 static struct sspan *qtnew P((struct sspan *qnext, long ival,
92 int istart, int iend, int cretry));
93 static struct sspan *qtadd_span P((struct sspan *qlist, long ival,
94 int istart, int iend,
95 boolean (*picmp) P((long, long)),
96 int cretry));
97 static struct sspan *qttime_parse P((const char *ztime,
98 struct sspan *qlist, long ival,
99 boolean (*picmp) P((long, long)),
100 int cretry));
101
102 /* A helper function to create a new time span with the specified
103 arguments. */
104
105 static struct sspan *
qtnew(qnext,ival,istart,iend,cretry)106 qtnew (qnext, ival, istart, iend, cretry)
107 struct sspan *qnext;
108 long ival;
109 int istart;
110 int iend;
111 int cretry;
112 {
113 struct sspan *q;
114
115 q = (struct sspan *) xmalloc (sizeof (struct sspan));
116 q->qnext = qnext;
117 q->ival = ival;
118 q->istart = istart;
119 q->iend = iend;
120 q->cretry = cretry;
121 return q;
122 }
123
124 /* A simple function to free a list of time spans. */
125
126 void
utimespan_free(q)127 utimespan_free (q)
128 struct sspan *q;
129 {
130 while (q != NULL)
131 {
132 struct sspan *qnext;
133
134 qnext = q->qnext;
135 xfree ((pointer) q);
136 q = qnext;
137 }
138 }
139
140 /* Add a time span to an existing list of time spans. We keep the
141 list sorted by time to make this operation easier. This modifies
142 the existing list, and returns the modified version. It takes a
143 comparison function which should return < 0 if the first argument
144 should take precedence over the second argument and == 0 if they
145 are the same (for grades this is igradecmp; for sizes it is minus
146 (the binary operator)). */
147
148 static struct sspan *
qtadd_span(qlist,ival,istart,iend,picmp,cretry)149 qtadd_span (qlist, ival, istart, iend, picmp, cretry)
150 struct sspan *qlist;
151 long ival;
152 int istart;
153 int iend;
154 boolean (*picmp) P((long, long));
155 int cretry;
156 {
157 struct sspan **pq;
158
159 /* istart < iend */
160 for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext)
161 {
162 int icmp;
163
164 /* Invariant: PREV (*pq) == NULL || PREV (*pq)->iend <= istart */
165 /* istart < iend && (*pq)->istart < (*pq)->iend */
166
167 if (iend <= (*pq)->istart)
168 {
169 /* istart < iend <= (*pq)->istart < (*pq)->iend */
170 /* No overlap, and we're at the right spot. See if we can
171 combine these spans. */
172 if (iend == (*pq)->istart
173 && cretry == (*pq)->cretry
174 && (*picmp) (ival, (*pq)->ival) == 0)
175 {
176 (*pq)->istart = istart;
177 return qlist;
178 }
179 /* We couldn't combine them. */
180 break;
181 }
182
183 if ((*pq)->iend <= istart)
184 {
185 /* (*pq)->istart < (*pq)->iend <= istart < iend */
186 /* No overlap. Try attaching this span. */
187 if ((*pq)->iend == istart
188 && (*pq)->cretry == cretry
189 && ((*pq)->qnext == NULL
190 || iend <= (*pq)->qnext->istart)
191 && (*picmp) (ival, (*pq)->ival) == 0)
192 {
193 (*pq)->iend = iend;
194 return qlist;
195 }
196 /* Couldn't attach; keep looking for the right spot. We
197 might be able to combine part of the new span onto an
198 existing span, but it's probably not worth it. */
199 continue;
200 }
201
202 /* istart < iend
203 && (*pq)->istart < (*pq)->iend
204 && istart < (*pq)->iend
205 && (*pq)->istart < iend */
206 /* Overlap. */
207
208 icmp = (*picmp) (ival, (*pq)->ival);
209
210 if (icmp == 0)
211 {
212 /* Just expand the old span to include the new span. */
213 if (istart < (*pq)->istart)
214 (*pq)->istart = istart;
215 if ((*pq)->iend < iend)
216 (*pq)->iend = iend;
217 }
218 else if (icmp < 0)
219 {
220 /* Replace the old span with the new span. */
221 if ((*pq)->istart < istart)
222 {
223 /* Save the initial portion of the old span. */
224 *pq = qtnew (*pq, (*pq)->ival, (*pq)->istart, istart,
225 (*pq)->cretry);
226 pq = &(*pq)->qnext;
227 }
228 if (iend < (*pq)->iend)
229 {
230 /* Save the final portion of the old span. */
231 (*pq)->qnext = qtnew ((*pq)->qnext, (*pq)->ival, iend,
232 (*pq)->iend, (*pq)->cretry);
233 }
234 (*pq)->ival = ival;
235 (*pq)->istart = istart;
236 (*pq)->iend = iend;
237 (*pq)->cretry = cretry;
238 }
239 else
240 {
241 /* Leave the old span untouched. */
242 if (istart < (*pq)->istart)
243 {
244 /* Put in the initial portion of the new span. */
245 *pq = qtnew (*pq, ival, istart, (*pq)->istart, cretry);
246 pq = &(*pq)->qnext;
247 }
248 if ((*pq)->iend < iend)
249 {
250 /* Put in the final portion of the new span. */
251 (*pq)->qnext = qtnew ((*pq)->qnext, ival, (*pq)->iend,
252 iend, cretry);
253 }
254 }
255
256 return qlist;
257 }
258
259 /* This is the spot for the new span, and there's no overlap. */
260
261 *pq = qtnew (*pq, ival, istart, iend, cretry);
262
263 return qlist;
264 }
265
266 /* An array of weekday abbreviations. The code below assumes that
267 each one starts with a lower case letter. */
268
269 static const struct
270 {
271 const char *zname;
272 int imin;
273 int imax;
274 } asTdays[] =
275 {
276 { "any", 0, 6 },
277 { "wk", 1, 5 },
278 { "su", 0, 0 },
279 { "mo", 1, 1 },
280 { "tu", 2, 2 },
281 { "we", 3, 3 },
282 { "th", 4, 4 },
283 { "fr", 5, 5 },
284 { "sa", 6, 6 },
285 { "never", -1, -2 },
286 { NULL, 0, 0 }
287 };
288
289 /* Parse a time string and add it to a span list. This function is
290 given the value and comparison function to use. The time string
291 continues to a null byte, a space or a semicolon. This returns the
292 new span list, or NULL on error. If no time matches, it will wind
293 up returning qlist, which may itself be NULL. */
294
295 static struct sspan *
qttime_parse(ztime,qlist,ival,picmp,cretry)296 qttime_parse (ztime, qlist, ival, picmp, cretry)
297 const char *ztime;
298 struct sspan *qlist;
299 long ival;
300 int (*picmp) P((long, long));
301 int cretry;
302 {
303 const char *zend;
304 char bfirst;
305 int i;
306
307 zend = ztime + strcspn (ztime, "; ");
308
309 if (pasTtable == NULL)
310 uinittimetables ();
311
312 /* Expand the string using a timetable. */
313 bfirst = tolower (BUCHAR (*ztime));
314 for (i = 0; i < cTtable; i++)
315 {
316 if (bfirst == tolower (BUCHAR (pasTtable[i].zname[0]))
317 && strncasecmp (ztime, pasTtable[i].zname, zend - ztime) == 0)
318 {
319 ztime = pasTtable[i].ztime;
320 zend = ztime + strlen (ztime);
321 /* Now search the table for this string. */
322 i = -1;
323 }
324 }
325
326 /* Look through the portions of the time string separated by a
327 comma or a vertical bar. */
328
329 for (; ztime < zend; ztime += strcspn (ztime, ",|"))
330 {
331 int iday;
332 boolean afday[7];
333 const char *z;
334 int istart, iend;
335
336 if (*ztime == ',' || *ztime == '|')
337 ++ztime;
338
339 for (iday = 0; iday < 7; iday++)
340 afday[iday] = FALSE;
341
342 /* Get the days. */
343
344 z = ztime;
345 do
346 {
347 bfirst = tolower (BUCHAR (*z));
348 for (iday = 0; asTdays[iday].zname != NULL; iday++)
349 {
350 int clen;
351
352 if (bfirst != asTdays[iday].zname[0])
353 continue;
354
355 clen = strlen (asTdays[iday].zname);
356 if (strncasecmp (z, asTdays[iday].zname, clen) == 0)
357 {
358 int iset;
359
360 for (iset = asTdays[iday].imin;
361 iset <= asTdays[iday].imax;
362 iset++)
363 afday[iset] = TRUE;
364 z += clen;
365 break;
366 }
367 }
368 if (asTdays[iday].zname == NULL)
369 {
370 ulog (LOG_ERROR, "%s: unparseable time string", ztime);
371 return NULL;
372 }
373 }
374 while (isalpha (BUCHAR (*z)));
375
376 /* Get the hours. */
377
378 if (! isdigit (BUCHAR (*z)))
379 {
380 istart = 0;
381 iend = 24 * 60;
382 }
383 else
384 {
385 char *zendnum;
386
387 istart = (int) strtol (z, &zendnum, 10);
388 if (*zendnum != '-' || ! isdigit (BUCHAR (zendnum[1])))
389 {
390 ulog (LOG_ERROR, "%s: unparseable time string", ztime);
391 return NULL;
392 }
393 z = zendnum + 1;
394 iend = (int) strtol (z, &zendnum, 10);
395 if (*zendnum != '\0'
396 && *zendnum != ' '
397 && *zendnum != ';'
398 && *zendnum != ','
399 && *zendnum != '|')
400 {
401 ulog (LOG_ERROR, "%s: unparseable time string", ztime);
402 return NULL;
403 }
404
405 istart = (istart / 100) * 60 + istart % 100;
406 iend = (iend / 100) * 60 + iend % 100;
407 }
408
409 /* Add the times we've found onto the list. */
410
411 for (iday = 0; iday < 7; iday++)
412 {
413 if (afday[iday])
414 {
415 int iminute;
416
417 iminute = iday * 24 * 60;
418 if (istart < iend)
419 qlist = qtadd_span (qlist, ival, iminute + istart,
420 iminute + iend, picmp, cretry);
421 else
422 {
423 /* Wrap around midnight. */
424 qlist = qtadd_span (qlist, ival, iminute,
425 iminute + iend, picmp, cretry);
426 qlist = qtadd_span (qlist, ival, iminute + istart,
427 iminute + 24 * 60, picmp,
428 cretry);
429 }
430 }
431 }
432 }
433
434 return qlist;
435 }
436
437 /* See if the current time matches a time span. If it does, return
438 TRUE, set *pival to the value for the matching span, and set
439 *pcretry to the retry for the matching span. Otherwise return
440 FALSE. */
441
442 boolean
ftimespan_match(qspan,pival,pcretry)443 ftimespan_match (qspan, pival, pcretry)
444 const struct sspan *qspan;
445 long *pival;
446 int *pcretry;
447 {
448 time_t inow;
449 struct tm *qtm;
450 int itm;
451 const struct sspan *q;
452
453 time (&inow);
454 qtm = localtime (&inow);
455
456 /* Get the number of minutes since Sunday for the time. */
457 itm = qtm->tm_wday * 24 * 60 + qtm->tm_hour * 60 + qtm->tm_min;
458
459 for (q = qspan; q != NULL; q = q->qnext)
460 {
461 if (q->istart <= itm && itm <= q->iend)
462 {
463 if (pival != NULL)
464 *pival = q->ival;
465 if (pcretry != NULL)
466 *pcretry = q->cretry;
467 return TRUE;
468 }
469 }
470
471 return FALSE;
472 }
473
474 /* Compare two work grades. */
475
476 static int itgradecmp P((long, long));
477
478 static int
itgradecmp(i1,i2)479 itgradecmp (i1, i2)
480 long i1;
481 long i2;
482 {
483 return igradecmp ((int) i1, (int) i2);
484 }
485
486 /* Parse a time grade string into a time span. A time grade string is
487 a series of single character work grades followed by time strings.
488 The time string may end with a semicolon and a retry time. Each
489 grade/time/retry tuple is separated by a single space. This
490 function returns a time span, or NULL if no time matches or an
491 error occurs. */
492
493 struct sspan *
qtimegrade_parse(ztimegrade)494 qtimegrade_parse (ztimegrade)
495 const char *ztimegrade;
496 {
497 struct sspan *qret;
498
499 if (ztimegrade == NULL)
500 return NULL;
501
502 qret = NULL;
503
504 while (TRUE)
505 {
506 const char *zretry;
507 int cretry;
508 struct sspan *qnext;
509
510 zretry = ztimegrade + strcspn (ztimegrade, "; ");
511 if (*zretry == ';')
512 cretry = atoi (zretry + 1);
513 else
514 cretry = 0;
515
516 qnext = qttime_parse (ztimegrade + 1, qret, (long) *ztimegrade,
517 itgradecmp, cretry);
518 if (qnext != NULL)
519 qret = qnext;
520
521 ztimegrade += strcspn (ztimegrade, " ");
522
523 if (*ztimegrade == '\0')
524 break;
525
526 ++ztimegrade;
527 }
528
529 return qret;
530 }
531
532 /* Compare sizes when putting them into a timestring. */
533
534 static int itsizecmp P((long, long));
535
536 static int
itsizecmp(i1,i2)537 itsizecmp (i1, i2)
538 long i1;
539 long i2;
540 {
541 /* We can't just return i1 - i2 because that would be a long. */
542 if (i1 < i2)
543 return -1;
544 else if (i1 == i2)
545 return 0;
546 else
547 return 1;
548 }
549
550 /* Parse a time size string into a span. A time size string is a
551 size, a space, a time string, a space, repeated. There is no retry
552 time associated with a time size string. */
553
554 struct sspan *
qtimesize_parse(ztimesize)555 qtimesize_parse (ztimesize)
556 const char *ztimesize;
557 {
558 struct sspan *qret;
559
560 if (ztimesize == NULL)
561 return NULL;
562
563 qret = NULL;
564
565 while (TRUE)
566 {
567 long isize;
568 char *zend;
569 struct sspan *qnext;
570
571 isize = strtol (ztimesize, &zend, 10);
572
573 #if DEBUG > 0
574 if (*zend != ' ')
575 ulog (LOG_FATAL, "qtimesize_parse: Can't happen");
576 #endif
577
578 ++zend;
579
580 qnext = qttime_parse (zend, qret, isize, itsizecmp, 0);
581 if (qnext != NULL)
582 qret = qnext;
583
584 ztimesize = zend + strcspn (zend, " ");
585
586 if (*ztimesize == '\0')
587 break;
588
589 ++ztimesize;
590 }
591
592 return qret;
593 }
594
595 /* Determine the grade of work we are permitted to do at the current
596 time, given a time/grade string. Return a null byte if no grades
597 are legal. */
598
599 char
btimegrade(ztimegrade)600 btimegrade (ztimegrade)
601 const char *ztimegrade;
602 {
603 struct sspan *qspan;
604 boolean fmatch;
605 long ival;
606
607 qspan = qtimegrade_parse (ztimegrade);
608 if (qspan == NULL)
609 return '\0';
610
611 fmatch = ftimespan_match (qspan, &ival, (int *) NULL);
612
613 utimespan_free (qspan);
614
615 if (! fmatch)
616 return '\0';
617
618 return (int) ival;
619 }
620
621 /* Determine the maximum size that may be transferred at the present
622 time, according to a time size string. This returns -1 if there
623 are no restrictions. */
624
625 long
cmax_size_now(ztimesize)626 cmax_size_now (ztimesize)
627 const char *ztimesize;
628 {
629 struct sspan *qspan;
630 boolean fmatch;
631 long ival;
632
633 qspan = qtimesize_parse (ztimesize);
634 if (qspan == NULL)
635 return -1;
636
637 fmatch = ftimespan_match (qspan, &ival, (int *) NULL);
638
639 utimespan_free (qspan);
640
641 if (! fmatch)
642 return -1;
643
644 return ival;
645 }
646
647 /* Determine the maximum size that may ever be transferred, according
648 to a time size string. This returns -1 if there is no limit. */
649
650 long
cmax_size_ever(ztimesize)651 cmax_size_ever (ztimesize)
652 const char *ztimesize;
653 {
654 struct sspan *qspan;
655 long imax;
656 struct sspan *q;
657
658 qspan = qtimesize_parse (ztimesize);
659 if (qspan == NULL)
660 return -1;
661
662 /* Look through the list of spans. If there is any gap larger than
663 1 hour, we assume there are no restrictions. Otherwise we keep
664 track of the largest value we see. I picked 1 hour arbitrarily,
665 on the theory that a 1 hour span to transfer large files might
666 actually occur, and is probably not an accident. */
667
668 if (qspan->istart >= 60)
669 {
670 utimespan_free (qspan);
671 return -1;
672 }
673
674 imax = -1;
675
676 for (q = qspan; q != NULL; q = q->qnext)
677 {
678 if (q->qnext == NULL)
679 {
680 if (q->iend <= 6 * 24 * 60 + 23 * 60)
681 {
682 utimespan_free (qspan);
683 return -1;
684 }
685 }
686 else
687 {
688 if (q->iend + 60 <= q->qnext->istart)
689 {
690 utimespan_free (qspan);
691 return -1;
692 }
693 }
694
695 if (imax < q->ival)
696 imax = q->ival;
697 }
698
699 utimespan_free (qspan);
700
701 return imax;
702 }
703