407 lines
11 KiB
C
407 lines
11 KiB
C
/* time.c
|
||
Parse a time string into a uuconf_timespan structure.
|
||
|
||
Copyright (C) 1992 Ian Lance Taylor
|
||
|
||
This file is part of the Taylor UUCP uuconf library.
|
||
|
||
This library is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU Library General Public License
|
||
as published by the Free Software Foundation; either version 2 of
|
||
the License, or (at your option) any later version.
|
||
|
||
This library 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
|
||
Library General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Library General Public
|
||
License along with this library; 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 Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
|
||
*/
|
||
|
||
#include "uucnfi.h"
|
||
|
||
#if USE_RCS_ID
|
||
const char _uuconf_time_rcsid[] = "$Id: time.c,v 1.1 1993/08/04 19:35:12 jtc Exp $";
|
||
#endif
|
||
|
||
#include <ctype.h>
|
||
#include <errno.h>
|
||
|
||
static int itadd_span P((struct sglobal *qglobal, int istart, int iend,
|
||
long ival, int cretry,
|
||
int (*picmp) P((long, long)),
|
||
struct uuconf_timespan **pqspan,
|
||
pointer pblock));
|
||
static int itnew P((struct sglobal *qglobal, struct uuconf_timespan **pqset,
|
||
struct uuconf_timespan *qnext, int istart, int iend,
|
||
long ival, int cretry, pointer pblock));
|
||
|
||
/* 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, the retry time, and the comparison function to
|
||
use. */
|
||
|
||
int
|
||
_uuconf_itime_parse (qglobal, ztime, ival, cretry, picmp, pqspan, pblock)
|
||
struct sglobal *qglobal;
|
||
char *ztime;
|
||
long ival;
|
||
int cretry;
|
||
int (*picmp) P((long, long));
|
||
struct uuconf_timespan **pqspan;
|
||
pointer pblock;
|
||
{
|
||
struct uuconf_timespan *qlist;
|
||
char bfirst;
|
||
const char *z;
|
||
|
||
qlist = *pqspan;
|
||
if (qlist == (struct uuconf_timespan *) &_uuconf_unset)
|
||
qlist = NULL;
|
||
|
||
/* Expand the string using a timetable. Keep rechecking the string
|
||
until it does not match. */
|
||
while (TRUE)
|
||
{
|
||
char **pz;
|
||
char *zfound;
|
||
|
||
bfirst = *ztime;
|
||
if (isupper (BUCHAR (bfirst)))
|
||
bfirst = tolower (BUCHAR (bfirst));
|
||
|
||
zfound = NULL;
|
||
pz = qglobal->qprocess->pztimetables;
|
||
|
||
/* We want the last timetable to have been defined with this
|
||
name, so we always look through the entire table. */
|
||
while (*pz != NULL)
|
||
{
|
||
if ((bfirst == (*pz)[0]
|
||
|| (isupper (BUCHAR ((*pz)[0]))
|
||
&& bfirst == tolower (BUCHAR ((*pz)[0]))))
|
||
&& strcasecmp (ztime, *pz) == 0)
|
||
zfound = pz[1];
|
||
pz += 2;
|
||
}
|
||
if (zfound == NULL)
|
||
break;
|
||
ztime = zfound;
|
||
}
|
||
|
||
/* Look through the time string. */
|
||
z = ztime;
|
||
while (*z != '\0')
|
||
{
|
||
int iday;
|
||
boolean afday[7];
|
||
int istart, iend;
|
||
|
||
if (*z == ',' || *z == '|')
|
||
++z;
|
||
if (*z == '\0' || *z == ';')
|
||
break;
|
||
|
||
for (iday = 0; iday < 7; iday++)
|
||
afday[iday] = FALSE;
|
||
|
||
/* Get the days. */
|
||
do
|
||
{
|
||
bfirst = *z;
|
||
if (isupper (BUCHAR (bfirst)))
|
||
bfirst = tolower (BUCHAR (bfirst));
|
||
for (iday = 0; asTdays[iday].zname != NULL; iday++)
|
||
{
|
||
size_t 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)
|
||
return UUCONF_SYNTAX_ERROR;
|
||
}
|
||
while (isalpha (BUCHAR (*z)));
|
||
|
||
/* Get the hours. */
|
||
if (! isdigit (BUCHAR (*z)))
|
||
{
|
||
istart = 0;
|
||
iend = 24 * 60;
|
||
}
|
||
else
|
||
{
|
||
char *zendnum;
|
||
|
||
istart = (int) strtol ((char *) z, &zendnum, 10);
|
||
if (*zendnum != '-' || ! isdigit (BUCHAR (zendnum[1])))
|
||
return UUCONF_SYNTAX_ERROR;
|
||
z = zendnum + 1;
|
||
iend = (int) strtol ((char *) z, &zendnum, 10);
|
||
z = zendnum;
|
||
|
||
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, iret;
|
||
|
||
iminute = iday * 24 * 60;
|
||
if (istart < iend)
|
||
iret = itadd_span (qglobal, iminute + istart,
|
||
iminute + iend, ival, cretry, picmp,
|
||
&qlist, pblock);
|
||
else
|
||
{
|
||
/* Wrap around midnight. */
|
||
iret = itadd_span (qglobal, iminute, iminute + iend,
|
||
ival, cretry, picmp, &qlist, pblock);
|
||
if (iret == UUCONF_SUCCESS)
|
||
iret = itadd_span (qglobal, iminute + istart,
|
||
iminute + 24 * 60, ival, cretry,
|
||
picmp, &qlist, pblock);
|
||
}
|
||
|
||
if (iret != UUCONF_SUCCESS)
|
||
return iret;
|
||
}
|
||
}
|
||
}
|
||
|
||
*pqspan = qlist;
|
||
|
||
return UUCONF_SUCCESS;
|
||
}
|
||
|
||
/* 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 int
|
||
itadd_span (qglobal, istart, iend, ival, cretry, picmp, pqspan, pblock)
|
||
struct sglobal *qglobal;
|
||
int istart;
|
||
int iend;
|
||
long ival;
|
||
int cretry;
|
||
int (*picmp) P((long, long));
|
||
struct uuconf_timespan **pqspan;
|
||
pointer pblock;
|
||
{
|
||
struct uuconf_timespan **pq;
|
||
int iret;
|
||
|
||
/* istart < iend */
|
||
for (pq = pqspan; *pq != NULL; pq = &(*pq)->uuconf_qnext)
|
||
{
|
||
int icmp;
|
||
|
||
/* Invariant: PREV (*pq) == NULL || PREV (*pq)->iend <= istart */
|
||
/* istart < iend && (*pq)->istart < (*pq)->iend */
|
||
|
||
if (iend <= (*pq)->uuconf_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)->uuconf_istart
|
||
&& cretry == (*pq)->uuconf_cretry
|
||
&& (*picmp) (ival, (*pq)->uuconf_ival) == 0)
|
||
{
|
||
(*pq)->uuconf_istart = istart;
|
||
return UUCONF_SUCCESS;
|
||
}
|
||
/* We couldn't combine them. */
|
||
break;
|
||
}
|
||
|
||
if ((*pq)->uuconf_iend <= istart)
|
||
{
|
||
/* (*pq)->istart < (*pq)->iend <= istart < iend */
|
||
/* No overlap. Try attaching this span. */
|
||
if ((*pq)->uuconf_iend == istart
|
||
&& (*pq)->uuconf_cretry == cretry
|
||
&& ((*pq)->uuconf_qnext == NULL
|
||
|| iend <= (*pq)->uuconf_qnext->uuconf_istart)
|
||
&& (*picmp) (ival, (*pq)->uuconf_ival) == 0)
|
||
{
|
||
(*pq)->uuconf_iend = iend;
|
||
return UUCONF_SUCCESS;
|
||
}
|
||
/* 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)->uuconf_ival);
|
||
|
||
if (icmp == 0)
|
||
{
|
||
/* Just expand the old span to include the new span. */
|
||
if (istart < (*pq)->uuconf_istart)
|
||
(*pq)->uuconf_istart = istart;
|
||
if ((*pq)->uuconf_iend >= iend)
|
||
return UUCONF_SUCCESS;
|
||
if ((*pq)->uuconf_qnext == NULL
|
||
|| iend <= (*pq)->uuconf_qnext->uuconf_istart)
|
||
{
|
||
(*pq)->uuconf_iend = iend;
|
||
return UUCONF_SUCCESS;
|
||
}
|
||
/* The span we are adding overlaps the next span as well.
|
||
Expand the old span up to the next old span, and keep
|
||
trying to add the new span. */
|
||
(*pq)->uuconf_iend = (*pq)->uuconf_qnext->uuconf_istart;
|
||
istart = (*pq)->uuconf_iend;
|
||
}
|
||
else if (icmp < 0)
|
||
{
|
||
/* Replace the old span with the new span. */
|
||
if ((*pq)->uuconf_istart < istart)
|
||
{
|
||
/* Save the initial portion of the old span. */
|
||
iret = itnew (qglobal, pq, *pq, (*pq)->uuconf_istart, istart,
|
||
(*pq)->uuconf_ival, (*pq)->uuconf_cretry,
|
||
pblock);
|
||
if (iret != UUCONF_SUCCESS)
|
||
return iret;
|
||
pq = &(*pq)->uuconf_qnext;
|
||
}
|
||
if (iend < (*pq)->uuconf_iend)
|
||
{
|
||
/* Save the final portion of the old span. */
|
||
iret = itnew (qglobal, &(*pq)->uuconf_qnext,
|
||
(*pq)->uuconf_qnext, iend, (*pq)->uuconf_iend,
|
||
(*pq)->uuconf_ival, (*pq)->uuconf_cretry,
|
||
pblock);
|
||
if (iret != UUCONF_SUCCESS)
|
||
return iret;
|
||
}
|
||
(*pq)->uuconf_ival = ival;
|
||
(*pq)->uuconf_istart = istart;
|
||
(*pq)->uuconf_cretry = cretry;
|
||
if ((*pq)->uuconf_qnext == NULL
|
||
|| iend <= (*pq)->uuconf_qnext->uuconf_istart)
|
||
{
|
||
(*pq)->uuconf_iend = iend;
|
||
return UUCONF_SUCCESS;
|
||
}
|
||
/* Move this span up to the next one, and keep trying to add
|
||
the new span. */
|
||
(*pq)->uuconf_iend = (*pq)->uuconf_qnext->uuconf_istart;
|
||
istart = (*pq)->uuconf_iend;
|
||
}
|
||
else
|
||
{
|
||
/* Leave the old span untouched. */
|
||
if (istart < (*pq)->uuconf_istart)
|
||
{
|
||
/* Put in the initial portion of the new span. */
|
||
iret = itnew (qglobal, pq, *pq, istart, (*pq)->uuconf_istart,
|
||
ival, cretry, pblock);
|
||
if (iret != UUCONF_SUCCESS)
|
||
return iret;
|
||
pq = &(*pq)->uuconf_qnext;
|
||
}
|
||
if (iend <= (*pq)->uuconf_iend)
|
||
return UUCONF_SUCCESS;
|
||
/* Keep trying to add the new span. */
|
||
istart = (*pq)->uuconf_iend;
|
||
}
|
||
}
|
||
|
||
/* This is the spot for the new span, and there's no overlap. */
|
||
|
||
return itnew (qglobal, pq, *pq, istart, iend, ival, cretry, pblock);
|
||
}
|
||
|
||
/* A utility function to create a new uuconf_timespan structure. */
|
||
|
||
static int
|
||
itnew (qglobal, pqset, qnext, istart, iend, ival, cretry, pblock)
|
||
struct sglobal *qglobal;
|
||
struct uuconf_timespan **pqset;
|
||
struct uuconf_timespan *qnext;
|
||
int istart;
|
||
int iend;
|
||
long ival;
|
||
int cretry;
|
||
pointer pblock;
|
||
{
|
||
register struct uuconf_timespan *qnew;
|
||
|
||
qnew = ((struct uuconf_timespan *)
|
||
uuconf_malloc (pblock, sizeof (struct uuconf_timespan)));
|
||
if (qnew == NULL)
|
||
{
|
||
qglobal->ierrno = errno;
|
||
return UUCONF_MALLOC_FAILED | UUCONF_ERROR_ERRNO;
|
||
}
|
||
|
||
qnew->uuconf_qnext = qnext;
|
||
qnew->uuconf_istart = istart;
|
||
qnew->uuconf_iend = iend;
|
||
qnew->uuconf_ival = ival;
|
||
qnew->uuconf_cretry = cretry;
|
||
|
||
*pqset = qnew;
|
||
|
||
return UUCONF_SUCCESS;
|
||
}
|