root/usr/src/lib/libnsl/dial/conn.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "mt.h"
#include "uucp.h"
static char _ProtoSys[40];      /* protocol string from Systems file entry */
static char _ProtoDev[40];      /* protocol string from Devices file entry */
static char _ProtoCfg[];        /* protocol string from Config  file entry */

static jmp_buf Sjbuf;
static unsigned expecttime;

static int      Modemctrl;

static void alarmtr(int);
static void getProto(char *, char *);
static int finds(char *, char *[], int);
static int getto(char *[]);     /* make this static when ct uses altconn() */
static int chat(int, char *[], int, char *, char *);
static int rddev(char *, char *[], char *, int);
static int expect(char *, int);
static int wrstr(int, char *, int, int);
static int wrchr(int, char *, int);
static int processdev(char *[], char *[]);
static int getdevline(char *, int);
static int getsysline(char *, int);
static int sysaccess(int);
static int clear_hup(int);
#ifndef SMALL
static char *currsys(void);
static char *currdev(void);
#endif
static int wait_for_hangup(int);
static int expect_str(char *, int);

static void sendthem(char *, int, char *, char *);
static void nap(unsigned int);
static int notin(char *, char *);
static int ifdate(char *);
static int classmatch(char *[], char *[]);

static char *Myline = CNULL;    /* to force which line will be used */
static char *Mytype = CNULL;    /* to force selection of specific device type */

/*
 * conn - place a telephone call to system and login, etc.
 *
 * return codes:
 *      FAIL - connection failed
 *      >0  - file no.  -  connect ok
 * When a failure occurs, Uerror is set.
 */

static void sysreset(void);
static int
conn(char *system)
{
        int nf, fn = FAIL;
        char *flds[F_MAX+1];

        CDEBUG(4, "conn(%s)\n", system);
        Uerror = 0;
        while ((nf = finds(system, flds, F_MAX)) > 0) {
                fn = getto(flds);
                CDEBUG(4, "getto ret %d\n", fn);
                if (fn < 0)
                        continue;
                if (EQUALS(Progname, "uucico")) {
                        if (chat(nf - F_LOGIN, flds + F_LOGIN, fn, "", "") ==
                            SUCCESS) {
                                sysreset();
                                return (fn); /* successful return */
                        }

                        /* login failed */
                        DEBUG(6, "close caller (%d)\n", fn);
                        fd_rmlock(fn);
                        (void) close(fn);
                        if (Dc[0] != NULLCHAR) {
                                /*EMPTY*/
                                DEBUG(6, "delock line (%s)\n", Dc);
                        }
                } else {
                        sysreset();
                        return (fn);
                }
        }

        /* finds or getto failed */
        sysreset();
        CDEBUG(1, "Call Failed: %s\n", UERRORTEXT);
        return (FAIL);
}

static void devreset(void);

/*
 * getto - connect to remote machine
 *
 * return codes:
 *      >0  -  file number - ok
 *      FAIL  -  failed
 */

static int
getto(char *flds[])
{
        char *dev[D_MAX+2], devbuf[BUFSIZ];
        int status;
        int dcf = -1;
        int reread = 0;
        int tries = 0;  /* count of call attempts - for limit purposes */

        CDEBUG(1, "Device Type %s wanted\n", flds[F_TYPE]);
        Uerror = 0;
        while (tries < TRYCALLS) {
                if ((status = rddev(flds[F_TYPE], dev, devbuf, D_MAX)) ==
                    FAIL) {
                        if (tries == 0 || ++reread >= TRYCALLS)
                                break;
                        devreset();
                        continue;
                }
                /* check class, check (and possibly set) speed */
                if (classmatch(flds, dev) != SUCCESS) {
                        DEBUG(7, "Skipping entry in '%s'", currdev());
                        DEBUG(7, " - class (%s) not wanted.\n", dev[D_CLASS]);
                        continue;
                }
                DEBUG(5, "Trying device entry from '%s'.\n", currdev());
                if ((dcf = processdev(flds, dev)) >= 0)
                        break;

                switch (Uerror) {
                case SS_CANT_ACCESS_DEVICE:
                case SS_DEVICE_FAILED:
                case SS_LOCKED_DEVICE:
                        break;
                default:
                        tries++;
                        break;
                }
        }
        devreset();     /* reset devices file(s) */
        if (status == FAIL && !Uerror) {
                CDEBUG(1, "Requested Device Type Not Found\n%s", "");
                Uerror = SS_NO_DEVICE;
        }
        return (dcf);
}

