xref: /386bsd/usr/src/libexec/uucp/time.c (revision a2142627)
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