706 lines
16 KiB
C
706 lines
16 KiB
C
/* time.c
|
||
Routines to deal with UUCP time strings.
|
||
|
||
Copyright (C) 1991, 1992 Ian Lance Taylor
|
||
|
||
This file is part of the Taylor UUCP package.
|
||
|
||
This program is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU General Public License as
|
||
published by the Free Software Foundation; either version 2 of the
|
||
License, or (at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful, but
|
||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
||
The author of the program may be contacted at ian@airs.com or
|
||
c/o AIRS, P.O. Box 520, Waltham, MA 02254.
|
||
|
||
$Log: time.c,v $
|
||
Revision 1.1.1.1 1993/03/21 09:45:37 cgd
|
||
initial import of 386bsd-0.1 sources
|
||
|
||
Revision 1.12 1992/03/17 00:32:40 ian
|
||
Cast argument to qttime_parse
|
||
|
||
Revision 1.11 1992/03/09 05:08:16 ian
|
||
Added status for wrong time to call, not used if system can't be called
|
||
|
||
Revision 1.10 1992/03/08 01:52:34 ian
|
||
Removed extraneous semicolons
|
||
|
||
Revision 1.9 1992/03/07 02:56:30 ian
|
||
Rewrote time routines
|
||
|
||
Revision 1.8 1992/02/08 03:54:18 ian
|
||
Include <string.h> only in <uucp.h>, added 1992 copyright
|
||
|
||
Revision 1.7 1992/01/11 17:30:10 ian
|
||
John Antypas: use memcpy instead of relying on structure assignment
|
||
|
||
Revision 1.6 1991/12/29 04:04:18 ian
|
||
Added a bunch of extern definitions
|
||
|
||
Revision 1.5 1991/12/22 20:57:57 ian
|
||
Added externs for strcasecmp or strncasecmp
|
||
|
||
Revision 1.4 1991/09/19 02:22:44 ian
|
||
Chip Salzenberg's patch to allow ";retrytime" at the end of a time string
|
||
|
||
Revision 1.3 1991/09/12 05:04:44 ian
|
||
Wrong sense of comparison in btime_low_grade
|
||
|
||
Revision 1.2 1991/09/11 16:59:00 ian
|
||
fcheck_time and btime_low_grade looped endlessly on unusual grades
|
||
|
||
Revision 1.1 1991/09/10 19:40:31 ian
|
||
Initial revision
|
||
|
||
*/
|
||
|
||
#include "uucp.h"
|
||
|
||
#if USE_RCS_ID
|
||
char time_rcsid[] = "$Id: time.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
|
||
#endif
|
||
|
||
#include <ctype.h>
|
||
|
||
#if HAVE_TIME_H
|
||
#include <time.h>
|
||
#endif
|
||
|
||
#if ! HAVE_TIME_T
|
||
#if HAVE_SYS_TIME_T
|
||
#include <sys/types.h>
|
||
#endif /* HAVE_SYS_TIME_T */
|
||
#endif /* ! HAVE_TIME_T */
|
||
|
||
#include "uutime.h"
|
||
|
||
/* External functions. */
|
||
extern int strncasecmp ();
|
||
extern time_t time ();
|
||
extern struct tm *localtime ();
|
||
|
||
/* Local functions. */
|
||
|
||
static struct sspan *qtnew P((struct sspan *qnext, long ival,
|
||
int istart, int iend, int cretry));
|
||
static struct sspan *qtadd_span P((struct sspan *qlist, long ival,
|
||
int istart, int iend,
|
||
boolean (*picmp) P((long, long)),
|
||
int cretry));
|
||
static struct sspan *qttime_parse P((const char *ztime,
|
||
struct sspan *qlist, long ival,
|
||
boolean (*picmp) P((long, long)),
|
||
int cretry));
|
||
|
||
/* A helper function to create a new time span with the specified
|
||
arguments. */
|
||
|
||
static struct sspan *
|
||
qtnew (qnext, ival, istart, iend, cretry)
|
||
struct sspan *qnext;
|
||
long ival;
|
||
int istart;
|
||
int iend;
|
||
int cretry;
|
||
{
|
||
struct sspan *q;
|
||
|
||
q = (struct sspan *) xmalloc (sizeof (struct sspan));
|
||
q->qnext = qnext;
|
||
q->ival = ival;
|
||
q->istart = istart;
|
||
q->iend = iend;
|
||
q->cretry = cretry;
|
||
return q;
|
||
}
|
||
|
||
/* A simple function to free a list of time spans. */
|
||
|
||
void
|
||
utimespan_free (q)
|
||
struct sspan *q;
|
||
{
|
||
while (q != NULL)
|
||
{
|
||
struct sspan *qnext;
|
||
|
||
qnext = q->qnext;
|
||
xfree ((pointer) q);
|
||
q = qnext;
|
||
}
|
||
}
|
||
|
||
/* Add a time span to an existing list of time spans. We keep the
|
||
list sorted by time to make this operation easier. This modifies
|
||
the existing list, and returns the modified version. It takes a
|
||
comparison function which should return < 0 if the first argument
|
||
should take precedence over the second argument and == 0 if they
|
||
are the same (for grades this is igradecmp; for sizes it is minus
|
||
(the binary operator)). */
|
||
|
||
static struct sspan *
|
||
qtadd_span (qlist, ival, istart, iend, picmp, cretry)
|
||
struct sspan *qlist;
|
||
long ival;
|
||
int istart;
|
||
int iend;
|
||
boolean (*picmp) P((long, long));
|
||
int cretry;
|
||
{
|
||
struct sspan **pq;
|
||
|
||
/* istart < iend */
|
||
for (pq = &qlist; *pq != NULL; pq = &(*pq)->qnext)
|
||
{
|
||
int icmp;
|
||
|
||
/* Invariant: PREV (*pq) == NULL || PREV (*pq)->iend <= istart */
|
||
/* istart < iend && (*pq)->istart < (*pq)->iend */
|
||
|
||
if (iend <= (*pq)->istart)
|
||
{
|
||
/* istart < iend <= (*pq)->istart < (*pq)->iend */
|
||
/* No overlap, and we're at the right spot. See if we can
|
||
combine these spans. */
|
||
if (iend == (*pq)->istart
|
||
&& cretry == (*pq)->cretry
|
||
&& (*picmp) (ival, (*pq)->ival) == 0)
|
||
{
|
||
(*pq)->istart = istart;
|
||
return qlist;
|
||
}
|
||
/* We couldn't combine them. */
|
||
break;
|
||
}
|
||
|
||
if ((*pq)->iend <= istart)
|
||
{
|
||
/* (*pq)->istart < (*pq)->iend <= istart < iend */
|
||
/* No overlap. Try attaching this span. */
|
||
if ((*pq)->iend == istart
|
||
&& (*pq)->cretry == cretry
|
||
&& ((*pq)->qnext == NULL
|
||
|| iend <= (*pq)->qnext->istart)
|
||
&& (*picmp) (ival, (*pq)->ival) == 0)
|
||
{
|
||
(*pq)->iend = iend;
|
||
return qlist;
|
||
}
|
||
/* Couldn't attach; keep looking for the right spot. We
|
||
might be able to combine part of the new span onto an
|
||
existing span, but it's probably not worth it. */
|
||
continue;
|
||
}
|
||
|
||
/* istart < iend
|
||
&& (*pq)->istart < (*pq)->iend
|
||
&& istart < (*pq)->iend
|
||
&& (*pq)->istart < iend */
|
||
/* Overlap. */
|
||
|
||
icmp = (*picmp) (ival, (*pq)->ival);
|
||
|
||
if (icmp == 0)
|
||
{
|
||
/* Just expand the old span to include the new span. */
|
||
if (istart < (*pq)->istart)
|
||
(*pq)->istart = istart;
|
||
if ((*pq)->iend < iend)
|
||
(*pq)->iend = iend;
|
||
}
|
||
else if (icmp < 0)
|
||
{
|
||
/* Replace the old span with the new span. */
|
||
if ((*pq)->istart < istart)
|
||
{
|
||
/* Save the initial portion of the old span. */
|
||
*pq = qtnew (*pq, (*pq)->ival, (*pq)->istart, istart,
|
||
(*pq)->cretry);
|
||
pq = &(*pq)->qnext;
|
||
}
|
||
if (iend < (*pq)->iend)
|
||
{
|
||
/* Save the final portion of the old span. */
|
||
(*pq)->qnext = qtnew ((*pq)->qnext, (*pq)->ival, iend,
|
||
(*pq)->iend, (*pq)->cretry);
|
||
}
|
||
(*pq)->ival = ival;
|
||
(*pq)->istart = istart;
|
||
(*pq)->iend = iend;
|
||
(*pq)->cretry = cretry;
|
||
}
|
||
else
|
||
{
|
||
/* Leave the old span untouched. */
|
||
if (istart < (*pq)->istart)
|
||
{
|
||
/* Put in the initial portion of the new span. */
|
||
*pq = qtnew (*pq, ival, istart, (*pq)->istart, cretry);
|
||
pq = &(*pq)->qnext;
|
||
}
|
||
if ((*pq)->iend < iend)
|
||
{
|
||
/* Put in the final portion of the new span. */
|
||
(*pq)->qnext = qtnew ((*pq)->qnext, ival, (*pq)->iend,
|
||
iend, cretry);
|
||
}
|
||
}
|
||
|
||
return qlist;
|
||
}
|
||
|
||
/* This is the spot for the new span, and there's no overlap. */
|
||
|
||
*pq = qtnew (*pq, ival, istart, iend, cretry);
|
||
|
||
return qlist;
|
||
}
|
||
|
||
/* An array of weekday abbreviations. The code below assumes that
|
||
each one starts with a lower case letter. */
|
||
|
||
static const struct
|
||
{
|
||
const char *zname;
|
||
int imin;
|
||
int imax;
|
||
} asTdays[] =
|
||
{
|
||
{ "any", 0, 6 },
|
||
{ "wk", 1, 5 },
|
||
{ "su", 0, 0 },
|
||
{ "mo", 1, 1 },
|
||
{ "tu", 2, 2 },
|
||
{ "we", 3, 3 },
|
||
{ "th", 4, 4 },
|
||
{ "fr", 5, 5 },
|
||
{ "sa", 6, 6 },
|
||
{ "never", -1, -2 },
|
||
{ NULL, 0, 0 }
|
||
};
|
||
|
||
/* Parse a time string and add it to a span list. This function is
|
||
given the value and comparison function to use. The time string
|
||
continues to a null byte, a space or a semicolon. This returns the
|
||
new span list, or NULL on error. If no time matches, it will wind
|
||
up returning qlist, which may itself be NULL. */
|
||
|
||
static struct sspan *
|
||
qttime_parse (ztime, qlist, ival, picmp, cretry)
|
||
const char *ztime;
|
||
struct sspan *qlist;
|
||
long ival;
|
||
int (*picmp) P((long, long));
|
||
int cretry;
|
||
{
|
||
const char *zend;
|
||
char bfirst;
|
||
int i;
|
||
|
||
zend = ztime + strcspn (ztime, "; ");
|
||
|
||
if (pasTtable == NULL)
|
||
uinittimetables ();
|
||
|
||
/* Expand the string using a timetable. */
|
||
bfirst = tolower (BUCHAR (*ztime));
|
||
for (i = 0; i < cTtable; i++)
|
||
{
|
||
if (bfirst == tolower (BUCHAR (pasTtable[i].zname[0]))
|
||
&& strncasecmp (ztime, pasTtable[i].zname, zend - ztime) == 0)
|
||
{
|
||
ztime = pasTtable[i].ztime;
|
||
zend = ztime + strlen (ztime);
|
||
/* Now search the table for this string. */
|
||
i = -1;
|
||
}
|
||
}
|
||
|
||
/* Look through the portions of the time string separated by a
|
||
comma or a vertical bar. */
|
||
|
||
for (; ztime < zend; ztime += strcspn (ztime, ",|"))
|
||
{
|
||
int iday;
|
||
boolean afday[7];
|
||
const char *z;
|
||
int istart, iend;
|
||
|
||
if (*ztime == ',' || *ztime == '|')
|
||
++ztime;
|
||
|
||
for (iday = 0; iday < 7; iday++)
|
||
afday[iday] = FALSE;
|
||
|
||
/* Get the days. */
|
||
|
||
z = ztime;
|
||
do
|
||
{
|
||
bfirst = tolower (BUCHAR (*z));
|
||
for (iday = 0; asTdays[iday].zname != NULL; iday++)
|
||
{
|
||
int clen;
|
||
|
||
if (bfirst != asTdays[iday].zname[0])
|
||
continue;
|
||
|
||
clen = strlen (asTdays[iday].zname);
|
||
if (strncasecmp (z, asTdays[iday].zname, clen) == 0)
|
||
{
|
||
int iset;
|
||
|
||
for (iset = asTdays[iday].imin;
|
||
iset <= asTdays[iday].imax;
|
||
iset++)
|
||
afday[iset] = TRUE;
|
||
z += clen;
|
||
break;
|
||
}
|
||
}
|
||
if (asTdays[iday].zname == NULL)
|
||
{
|
||
ulog (LOG_ERROR, "%s: unparseable time string", ztime);
|
||
return NULL;
|
||
}
|
||
}
|
||
while (isalpha (BUCHAR (*z)));
|
||
|
||
/* Get the hours. */
|
||
|
||
if (! isdigit (BUCHAR (*z)))
|
||
{
|
||
istart = 0;
|
||
iend = 24 * 60;
|
||
}
|
||
else
|
||
{
|
||
char *zendnum;
|
||
|
||
istart = (int) strtol (z, &zendnum, 10);
|
||
if (*zendnum != '-' || ! isdigit (BUCHAR (zendnum[1])))
|
||
{
|
||
ulog (LOG_ERROR, "%s: unparseable time string", ztime);
|
||
return NULL;
|
||
}
|
||
z = zendnum + 1;
|
||
iend = (int) strtol (z, &zendnum, 10);
|
||
if (*zendnum != '\0'
|
||
&& *zendnum != ' '
|
||
&& *zendnum != ';'
|
||
&& *zendnum != ','
|
||
&& *zendnum != '|')
|
||
{
|
||
ulog (LOG_ERROR, "%s: unparseable time string", ztime);
|
||
return NULL;
|
||
}
|
||
|
||
istart = (istart / 100) * 60 + istart % 100;
|
||
iend = (iend / 100) * 60 + iend % 100;
|
||
}
|
||
|
||
/* Add the times we've found onto the list. */
|
||
|
||
for (iday = 0; iday < 7; iday++)
|
||
{
|
||
if (afday[iday])
|
||
{
|
||
int iminute;
|
||
|
||
iminute = iday * 24 * 60;
|
||
if (istart < iend)
|
||
qlist = qtadd_span (qlist, ival, iminute + istart,
|
||
iminute + iend, picmp, cretry);
|
||
else
|
||
{
|
||
/* Wrap around midnight. */
|
||
qlist = qtadd_span (qlist, ival, iminute,
|
||
iminute + iend, picmp, cretry);
|
||
qlist = qtadd_span (qlist, ival, iminute + istart,
|
||
iminute + 24 * 60, picmp,
|
||
cretry);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return qlist;
|
||
}
|
||
|
||
/* See if the current time matches a time span. If it does, return
|
||
TRUE, set *pival to the value for the matching span, and set
|
||
*pcretry to the retry for the matching span. Otherwise return
|
||
FALSE. */
|
||
|
||
boolean
|
||
ftimespan_match (qspan, pival, pcretry)
|
||
const struct sspan *qspan;
|
||
long *pival;
|
||
int *pcretry;
|
||
{
|
||
time_t inow;
|
||
struct tm *qtm;
|
||
int itm;
|
||
const struct sspan *q;
|
||
|
||
time (&inow);
|
||
qtm = localtime (&inow);
|
||
|
||
/* Get the number of minutes since Sunday for the time. */
|
||
itm = qtm->tm_wday * 24 * 60 + qtm->tm_hour * 60 + qtm->tm_min;
|
||
|
||
for (q = qspan; q != NULL; q = q->qnext)
|
||
{
|
||
if (q->istart <= itm && itm <= q->iend)
|
||
{
|
||
if (pival != NULL)
|
||
*pival = q->ival;
|
||
if (pcretry != NULL)
|
||
*pcretry = q->cretry;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/* Compare two work grades. */
|
||
|
||
static int itgradecmp P((long, long));
|
||
|
||
static int
|
||
itgradecmp (i1, i2)
|
||
long i1;
|
||
long i2;
|
||
{
|
||
return igradecmp ((int) i1, (int) i2);
|
||
}
|
||
|
||
/* Parse a time grade string into a time span. A time grade string is
|
||
a series of single character work grades followed by time strings.
|
||
The time string may end with a semicolon and a retry time. Each
|
||
grade/time/retry tuple is separated by a single space. This
|
||
function returns a time span, or NULL if no time matches or an
|
||
error occurs. */
|
||
|
||
struct sspan *
|
||
qtimegrade_parse (ztimegrade)
|
||
const char *ztimegrade;
|
||
{
|
||
struct sspan *qret;
|
||
|
||
if (ztimegrade == NULL)
|
||
return NULL;
|
||
|
||
qret = NULL;
|
||
|
||
while (TRUE)
|
||
{
|
||
const char *zretry;
|
||
int cretry;
|
||
struct sspan *qnext;
|
||
|
||
zretry = ztimegrade + strcspn (ztimegrade, "; ");
|
||
if (*zretry == ';')
|
||
cretry = atoi (zretry + 1);
|
||
else
|
||
cretry = 0;
|
||
|
||
qnext = qttime_parse (ztimegrade + 1, qret, (long) *ztimegrade,
|
||
itgradecmp, cretry);
|
||
if (qnext != NULL)
|
||
qret = qnext;
|
||
|
||
ztimegrade += strcspn (ztimegrade, " ");
|
||
|
||
if (*ztimegrade == '\0')
|
||
break;
|
||
|
||
++ztimegrade;
|
||
}
|
||
|
||
return qret;
|
||
}
|
||
|
||
/* Compare sizes when putting them into a timestring. */
|
||
|
||
static int itsizecmp P((long, long));
|
||
|
||
static int
|
||
itsizecmp (i1, i2)
|
||
long i1;
|
||
long i2;
|
||
{
|
||
/* We can't just return i1 - i2 because that would be a long. */
|
||
if (i1 < i2)
|
||
return -1;
|
||
else if (i1 == i2)
|
||
return 0;
|
||
else
|
||
return 1;
|
||
}
|
||
|
||
/* Parse a time size string into a span. A time size string is a
|
||
size, a space, a time string, a space, repeated. There is no retry
|
||
time associated with a time size string. */
|
||
|
||
struct sspan *
|
||
qtimesize_parse (ztimesize)
|
||
const char *ztimesize;
|
||
{
|
||
struct sspan *qret;
|
||
|
||
if (ztimesize == NULL)
|
||
return NULL;
|
||
|
||
qret = NULL;
|
||
|
||
while (TRUE)
|
||
{
|
||
long isize;
|
||
char *zend;
|
||
struct sspan *qnext;
|
||
|
||
isize = strtol (ztimesize, &zend, 10);
|
||
|
||
#if DEBUG > 0
|
||
if (*zend != ' ')
|
||
ulog (LOG_FATAL, "qtimesize_parse: Can't happen");
|
||
#endif
|
||
|
||
++zend;
|
||
|
||
qnext = qttime_parse (zend, qret, isize, itsizecmp, 0);
|
||
if (qnext != NULL)
|
||
qret = qnext;
|
||
|
||
ztimesize = zend + strcspn (zend, " ");
|
||
|
||
if (*ztimesize == '\0')
|
||
break;
|
||
|
||
++ztimesize;
|
||
}
|
||
|
||
return qret;
|
||
}
|
||
|
||
/* Determine the grade of work we are permitted to do at the current
|
||
time, given a time/grade string. Return a null byte if no grades
|
||
are legal. */
|
||
|
||
char
|
||
btimegrade (ztimegrade)
|
||
const char *ztimegrade;
|
||
{
|
||
struct sspan *qspan;
|
||
boolean fmatch;
|
||
long ival;
|
||
|
||
qspan = qtimegrade_parse (ztimegrade);
|
||
if (qspan == NULL)
|
||
return '\0';
|
||
|
||
fmatch = ftimespan_match (qspan, &ival, (int *) NULL);
|
||
|
||
utimespan_free (qspan);
|
||
|
||
if (! fmatch)
|
||
return '\0';
|
||
|
||
return (int) ival;
|
||
}
|
||
|
||
/* Determine the maximum size that may be transferred at the present
|
||
time, according to a time size string. This returns -1 if there
|
||
are no restrictions. */
|
||
|
||
long
|
||
cmax_size_now (ztimesize)
|
||
const char *ztimesize;
|
||
{
|
||
struct sspan *qspan;
|
||
boolean fmatch;
|
||
long ival;
|
||
|
||
qspan = qtimesize_parse (ztimesize);
|
||
if (qspan == NULL)
|
||
return -1;
|
||
|
||
fmatch = ftimespan_match (qspan, &ival, (int *) NULL);
|
||
|
||
utimespan_free (qspan);
|
||
|
||
if (! fmatch)
|
||
return -1;
|
||
|
||
return ival;
|
||
}
|
||
|
||
/* Determine the maximum size that may ever be transferred, according
|
||
to a time size string. This returns -1 if there is no limit. */
|
||
|
||
long
|
||
cmax_size_ever (ztimesize)
|
||
const char *ztimesize;
|
||
{
|
||
struct sspan *qspan;
|
||
long imax;
|
||
struct sspan *q;
|
||
|
||
qspan = qtimesize_parse (ztimesize);
|
||
if (qspan == NULL)
|
||
return -1;
|
||
|
||
/* Look through the list of spans. If there is any gap larger than
|
||
1 hour, we assume there are no restrictions. Otherwise we keep
|
||
track of the largest value we see. I picked 1 hour arbitrarily,
|
||
on the theory that a 1 hour span to transfer large files might
|
||
actually occur, and is probably not an accident. */
|
||
|
||
if (qspan->istart >= 60)
|
||
{
|
||
utimespan_free (qspan);
|
||
return -1;
|
||
}
|
||
|
||
imax = -1;
|
||
|
||
for (q = qspan; q != NULL; q = q->qnext)
|
||
{
|
||
if (q->qnext == NULL)
|
||
{
|
||
if (q->iend <= 6 * 24 * 60 + 23 * 60)
|
||
{
|
||
utimespan_free (qspan);
|
||
return -1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (q->iend + 60 <= q->qnext->istart)
|
||
{
|
||
utimespan_free (qspan);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
if (imax < q->ival)
|
||
imax = q->ival;
|
||
}
|
||
|
||
utimespan_free (qspan);
|
||
|
||
return imax;
|
||
}
|