/*
 * classmatch - process 'Any' in Devices and Systems and
 *      determine the correct speed, or match for ==
 */

static int
classmatch(char *flds[], char *dev[])
{
        /* check class, check (and possibly set) speed */
        if (EQUALS(flds[F_CLASS], "Any") && EQUALS(dev[D_CLASS], "Any")) {
                dev[D_CLASS] = DEFAULT_BAUDRATE;
                return (SUCCESS);
        } else if (EQUALS(dev[D_CLASS], "Any")) {
                dev[D_CLASS] = flds[F_CLASS];
                return (SUCCESS);
        } else if (EQUALS(flds[F_CLASS], "Any") ||
            EQUALS(flds[F_CLASS], dev[D_CLASS]))
                return (SUCCESS);
        else
                return (FAIL);
}


/*
 *      rddev - find and unpack a line from device file for this caller type
 *      lines starting with whitespace of '#' are comments
 *
 *      return codes:
 *              >0  -  number of arguments in vector - succeeded
 *              FAIL - EOF
 */

static int
rddev(char *type, char *dev[], char *buf, int devcount)
{
        char *commap, d_type[BUFSIZ];
        int na;

        while (getdevline(buf, BUFSIZ)) {
                if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' ||
                    buf[0] == '\0' || buf[0] == '#')
                        continue;
                na = getargs(buf, dev, devcount);
                ASSERT(na >= D_CALLER, "BAD LINE", buf, na);

                if (strncmp(dev[D_LINE], "/dev/", 5) == 0) {
                        /* since cu (altconn()) strips off leading */
                        /* "/dev/",  do the same here.  */
                        (void) strcpy(dev[D_LINE], &(dev[D_LINE][5]));
                }

                /* may have ",M" subfield in D_LINE */
                Modemctrl = FALSE;
                if ((commap = strchr(dev[D_LINE], ',')) != NULL) {
                        if (strcmp(commap, ",M") == SAME)
                                Modemctrl = TRUE;
                        *commap = '\0';
                }

                /*
                 * D_TYPE field may have protocol subfield, which
                 * must be pulled off before comparing to desired type.
                 */
                (void) strcpy(d_type, dev[D_TYPE]);
                if ((commap = strchr(d_type, ',')) != NULL)
                        *commap = '\0';

                /* to force the requested device type to be used. */
                if ((Mytype != NULL) && (!EQUALS(Mytype, d_type)))
                        continue;
                /* to force the requested line to be used */
                if ((Myline != NULL) && (!EQUALS(Myline, dev[D_LINE])))
                        continue;

                bsfix(dev);     /* replace \X fields */

                if (EQUALS(d_type, type)) {
                        getProto(_ProtoDev, dev[D_TYPE]);
                        return (na);
                }
        }
        return (FAIL);
}


/*
 * finds        - set system attribute vector
 *
 * input:
 *      fsys - open Systems file descriptor
 *      sysnam - system name to find
 * output:
 *      flds - attibute vector from Systems file
 *      fldcount - number of fields in flds
 * return codes:
 *      >0  -  number of arguments in vector - succeeded
 *      FAIL - failed
 * Uerror set:
 *      0 - found a line in Systems file
 *      SS_BADSYSTEM - no line found in Systems file
 *      SS_TIME_WRONG - wrong time to call
 */

static int
finds(char *sysnam, char *flds[], int fldcount)
{
        static char *info;      /* dynamically allocated BUFSIZ */
        int na;

        /*
         * format of fields
         *      0 name;
         *      1 time
         *      2 acu/hardwired
         *      3 speed
         *      etc
         */

        if (sysnam == 0 || *sysnam == 0) {
                Uerror = SS_BADSYSTEM;
                return (FAIL);
        }

        if (info == NULL) {
                info = malloc(BUFSIZ);
                if (info == NULL) {
                        DEBUG(1, "malloc failed for info in finds\n", 0);
                        return (0);
                }
        }
        while (getsysline(info, BUFSIZ)) {
                na = getargs(info, flds, fldcount);
                bsfix(flds);    /* replace \X fields */
                if (!EQUALSN(sysnam, flds[F_NAME], MAXBASENAME))
                        continue;
                /* check if requested Mytype device type */
                if ((Mytype != CNULL) &&
                    (!EQUALSN(flds[F_TYPE], Mytype, strlen(Mytype)))) {
                        DEBUG(7, "Skipping entry in '%s'", currsys());
                        DEBUG(7, " - type (%s) not wanted.\n", flds[F_TYPE]);
                        continue;
                } else {
                        /*EMPTY*/
                        DEBUG(5, "Trying entry from '%s'", currsys());
                        DEBUG(5, " - device type %s.\n", flds[F_TYPE]);
                }
                /* OK if not uucico (ie. ct or cu) or the time is right */
                if (!EQUALS(Progname, "uucico") || ifdate(flds[F_TIME])) {
                        /*  found a good entry  */
                        getProto(_ProtoSys, flds[F_TYPE]);
                        Uerror = 0;
                        return (na);    /* FOUND OK LINE */
                }
                CDEBUG(1, "Wrong Time To Call: %s\n", flds[F_TIME]);
                Uerror = SS_TIME_WRONG;
        }
        if (!Uerror)
                Uerror = SS_BADSYSTEM;
        return (FAIL);
}

/*
 * getProto - get the protocol letters from the input string.
 * input:
 *      str - string from Systems/Devices/Config file,
 *              a ',' delimits the protocol string
 *              e.g. ACU,g or DK,d
 * output:
 *      str - the , (if present) will be replaced with NULLCHAR
 *
 * return:  none
 */

static void
getProto(char *save, char *str)
{
        char *p;

        *save = NULLCHAR;
        if ((p = strchr(str, ',')) != NULL) {
                *p = NULLCHAR;
                (void) strcpy(save, p+1);
                DEBUG(7, "Protocol = %s\n", save);
        }
}

/*
 * chat -       do conversation
 * input:
 *      nf - number of fields in flds array
 *      flds - fields from Systems file
 *      fn - write file number
 *      phstr1 - phone number to replace \D
 *      phstr2 - phone number to replace \T
 *
 *      return codes:  0  |  FAIL
 */

static int
chat(int nf, char *flds[], int fn, char *phstr1, char *phstr2)
{
        char *want, *altern;
        int k, ok;

        for (k = 0; k < nf; k += 2) {
                want = flds[k];
                ok = FAIL;
                while (ok != 0) {
                        altern = index(want, '-');
                        if (altern != NULL)
                                *altern++ = NULLCHAR;
                        ok = expect(want, fn);
                        if (ok == 0)
                                break;
                        if (altern == NULL) {
                                Uerror = SS_LOGIN_FAILED;
                                logent(UERRORTEXT, "FAILED");
                                return (FAIL);
                        }
                        want = index(altern, '-');
                        if (want != NULL)
                                *want++ = NULLCHAR;
                        sendthem(altern, fn, phstr1, phstr2);
                }
                (void) sleep(2);
                if (flds[k+1])
                        sendthem(flds[k+1], fn, phstr1, phstr2);
        }
        return (0);
}

#define MR 1000

/*
 *      expect(str, fn) look for expected string w/ possible special chars
 *      char *str;
 *
 *      return codes:
 *              0  -  found
 *              FAIL  -  lost line or too many characters read
 *              some character  -  timed out
 */

static int
expect(char *str, int fn)
{
        char *bptr, *sptr;
        char    buf[BUFSIZ];

        bptr = buf;

        for (sptr = str; *sptr; sptr++) {
                if (*sptr == '\\') {
                        switch (*++sptr) {
                        case 'H':
                                *bptr++ = '\0';
                                if (expect_str(buf, fn) == FAIL) {
                                        return (FAIL);
                                }
                                if (wait_for_hangup(fn) == FAIL) {
                                        return (FAIL);
                                }
                                bptr = buf;
                                continue;
                        case '\\':
                                *bptr++ = '\\';
                                continue;
                        default:
                                *bptr++ = '\\';
                                *bptr++ = *sptr;
                                continue;
                        }
                } else
                        *bptr++ = *sptr;
        }
        *bptr = '\0';
        if (expect_str(buf, fn) == FAIL) {
                return (FAIL);
        }
        return (0);
}

/*
 *      expect_str(str, fn)     look for expected string, w/ no special chars
 *
 *      return codes:
 *              0  -  found
 *              FAIL  -  too many characters read
 *              some character  -  timed out
 */

static int
expect_str(char *str, int fn)
{
        static char rdvec[MR];
        char *rp = rdvec;
        int kr, c;
        char nextch;

        *rp = 0;

        CDEBUG(4, "expect: (%s", "");
        for (c = 0; (kr = str[c]) != 0; c++)
                if (kr < 040) {
                        /*EMPTY*/
                        CDEBUG(4, "^%c", kr | 0100);
                } else {
                        /*EMPTY*/
                        CDEBUG(4, "%c", kr);
                }
        CDEBUG(4, ")\n%s", "");

        if (EQUALS(str, "\"\"")) {
                CDEBUG(4, "got it\n%s", "");
                return (0);
        }
        if (*str == '\0')
                return (0);
        if (setjmp(Sjbuf))
                return (FAIL);
        (void) signal(SIGALRM, alarmtr);
        (void) alarm(expecttime);
        while (notin(str, rdvec)) {
                errno = 0;
                kr = (*Read)(fn, &nextch, 1);
                if (kr <= 0) {
                        (void) alarm(0);
                        CDEBUG(4, "lost line errno - %d\n", errno);
                        logent("LOGIN", "LOST LINE");
                        return (FAIL);
                }
                c = nextch & 0177;
                CDEBUG(4, "%s", c < 040 ? "^" : "");
                CDEBUG(4, "%c", c < 040 ? c | 0100 : c);
                if ((*rp = nextch & 0177) != NULLCHAR)
                        rp++;
                if (rp >= rdvec + MR) {
                        CDEBUG(4, "enough already\n%s", "");
                        (void) alarm(0);
                        return (FAIL);
                }
                *rp = NULLCHAR;
        }
        (void) alarm(0);
        CDEBUG(4, "got it\n%s", "");
        return (0);
}


/*
 *      alarmtr()  -  catch alarm routine for "expect".
 */
/*ARGSUSED*/
static void
alarmtr(int sig)
{
        CDEBUG(6, "timed out\n%s", "");
        longjmp(Sjbuf, 1);
}

/*
 *      wait_for_hangup() - wait for a hangup to occur on the given device
 */
int
wait_for_hangup(int dcf)
{
        int rval;
        char buff[BUFSIZ];

        CDEBUG(4, "Waiting for hangup\n%s", "");
        while ((rval = read(dcf, buff, BUFSIZ)) > 0)
                ;

        if (rval < 0)
                return (FAIL);
        CDEBUG(4, "Received hangup\n%s", "");

        if (clear_hup(dcf) != SUCCESS) {
                CDEBUG(4, "Unable to clear hup on device\n%s", "");
                return (FAIL);
        }
        return (SUCCESS);
}

/*
 *      sendthem(str, fn, phstr1, phstr2)       send line of chat sequence
 *      char *str, *phstr;
 *
 *      return codes:  none
 */

#define FLUSH() { \
        if ((bptr - buf) > 0) \
                if (wrstr(fn, buf, bptr - buf, echocheck) != SUCCESS) \
                        goto err; \
        bptr = buf; \
}

static void
sendthem(char *str, int fn, char *phstr1, char *phstr2)
{
        int sendcr = 1, echocheck = 0;
        char    *sptr, *bptr;
        char    buf[BUFSIZ];
        struct termio   ttybuf;

        /* should be EQUALS, but previous versions had BREAK n for integer n */

        if (PREFIX("BREAK", str)) {
                /* send break */
                CDEBUG(5, "BREAK\n%s", "");
                (*genbrk)(fn);
                return;
        }

        if (EQUALS(str, "EOT")) {
                CDEBUG(5, "EOT\n%s", "");
                (void) (*Write)(fn, EOTMSG, strlen(EOTMSG));
                return;
        }

        if (EQUALS(str, "\"\"")) {
                CDEBUG(5, "\"\"\n%s", "");
                str += 2;
        }

        bptr = buf;
        CDEBUG(5, "sendthem (%s", "");
        for (sptr = str; *sptr; sptr++) {
                if (*sptr == '\\') {
                        switch (*++sptr) {

                        /* adjust switches */
                        case 'c':       /* no CR after string */
                                FLUSH();
                                if (sptr[1] == NULLCHAR) {
                                        CDEBUG(5, "<NO CR>%s", "");
                                        sendcr = 0;
                                } else {
                                        /*EMPTY*/
                                        CDEBUG(5, "<NO CR IGNORED>\n%s", "");
                                }
                                continue;
                        }

                        /* stash in buf and continue */
                        switch (*sptr) {
                        case 'D':       /* raw phnum */
                                (void) strcpy(bptr, phstr1);
                                bptr += strlen(bptr);
                                continue;
                        case 'T':       /* translated phnum */
                                (void) strcpy(bptr, phstr2);
                                bptr += strlen(bptr);
                                continue;
                        case 'N':       /* null */
                                *bptr++ = 0;
                                continue;
                        case 's':       /* space */
                                *bptr++ = ' ';
                                continue;
                        case '\\':      /* backslash escapes itself */
                                *bptr++ = *sptr;
                                continue;
                        default:        /* send the backslash */
                                *bptr++ = '\\';
                                *bptr++ = *sptr;
                                continue;

                        /* flush buf, perform action, and continue */
                        case 'E':       /* echo check on */
                                FLUSH();
                                CDEBUG(5, "ECHO CHECK ON\n%s", "");
                                echocheck = 1;
                                continue;
                        case 'e':       /* echo check off */
                                FLUSH();
                                CDEBUG(5, "ECHO CHECK OFF\n%s", "");
                                echocheck = 0;
                                continue;
                        case 'd':       /* sleep briefly */
                                FLUSH();
                                CDEBUG(5, "DELAY\n%s", "");
                                (void) sleep(2);
                                continue;
                        case 'p':       /* pause momentarily */
                                FLUSH();
                                CDEBUG(5, "PAUSE\n%s", "");
                                nap(HZ/4);      /* approximately 1/4 second */
                                continue;
                        case 'K':       /* inline break */
                                FLUSH();
                                CDEBUG(5, "BREAK\n%s", "");
                                (*genbrk)(fn);
                                continue;
                        case 'M':       /* modem control - set CLOCAL */
                        case 'm':       /* no modem control - clear CLOCAL */
                                FLUSH();
                                CDEBUG(5, ")\n%s CLOCAL ",
                                    (*sptr == 'M' ? "set" : "clear"));
                                if ((*Ioctl)(fn, TCGETA, &ttybuf) != 0) {
                                        /*EMPTY*/
                                        CDEBUG(5,
                                            "ignored. TCGETA failed, errno %d",
                                            errno);
                                } else {
                                        if (*sptr == 'M')
                                        ttybuf.c_cflag |= CLOCAL;
                                        else
                                        ttybuf.c_cflag &= ~CLOCAL;
                                        if ((*Ioctl)(fn, TCSETAW, &ttybuf) != 0)
                                                /*EMPTY*/
                                        CDEBUG(5,
                                            "failed. TCSETAW failed, errno %d",
                                            errno);
                                }
                                CDEBUG(5, "\n%s", "");
                                continue;
                        }
                } else
                        *bptr++ = *sptr;
        }
        if (sendcr)
                *bptr++ = '\r';
        if ((bptr - buf) > 0)
                (void) wrstr(fn, buf, bptr - buf, echocheck);

err:
        CDEBUG(5, ")\n%s", "");
}

#undef FLUSH

static int
wrstr(int fn, char *buf, int len, int echocheck)
{
        int     i;
        char dbuf[BUFSIZ], *dbptr = dbuf;

        buf[len] = 0;

        if (echocheck)
                return (wrchr(fn, buf, len));

        if (Debug >= 5) {
                if (sysaccess(ACCESS_SYSTEMS) == 0) {
                        /* Systems file access ok */
                        for (i = 0; i < len; i++) {
                                *dbptr = buf[i];
                                if (*dbptr < 040) {
                                        *dbptr++ = '^';
                                        *dbptr = buf[i] | 0100;
                                }
                                dbptr++;
                        }
                        *dbptr = 0;
                } else
                        (void) strcpy(dbuf, "????????");
                CDEBUG(5, "%s", dbuf);
        }
        if ((*Write)(fn, buf, len) != len)
                return (FAIL);
        return (SUCCESS);
}

static int
wrchr(int fn, char *buf, int len)
{
        int     i, saccess;
        char    cin, cout;

        saccess = (sysaccess(ACCESS_SYSTEMS) == 0); /* protect Systems file */
        if (setjmp(Sjbuf))
                return (FAIL);
        (void) signal(SIGALRM, alarmtr);

        for (i = 0; i < len; i++) {
                cout = buf[i];
                if (saccess) {
                        /*EMPTY*/
                        CDEBUG(5, "%s", cout < 040 ? "^" : "");
                        CDEBUG(5, "%c", cout < 040 ? cout | 0100 : cout);
                } else {
                        /*EMPTY*/
                        CDEBUG(5, "?%s", "");
                }
                if (((*Write)(fn, &cout, 1)) != 1)
                        return (FAIL);
                do {
                        (void) alarm(expecttime);
                        if ((*Read)(fn, &cin, 1) != 1)
                                return (FAIL);
                        (void) alarm(0);
                        cin &= 0177;
                        if (saccess) {
                                /*EMPTY*/
                                CDEBUG(5, "%s", cin < 040 ? "^" : "");
                                CDEBUG(5, "%c", cin < 040 ? cin | 0100 : cin);
                        } else {
                                /*EMPTY*/
                                CDEBUG(5, "?%s", "");
                        }
                } while (cout != (cin & 0177));
        }
        return (SUCCESS);
}


/*
 *      notin(sh, lg)   check for occurrence of substring "sh"
 *      char *sh, *lg;
 *
 *      return codes:
 *              0  -  found the string
 *              1  -  not in the string
 */

static int
notin(char *sh, char *lg)
{
        while (*lg != NULLCHAR) {
                if (PREFIX(sh, lg))
                        return (0);
                else
                        lg++;
        }
        return (1);
}


/*
 *      ifdate(s)
 *      char *s;
 *
 *      ifdate  -  this routine will check a string (s)
 *      like "MoTu0800-1730" to see if the present
 *      time is within the given limits.
 *      SIDE EFFECT - Retrytime is set to number following ";"
 *
 *      String alternatives:
 *              Wk - Mo thru Fr
 *              zero or one time means all day
 *              Any - any day
 *
 *      return codes:
 *              0  -  not within limits
 *              1  -  within limits
 */

static int
ifdate(char *s)
{
        static char *days[] = {
                "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
        };
        time_t  clock;
        int     t__now;
        char    *p;
        struct tm       *tp;

        (void) time(&clock);
        tp = localtime(&clock);
        t__now = tp->tm_hour * 100 + tp->tm_min;        /* "navy" time */

        /*
         *      pick up retry time for failures
         *      global variable Retrytime is set here
         */
        if ((p = rindex(s, ';')) != NULL)
                if (isdigit(p[1])) {
                if (sscanf(p+1, "%ld", &Retrytime) < 1)
                        Retrytime = 5;  /* 5 minutes is error default */
                Retrytime  *= 60;
                *p = NULLCHAR;
                }

        while (*s) {
                int     i, dayok;

                for (dayok = 0; (!dayok) && isalpha(*s); s++) {
                        if (PREFIX("Any", s))
                                dayok = 1;
                        else if (PREFIX("Wk", s)) {
                                if (tp->tm_wday >= 1 && tp->tm_wday <= 5)
                                        dayok = 1;
                        } else
                                for (i = 0; days[i]; i++)
                                        if (PREFIX(days[i], s))
                                                if (tp->tm_wday == i)
                                                        dayok = 1;
                }

                if (dayok) {
                        int     t__low, t__high;

                        while (isalpha(*s))     /* flush remaining day stuff */
                                s++;

                        if ((sscanf(s, "%d-%d", &t__low, &t__high) < 2) ||
                            (t__low == t__high))
                                return (1);

                        /* 0000 crossover? */
                        if (t__low < t__high) {
                                if (t__low <= t__now && t__now <= t__high)
                                        return (1);
                        } else if (t__low <= t__now || t__now <= t__high)
                                return (1);

                        /* aim at next time slot */
                        if ((s = index(s, ',')) == NULL)
                                break;
                }
                if (*s)
                        s++;
        }
        return (0);
}

/*
 *      char *
 *      fdig(cp)        find first digit in string
 *
 *      return - pointer to first digit in string or end of string
 */

static char *
fdig(char *cp)
{
        char *c;

        for (c = cp; *c; c++)
                if (*c >= '0' && *c <= '9')
                        break;
        return (c);
}

        /* nap(n) -- sleep for 'n' ticks of 1/60th sec each. */
        /* This version uses the select system call */


static void
nap(unsigned int n)
{
        struct timeval tv;

        if (n == 0)
                return;
        tv.tv_sec = n/60;
        tv.tv_usec = ((n%60)*1000000L)/60;
        (void) select(32, 0, 0, 0, &tv);
}