Home | History | Annotate | Download | only in bnu
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 
     31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     32 
     33 
     34 #include "uucp.h"
     35 
     36 GLOBAL char _Protocol[40] = "";	/* working protocol string */
     37 static char _ProtoSys[40] = "";	/* protocol string from Systems file entry */
     38 static char _ProtoDev[40] = "";	/* protocol string from Devices file entry */
     39 EXTERN char _ProtoCfg[];	/* protocol string from Config  file entry */
     40 
     41 EXTERN jmp_buf Sjbuf;
     42 EXTERN unsigned expecttime;
     43 
     44 GLOBAL int	Modemctrl;
     45 
     46 /* Parity control during login procedure */
     47 #define	P_ZERO	0
     48 #define	P_ONE	1
     49 #define	P_EVEN	2
     50 #define	P_ODD	3
     51 
     52 static char	par_tab[128];
     53 
     54 EXTERN void alarmtr();
     55 static void addProto(), mergeProto(), removeProto();
     56 static void bld_partab();
     57 static char *nextProto();
     58 EXTERN char *findProto();
     59 static void getProto();
     60 EXTERN int getto();		/* make this static when ct uses altconn() */
     61 EXTERN int chat(), rddev(), expect(), wrstr(), wrchr();
     62 EXTERN int processdev(), getdevline(), getsysline(), sysaccess();
     63 EXTERN int clear_hup();
     64 EXTERN char *currsys(), *currdev();
     65 static int finds();
     66 static int wait_for_hangup(), expect_str();
     67 
     68 EXTERN void sendthem(), nap();
     69 static int notin(), ifdate(), checkdate(), checktime(), classmatch();
     70 
     71 GLOBAL char *Myline = CNULL;	/* to force which line will be used */
     72 GLOBAL char *Mytype = CNULL;	/* to force selection of specific device type */
     73 GLOBAL int Dologin;		/* to force login chat sequence */
     74 
     75 /*
     76  * conn - place a telephone call to system and login, etc.
     77  *
     78  * return codes:
     79  *	FAIL - connection failed
     80  *	>0  - file no.  -  connect ok
     81  * When a failure occurs, Uerror is set.
     82  */
     83 
     84 GLOBAL int
     85 conn(system)
     86 char *system;
     87 {
     88 	int nf, fn;
     89 	char *flds[F_MAX+1];
     90 	EXTERN void sysreset();
     91 
     92 	CDEBUG(4, "conn(%s)\n", system);
     93 	Uerror = 0;
     94 	while ((nf = finds(system, flds, F_MAX)) > 0) {
     95 		fn = getto(flds);
     96 		CDEBUG(4, "getto ret %d\n", fn);
     97 		if (fn < 0)
     98 		    continue;
     99 
    100 #ifdef TIOCSPGRP
    101 		{
    102 #ifdef ATTSV
    103 		int pgrp = getpgrp();
    104 #else
    105 		int pgrp = getpgrp(0);
    106 #endif
    107 		ioctl(fn, TIOCSPGRP, &pgrp);
    108 		}
    109 #endif
    110 		if (Dologin || EQUALS(Progname, "uucico")) {
    111 		    if (chat(nf - F_LOGIN, flds + F_LOGIN, fn,"","") == SUCCESS) {
    112 			sysreset();
    113 			return(fn); /* successful return */
    114 		    }
    115 
    116 		    /* login failed */
    117 		    DEBUG(6, "close caller (%d)\n", fn);
    118 		    fd_rmlock(fn);
    119 		    close(fn);
    120 		    if (Dc[0] != NULLCHAR) {
    121 			DEBUG(6, "delock line (%s)\n", Dc);
    122 		    }
    123 		} else {
    124 		    sysreset();
    125 		    return(fn);
    126 		}
    127 	}
    128 
    129 	/* finds or getto failed */
    130 	sysreset();
    131 	CDEBUG(1, "Call Failed: %s\n", UERRORTEXT);
    132 	return(FAIL);
    133 }
    134 
    135 /*
    136  * getto - connect to remote machine
    137  *
    138  * return codes:
    139  *	>0  -  file number - ok
    140  *	FAIL  -  failed
    141  */
    142 
    143 GLOBAL int
    144 getto(flds)
    145 char *flds[];
    146 {
    147 	char *dev[D_MAX+2], devbuf[BUFSIZ];
    148 	int status;
    149 	int dcf = -1;
    150 	int reread = 0;
    151 	int tries = 0;	/* count of call attempts - for limit purposes */
    152 	EXTERN void devreset();
    153 
    154 	CDEBUG(1, "Device Type %s wanted\n", flds[F_TYPE]);
    155 	Uerror = 0;
    156 	while (tries < TRYCALLS) {
    157 		if ((status=rddev(flds[F_TYPE], dev, devbuf, D_MAX)) == FAIL) {
    158 			if (tries == 0 || ++reread >= TRYCALLS)
    159 				break;
    160 			devreset();
    161 			continue;
    162 		}
    163 		/* check class, check (and possibly set) speed */
    164 		if (classmatch(flds, dev) != SUCCESS) {
    165 		    DEBUG(7, "Skipping entry in '%s'", currdev());
    166 		    DEBUG(7, " - class (%s) not wanted.\n", dev[D_CLASS]);
    167 		    continue;
    168 		}
    169                 DEBUG(5, "Trying device entry '%s' ", dev[D_LINE]);
    170 		DEBUG(5, "from '%s'.\n", currdev());
    171 		if ((dcf = processdev(flds, dev)) >= 0)
    172 			break;
    173 
    174 		switch(Uerror) {
    175 		case SS_CANT_ACCESS_DEVICE:
    176 		case SS_DEVICE_FAILED:
    177 		case SS_LOCKED_DEVICE:
    178 		case SS_CHAT_FAILED:
    179 			break;
    180 		default:
    181 			tries++;
    182 			break;
    183 		}
    184 	}
    185 	devreset();	/* reset devices file(s) */
    186 	if (status == FAIL && !Uerror) {
    187 	    CDEBUG(1, "Requested Device Type Not Found\n%s", "");
    188 	    Uerror = SS_NO_DEVICE;
    189 	}
    190 	return(dcf);
    191 }
    192 
    193 /*
    194  * classmatch - process 'Any' in Devices and Systems and
    195  * 	determine the correct speed, or match for ==
    196  */
    197 
    198 static int
    199 classmatch(flds, dev)
    200 char *flds[], *dev[];
    201 {
    202 	/* check class, check (and possibly set) speed */
    203 	if (EQUALS(flds[F_CLASS], "Any")
    204 	   && EQUALS(dev[D_CLASS], "Any")) {
    205 		dev[D_CLASS] = DEFAULT_BAUDRATE;
    206 		return(SUCCESS);
    207 	} else if (EQUALS(dev[D_CLASS], "Any")) {
    208 		dev[D_CLASS] = flds[F_CLASS];
    209 		return(SUCCESS);
    210 	} else if (EQUALS(flds[F_CLASS], "Any") ||
    211 	EQUALS(flds[F_CLASS], dev[D_CLASS]))
    212 		return(SUCCESS);
    213 	else
    214 		return(FAIL);
    215 }
    216 
    217 
    218 /*
    219  *	rddev - find and unpack a line from device file for this caller type
    220  *	lines starting with whitespace of '#' are comments
    221  *
    222  *	return codes:
    223  *		>0  -  number of arguments in vector - succeeded
    224  *		FAIL - EOF
    225  */
    226 
    227 GLOBAL int
    228 rddev(type, dev, buf, devcount)
    229 char *type;
    230 char *dev[];
    231 char *buf;
    232 {
    233 	char *commap, d_type[BUFSIZ];
    234 	int na;
    235 
    236 	while (getdevline(buf, BUFSIZ)) {
    237 		if (buf[0] == ' ' || buf[0] == '\t'
    238 		    ||  buf[0] == '\n' || buf[0] == '\0' || buf[0] == '#')
    239 			continue;
    240 		na = getargs(buf, dev, devcount);
    241 		ASSERT(na >= D_CALLER, "BAD LINE", buf, na);
    242 
    243 		if ( strncmp(dev[D_LINE],"/dev/",5) == 0 ) {
    244 			/* since cu (altconn()) strips off leading */
    245 			/* "/dev/",  do the same here.  */
    246 			strcpy(dev[D_LINE], &(dev[D_LINE][5]) );
    247 		}
    248 
    249 		/* may have ",M" subfield in D_LINE */
    250 		Modemctrl = FALSE;
    251 		if ( (commap = strchr(dev[D_LINE], ',')) != (char *)NULL ) {
    252 			if ( strcmp( commap, ",M") == SAME )
    253 				Modemctrl = TRUE;
    254 			*commap = '\0';
    255 		}
    256 
    257 		/*
    258 		 * D_TYPE field may have protocol subfield, which
    259 		 * must be pulled off before comparing to desired type.
    260 		 */
    261 		(void)strcpy(d_type, dev[D_TYPE]);
    262 		if ((commap = strchr(d_type, ',')) != (char *)NULL )
    263 			*commap = '\0';
    264 
    265 		/* to force the requested device type to be used. */
    266 		if ((Mytype != NULL) && (!EQUALS(Mytype, d_type)) )
    267 		    continue;
    268 		/* to force the requested line to be used */
    269 		if ((Myline != NULL) && (!EQUALS(Myline, dev[D_LINE])) )
    270 		    continue;
    271 
    272 		bsfix(dev);	/* replace \X fields */
    273 
    274 		if (EQUALS(d_type, type)) {
    275 			getProto( _ProtoDev, dev[D_TYPE] );
    276 			return(na);
    277 		}
    278 	}
    279 	return(FAIL);
    280 }
    281 
    282 
    283 /*
    284  * finds	- set system attribute vector
    285  *
    286  * input:
    287  *	fsys - open Systems file descriptor
    288  *	sysnam - system name to find
    289  * output:
    290  *	flds - attibute vector from Systems file
    291  *	fldcount - number of fields in flds
    292  * return codes:
    293  *	>0  -  number of arguments in vector - succeeded
    294  *	FAIL - failed
    295  * Uerror set:
    296  *	0 - found a line in Systems file
    297  *	SS_BADSYSTEM - no line found in Systems file
    298  *	SS_TIME_WRONG - wrong time to call
    299  */
    300 
    301 static int
    302 finds(sysnam, flds, fldcount)
    303 char *sysnam, *flds[];
    304 {
    305 	static char info[BUFSIZ];
    306 	int na;
    307 
    308 	/* format of fields
    309 	 *	0 name;
    310 	 *	1 time
    311 	 *	2 acu/hardwired
    312 	 *	3 speed
    313 	 *	etc
    314 	 */
    315 	if (sysnam == 0 || *sysnam == 0 ) {
    316 		Uerror = SS_BADSYSTEM;
    317 		return(FAIL);
    318 	}
    319 
    320 	while (getsysline(info, sizeof(info))) {
    321 		na = getargs(info, flds, fldcount);
    322 		bsfix(flds);	/* replace \X fields */
    323 		if ( !EQUALSN(sysnam, flds[F_NAME], MAXBASENAME))
    324 			continue;
    325 		/* check if requested Mytype device type */
    326 		if ((Mytype != CNULL) &&
    327 		    (na <= F_TYPE ||
    328 			!EQUALSN(flds[F_TYPE], Mytype, strlen(Mytype)))) {
    329 		    DEBUG(7, "Skipping entry in '%s'", currsys());
    330 		    DEBUG(7, " - type (%s) not wanted.\n", na > F_TYPE ?
    331 			flds[F_TYPE] : "Missing type entry");
    332 		    continue;
    333 		} else {
    334 		    DEBUG(5, "Trying entry from '%s'", currsys());
    335 		    DEBUG(5, " - device type %s.\n", na > F_TYPE ?
    336 			flds[F_TYPE] : "<Missing type entry>");
    337 		}
    338 		/* OK if not uucico (ie. ct or cu) or the time is right */
    339 		if (!EQUALS(Progname, "uucico") ||
    340 		    (na > F_TIME && ifdate(flds[F_TIME]))) {
    341 			/*  found a good entry  */
    342 			if (na > F_TYPE) {
    343 				getProto(_ProtoSys, flds[F_TYPE]);
    344 				Uerror = 0;
    345 				return(na);	/* FOUND OK LINE */
    346 			}
    347 			DEBUG(5, "Trying entry from '%s'", currsys());
    348 			DEBUG(5, " - Missing type entry for <%s>.\n",
    349 				flds[F_NAME]);
    350 		} else {
    351 			CDEBUG(1, "Wrong Time To Call: %s\n", na > F_TIME ?
    352 			    flds[F_TIME] : "<Missing time entry>");
    353 			if (!Uerror)
    354 				Uerror = SS_TIME_WRONG;
    355 		}
    356 	}
    357 	if (!Uerror)
    358 		Uerror = SS_BADSYSTEM;
    359 	return(FAIL);
    360 }
    361 
    362 /*
    363  * getProto - get the protocol letters from the input string.
    364  * input:
    365  *	str - string from Systems/Devices/Config file,
    366  *		a ',' delimits the protocol string
    367  *		e.g. ACU,g or DK,d
    368  * output:
    369  *	str - the , (if present) will be replaced with NULLCHAR
    370  *
    371  * return:  none
    372  */
    373 
    374 static void
    375 getProto(save, str)
    376 char *save;
    377 char *str;
    378 {
    379 	char *p;
    380 
    381 	*save = NULLCHAR;
    382 	if ( (p=strchr(str, ',')) != NULL) {
    383 		*p = NULLCHAR;
    384 		(void) strcpy(save, p+1);
    385 		DEBUG(7, "Protocol = %s\n", save);
    386 	}
    387 	return;
    388 }
    389 
    390 /*
    391  * check for a specified protocol selection string
    392  * return:
    393  *	protocol string pointer
    394  *	NULL if none specified for LOGNAME
    395  */
    396 GLOBAL char *
    397 protoString(valid)
    398 char *valid;
    399 {
    400 	char *save;
    401 
    402 	save =strdup(valid);
    403 	_Protocol[0] = '\0';
    404 
    405 	if ( _ProtoSys[0] != '\0' )
    406 	    addProto(_ProtoSys, valid);
    407 	if ( _ProtoDev[0] != '\0' )
    408 	    addProto(_ProtoDev, valid);
    409 	if ( _ProtoCfg[0] != '\0' )
    410 	    addProto(_ProtoCfg, valid);
    411 
    412 	if ( _Protocol[0] == '\0' ) {
    413 	    (void) strcpy(valid, save);
    414 	    (void) strcpy(_Protocol, save);
    415 	}
    416 
    417 	return(_Protocol[0] == NULLCHAR ? NULL : _Protocol);
    418 }
    419 
    420 /*
    421  * addProto
    422  *
    423  * Verify that the desired protocols from the Systems and Devices file
    424  * have been compiled into this application.
    425  *
    426  * 	desired -	list of desired protocols
    427  *	valid -		list of protocols that are compiled in.
    428  */
    429 
    430 static void
    431 addProto (desired, valid)
    432 char *desired;
    433 char *valid;
    434 {
    435 	char *protoPtr;
    436 	char *wantPtr;
    437 
    438 	if ( *desired == '\0' )
    439 	    return;
    440 
    441 	if ( *(protoPtr = _Protocol) != NULLCHAR ) {
    442 	    while ( *(protoPtr = nextProto(protoPtr)) != NULLCHAR ) {
    443 		if ( *(wantPtr = findProto(desired, *protoPtr)) == NULLCHAR ) {
    444 		    removeProto(valid, *protoPtr);
    445 		    removeProto(protoPtr, *protoPtr);
    446 		} else {
    447 		    mergeProto(protoPtr, wantPtr);
    448 		    protoPtr++;
    449 		}
    450 	    }
    451 	} else {
    452 	    wantPtr = desired;
    453 	    while ( *(wantPtr = nextProto(wantPtr)) != NULLCHAR ) {
    454 		if ( *(findProto(valid, *wantPtr)) != NULLCHAR ) {
    455 		    mergeProto(protoPtr, wantPtr);
    456 		}
    457 		wantPtr++;
    458 	    }
    459 	}
    460 	if ( *(protoPtr = _Protocol) != NULLCHAR ) {
    461 	    while ( *(protoPtr = nextProto(protoPtr)) != NULLCHAR )
    462 		*(valid++) = *(protoPtr++);
    463 	    *valid = NULLCHAR;
    464 	}
    465 	return;
    466 }
    467 
    468 /*
    469  * mergeProto
    470  *
    471  * input
    472  * 	char *tostring, *fromstring;
    473  */
    474 static void
    475 mergeProto(tostring, fromstring)
    476 char *tostring, *fromstring;
    477 {
    478 	char buffer[BUFSIZ];
    479 	int length;
    480 
    481 	while ( *(tostring = nextProto(tostring)) != NULLCHAR ) {
    482 	    if ( *tostring == *fromstring )
    483 		break;
    484 	    else
    485 		tostring++;
    486 	}
    487 
    488 	if ( *tostring == NULLCHAR ) {
    489 	    length = nextProto(fromstring + 1) - fromstring;
    490 	    (void) strncpy(tostring, fromstring, length);
    491 	    *(tostring + length) = NULLCHAR;
    492 	} else {
    493 	    tostring++;
    494 	    fromstring++;
    495 	    if ( (*tostring !=  '(') && (*fromstring == '(') ) {
    496 		(void) strcpy(buffer, tostring);
    497 		length = nextProto(fromstring) - fromstring;
    498 	        (void) strncpy(tostring, fromstring, length);
    499 		(void) strcpy(tostring+length, buffer);
    500 	    }
    501 	}
    502 	return;
    503 }
    504 
    505 /*
    506  * removeProto
    507  *
    508  * char *old
    509  * char letter
    510  *
    511  * return
    512  *	none
    513  */
    514 static void
    515 removeProto(string, letter)
    516 char *string, letter;
    517 {
    518 	while ( *(string = nextProto(string)) != NULLCHAR ) {
    519 	    if ( *string == letter )
    520 		(void) strcpy(string, nextProto(string+1));
    521 	    else
    522 		string++;
    523 	}
    524 }
    525 
    526 /*
    527  * nextProto
    528  *	char *string;
    529  * return
    530  *	char * to next non-parameter letter
    531  */
    532 static char *
    533 nextProto(string)
    534 char *string;
    535 {
    536 	if ( *string == '(' )
    537 	    while ( *string != NULLCHAR )
    538 		if ( *(string++) == ')' )
    539 		    break;
    540 	return(string);
    541 }
    542 
    543 /*
    544  * findProto
    545  *	char *desired,
    546  *	char protoPtr;
    547  * return
    548  *	char *pointer to found or string terminating NULLCHAR
    549  */
    550 GLOBAL char *
    551 findProto(string, letter)
    552 char *string;
    553 char letter;
    554 {
    555 	while ( *(string = nextProto(string)) != NULLCHAR )
    556 	    if ( *string == letter )
    557 		break;
    558 	    else
    559 		string++;
    560 	return(string);
    561 }
    562 
    563 /*
    564  * chat -	do conversation
    565  * input:
    566  *	nf - number of fields in flds array
    567  *	flds - fields from Systems file
    568  *	fn - write file number
    569  *	phstr1 - phone number to replace \D
    570  *	phstr2 - phone number to replace \T
    571  *
    572  *	return codes:  0  |  FAIL
    573  */
    574 
    575 GLOBAL int
    576 chat(nf, flds, fn, phstr1, phstr2)
    577 char *flds[], *phstr1, *phstr2;
    578 int nf, fn;
    579 {
    580 	char *want, *altern;
    581 	int k, ok;
    582 
    583 	for (k = 0; k < nf; k += 2) {
    584 		want = flds[k];
    585 		ok = FAIL;
    586 		while (ok != 0) {
    587 			altern = index(want, '-');
    588 			if (altern != NULL)
    589 				*altern++ = NULLCHAR;
    590 			ok = expect(want, fn);
    591 			if (ok == 0)
    592 				break;
    593 			if (altern == NULL) {
    594 				Uerror = SS_LOGIN_FAILED;
    595 				logent(UERRORTEXT, "FAILED");
    596 				return(FAIL);
    597 			}
    598 			want = index(altern, '-');
    599 			if (want != NULL)
    600 				*want++ = NULLCHAR;
    601 			sendthem(altern, fn, phstr1, phstr2);
    602 		}
    603 		sleep(2);
    604 		if (flds[k+1])
    605 		    sendthem(flds[k+1], fn, phstr1, phstr2);
    606 	}
    607 	return(0);
    608 }
    609 
    610 #define MR 1000
    611 
    612 /*
    613  *	expect(str, fn)	look for expected string w/ possible special chars
    614  *
    615  *	return codes:
    616  *		0  -  found
    617  *		FAIL  -  too many characters read
    618  *		some character  -  timed out
    619  */
    620 
    621 GLOBAL int
    622 expect(str, fn)
    623 char *str;
    624 int fn;
    625 {
    626 	char *bptr, *sptr;
    627 	char    buf[BUFSIZ];
    628 
    629 	bptr = buf;
    630 
    631 	for (sptr = str; *sptr; sptr++) {
    632 		if (*sptr == '\\') {
    633 			switch (*++sptr) {
    634 			case 'H':
    635 				*bptr++ = '\0';
    636 				if (expect_str(buf, fn) == FAIL) {
    637 					return (FAIL);
    638 				}
    639 				if (wait_for_hangup(fn) == FAIL) {
    640 					return (FAIL);
    641 				}
    642 				bptr = buf;
    643 				continue;
    644 			case '\\':
    645 				*bptr++ = '\\';
    646 				continue;
    647 			default:
    648 				*bptr++ = '\\';
    649 				*bptr++ = *sptr;
    650 				continue;
    651 			}
    652 		} else
    653 			*bptr++ = *sptr;
    654 	}
    655 	*bptr = '\0';
    656 	if (expect_str(buf, fn) == FAIL) {
    657 		return (FAIL);
    658 	}
    659 	return (0);
    660 }
    661 
    662 /*
    663  *	expect_str(str, fn)	look for expected string, w/ no special chars
    664  *
    665  *	return codes:
    666  *		0  -  found
    667  *		FAIL  -  too many characters read
    668  *		some character  -  timed out
    669  */
    670 
    671 GLOBAL int
    672 expect_str(str, fn)
    673 char *str;
    674 int fn;
    675 {
    676 	static char rdvec[MR];
    677 	char *rp = rdvec;
    678 	int kr, c;
    679 	char nextch;
    680 
    681 	*rp = 0;
    682 
    683 	CDEBUG(4, "expect: (%s", "");
    684 	for (c=0; (kr=str[c]) != 0 ; c++)
    685 		if (kr < 040) {
    686 			CDEBUG(4, "^%c", kr | 0100);
    687 		} else
    688 			CDEBUG(4, "%c", kr);
    689 	CDEBUG(4, ")\n%s", "");
    690 
    691 	if (EQUALS(str, "\"\"")) {
    692 		CDEBUG(4, "got it\n%s", "");
    693 		return(0);
    694 	}
    695 	if (*str== '\0') {
    696 		return(0);
    697 	}
    698 	if (setjmp(Sjbuf)) {
    699 		return (FAIL);
    700 	}
    701 	(void) signal(SIGALRM, alarmtr);
    702 	alarm(expecttime);
    703 	while (notin(str, rdvec)) {
    704 		errno = 0;
    705 		kr = (*Read)(fn, &nextch, 1);
    706 		if (kr <= 0) {
    707 			alarm(0);
    708 			CDEBUG(4, "lost line errno - %d\n", errno);
    709 			logent("LOGIN", "LOST LINE");
    710 			return(FAIL);
    711 		}
    712 		c = nextch & 0177;
    713 		CDEBUG(4, "%s", c < 040 ? "^" : "");
    714 		CDEBUG(4, "%c", c < 040 ? c | 0100 : c);
    715 		if ((*rp = nextch & 0177) != NULLCHAR)
    716 			rp++;
    717 		if (rp >= rdvec + MR) {
    718 			CDEBUG(4, "enough already\n%s", "");
    719 			alarm(0);
    720 			return(FAIL);
    721 		}
    722 		*rp = NULLCHAR;
    723 	}
    724 	alarm(0);
    725 	CDEBUG(4, "got it\n%s", "");
    726 	return(0);
    727 }
    728 /*
    729  *	alarmtr()  -  catch alarm routine for "expect".
    730  */
    731 /*ARGSUSED*/
    732 GLOBAL void
    733 alarmtr(sig)
    734 int sig;
    735 {
    736 	CDEBUG(6, "timed out\n%s", "");
    737 	longjmp(Sjbuf, 1);
    738 }
    739 
    740 /*
    741  *	wait_for_hangup() - wait for a hangup to occur on the given device
    742  */
    743 int
    744 wait_for_hangup(dcf)
    745 	int dcf;
    746 {
    747 	int rval;
    748 	char buff[BUFSIZ];
    749 
    750 	CDEBUG(4, "Waiting for hangup\n%s", "");
    751 	while((rval = read(dcf, buff, BUFSIZ)) > 0);
    752 
    753 	if (rval < 0) {
    754 		return (FAIL);
    755 	}
    756 	CDEBUG(4, "Received hangup\n%s", "");
    757 
    758 	if (clear_hup(dcf) != SUCCESS) {
    759 	    CDEBUG(4, "Unable to clear hup on device\n%s", "");
    760 	    return (FAIL);
    761 	}
    762 	return (SUCCESS);
    763 }
    764 
    765 /*
    766  *	sendthem(str, fn, phstr1, phstr2)	send line of chat sequence
    767  *	char *str, *phstr;
    768  *
    769  *	return codes:  none
    770  */
    771 
    772 #define FLUSH() {\
    773 	if ((bptr - buf) > 0)\
    774 		if (wrstr(fn, buf, bptr - buf, echocheck) != SUCCESS)\
    775 			goto err;\
    776 	bptr = buf;\
    777 }
    778 
    779 GLOBAL void
    780 sendthem(str, fn, phstr1, phstr2)
    781 char *str, *phstr1, *phstr2;
    782 int fn;
    783 {
    784 	int sendcr = 1, echocheck = 0;
    785 	char	*sptr, *bptr;
    786 	char	buf[BUFSIZ];
    787 	struct termio	ttybuf;
    788 	static int p_init = 0;
    789 
    790 	if (!p_init) {
    791 		p_init++;
    792 		bld_partab(P_EVEN);
    793 	}
    794 
    795 	/* should be EQUALS, but previous versions had BREAK n for integer n */
    796 	if (PREFIX("BREAK", str)) {
    797 		/* send break */
    798 		CDEBUG(5, "BREAK\n%s", "");
    799 		(*genbrk)(fn);
    800 		return;
    801 	}
    802 
    803 	if (PREFIX("STTY=", str)) {
    804 		CDEBUG(5, "STTY %s\n", str+5);
    805 		setmode(str+5, fn);
    806 		return;
    807 	}
    808 
    809 	if (EQUALS(str, "EOT")) {
    810 		CDEBUG(5, "EOT\n%s", "");
    811 		bptr = buf;
    812 		for (sptr = EOTMSG; *sptr; sptr++)
    813 			*bptr++ = par_tab[*sptr&0177];
    814 		(void) (*Write)(fn, buf, bptr - buf);
    815 		return;
    816 	}
    817 
    818 	/* Set parity as needed */
    819 	if (EQUALS(str, "P_ZERO")) {
    820 		bld_partab(P_ZERO);
    821 		return;
    822 	}
    823 	if (EQUALS(str, "P_ONE")) {
    824 		bld_partab(P_ONE);
    825 		return;
    826 	}
    827 	if (EQUALS(str, "P_EVEN")) {
    828 		bld_partab(P_EVEN);
    829 		return;
    830 	}
    831 	if (EQUALS(str, "P_ODD")) {
    832 		bld_partab(P_ODD);
    833 		return;
    834 	}
    835 
    836 	if (EQUALS(str, "\"\"")) {
    837 		CDEBUG(5, "\"\"\n%s", "");
    838 		str += 2;
    839 	}
    840 
    841 	bptr = buf;
    842 	CDEBUG(5, "sendthem (%s", "");
    843 	for (sptr = str; *sptr; sptr++) {
    844 		if (*sptr == '\\') {
    845 			switch(*++sptr) {
    846 
    847 			/* adjust switches */
    848 			case 'c':	/* no CR after string */
    849 				FLUSH();
    850 				if (sptr[1] == NULLCHAR) {
    851 					CDEBUG(5, "<NO CR>%s", "");
    852 					sendcr = 0;
    853 				} else
    854 					CDEBUG(5, "<NO CR IGNORED>\n%s", "");
    855 				continue;
    856 
    857 			/* stash in buf and continue */
    858 			case 'D':	/* raw phnum */
    859 				strcpy(bptr, phstr1);
    860 				bptr += strlen(bptr);
    861 				continue;
    862 			case 'T':	/* translated phnum */
    863 				strcpy(bptr, phstr2);
    864 				bptr += strlen(bptr);
    865 				continue;
    866 			case 'N':	/* null */
    867 				*bptr++ = 0;
    868 				continue;
    869 			case 's':	/* space */
    870 				*bptr++ = ' ';
    871 				continue;
    872 			case '\\':	/* backslash escapes itself */
    873 				*bptr++ = *sptr;
    874 				continue;
    875 			default:	/* send the backslash */
    876 				*bptr++ = '\\';
    877 				*bptr++ = *sptr;
    878 				continue;
    879 
    880 			/* flush buf, perform action, and continue */
    881 			case 'E':	/* echo check on */
    882 				FLUSH();
    883 				CDEBUG(5, "ECHO CHECK ON\n%s", "");
    884 				echocheck = 1;
    885 				continue;
    886 			case 'e':	/* echo check off */
    887 				FLUSH();
    888 				CDEBUG(5, "ECHO CHECK OFF\n%s", "");
    889 				echocheck = 0;
    890 				continue;
    891 			case 'd':	/* sleep briefly */
    892 				FLUSH();
    893 				CDEBUG(5, "DELAY\n%s", "");
    894 				sleep(2);
    895 				continue;
    896 			case 'p':	/* pause momentarily */
    897 				FLUSH();
    898 				CDEBUG(5, "PAUSE\n%s", "");
    899 				nap(HZ/4);	/* approximately 1/4 second */
    900 				continue;
    901 			case 'K':	/* inline break */
    902 				FLUSH();
    903 				CDEBUG(5, "BREAK\n%s", "");
    904 				(*genbrk)(fn);
    905 				continue;
    906 			case 'M':	/* modem control - set CLOCAL */
    907 			case 'm':	/* no modem control - clear CLOCAL */
    908 				FLUSH();
    909 				CDEBUG(5, ")\n%s CLOCAL ",
    910 					(*sptr == 'M' ? "set" : "clear"));
    911 #ifdef ATTSVTTY
    912 				if ( (*Ioctl)(fn, TCGETA, &ttybuf) != 0  ) {
    913 				    CDEBUG(5, "ignored. TCGETA failed, errno %d", errno);
    914 				} else {
    915 				    if (*sptr == 'M')
    916 					ttybuf.c_cflag |= CLOCAL;
    917 				    else
    918 					ttybuf.c_cflag &= ~CLOCAL;
    919 				    if ( (*Ioctl)(fn, TCSETAW, &ttybuf) != 0 )
    920 					CDEBUG(5, "failed. TCSETAW failed, errno %d", errno);
    921 				}
    922 #endif
    923 				CDEBUG(5, "\n%s", "");
    924 				continue;
    925 			}
    926 		} else
    927 			*bptr++ = *sptr;
    928 	}
    929 	if (sendcr)
    930 		*bptr++ = '\r';
    931 	if ( (bptr - buf) > 0 )
    932 		(void) wrstr(fn, buf, bptr - buf, echocheck);
    933 
    934 err:
    935 	CDEBUG(5, ")\n%s", "");
    936 	return;
    937 }
    938 
    939 /*
    940  * generate parity table for use by sendthem.
    941  */
    942 static void
    943 bld_partab(type)
    944 int type;
    945 {
    946 	int i, j, n;
    947 
    948 	for (i = 0; i < 128; i++) {
    949 		n = 0;
    950 		for (j = i&0177; j; j = (j-1)&j)
    951 			n++;
    952 		par_tab[i] = i;
    953 		if (type == P_ONE
    954 		 || (type == P_EVEN && (n&01) != 0)
    955 		 || (type == P_ODD && (n&01) == 0))
    956 			par_tab[i] |= 0200;
    957 	}
    958 }
    959 
    960 #undef FLUSH
    961 
    962 GLOBAL int
    963 wrstr(fn, buf, len, echocheck)
    964 char *buf;
    965 {
    966 	int 	i;
    967 	char dbuf[BUFSIZ], *dbptr = dbuf;
    968 
    969 	if (echocheck)
    970 		return(wrchr(fn, buf, len));
    971 
    972 	if (Debug >= 5) {
    973 		if (sysaccess(ACCESS_SYSTEMS) == 0) { /* Systems file access ok */
    974 			for (i = 0; i < len; i++) {
    975 				*dbptr = buf[i];
    976 				if (*dbptr < 040) {
    977 					*dbptr++ = '^';
    978 					*dbptr = buf[i] | 0100;
    979 				}
    980 				dbptr++;
    981 			}
    982 			*dbptr = 0;
    983 		} else
    984 			strcpy(dbuf, "????????");
    985 		CDEBUG(5, "%s", dbuf);
    986 	}
    987 	dbptr = dbuf;
    988 	for (i = 0; i < len; i++)
    989 		*dbptr++ = par_tab[buf[i]&0177];
    990 	if ((*Write)(fn, dbuf, len) != len)
    991 		return(FAIL);
    992 	return(SUCCESS);
    993 }
    994 
    995 GLOBAL int
    996 wrchr(fn, buf, len)
    997 int fn;
    998 char *buf;
    999 {
   1000 	int 	i, saccess;
   1001 	char	cin, cout;
   1002 
   1003 	saccess = (sysaccess(ACCESS_SYSTEMS) == 0); /* protect Systems file */
   1004 	if (setjmp(Sjbuf))
   1005 		return(FAIL);
   1006 	(void) signal(SIGALRM, alarmtr);
   1007 
   1008 	for (i = 0; i < len; i++) {
   1009 		cout = buf[i]&0177;
   1010 		if (saccess) {
   1011 			CDEBUG(5, "%s", cout < 040 ? "^" : "");
   1012 			CDEBUG(5, "%c", cout < 040 ? cout | 0100 : cout);
   1013 		} else
   1014 			CDEBUG(5, "?%s", "");
   1015 		if (((*Write)(fn, &par_tab[cout], 1)) != 1)
   1016 			return(FAIL);
   1017 		do {
   1018 			(void) alarm(expecttime);
   1019 			if ((*Read)(fn, &cin, 1) != 1)
   1020 				return(FAIL);
   1021 			(void) alarm(0);
   1022 			cin &= 0177;
   1023 			if (saccess) {
   1024 				CDEBUG(5, "%s", cin < 040 ? "^" : "");
   1025 				CDEBUG(5, "%c", cin < 040 ? cin | 0100 : cin);
   1026 			} else
   1027 				CDEBUG(5, "?%s", "");
   1028 		} while (cout != cin);
   1029 	}
   1030 	return(SUCCESS);
   1031 }
   1032 
   1033 
   1034 /*
   1035  *	notin(sh, lg)	check for occurrence of substring "sh"
   1036  *	char *sh, *lg;
   1037  *
   1038  *	return codes:
   1039  *		0  -  found the string
   1040  *		1  -  not in the string
   1041  */
   1042 
   1043 static int
   1044 notin(sh, lg)
   1045 char *sh, *lg;
   1046 {
   1047 	while (*lg != NULLCHAR) {
   1048 		if (PREFIX(sh, lg))
   1049 			return(0);
   1050 		else
   1051 			lg++;
   1052 	}
   1053 	return(1);
   1054 }
   1055 
   1056 
   1057 /*
   1058  *	ifdate(s)
   1059  *	char *s;
   1060  *
   1061  *	ifdate  -  this routine will check a string (s)
   1062  *	like "MoTu0800-1730" to see if the present
   1063  *	time is within the given limits.
   1064  *
   1065  *	SIDE EFFECT - Retrytime is set to number following ";"
   1066  *	SIDE EFFECT - MaxGrade is set to character following "/"
   1067  *
   1068  *	if a grade is specified, iswrk() is consulted, so that we don't
   1069  *	place calls when there's only low priority work.  this will appear
   1070  *	as a "wrong time to call" in the status file.  sorry.
   1071  *
   1072  *	String alternatives:
   1073  *		Wk - Mo thru Fr
   1074  *		zero or one time means all day
   1075  *		Any - any day
   1076  *
   1077  *	return codes:
   1078  *		0  -  not within limits, or grade too low
   1079  *		1  -  within limits
   1080  */
   1081 
   1082 static int
   1083 ifdate(s)
   1084 char *s;
   1085 {
   1086 	char	*r;
   1087 #ifdef MAXGRADE
   1088 	char	*m, grade;
   1089 #endif
   1090 	struct tm	*tp;
   1091 	time_t	clock;
   1092 	int	t__now;
   1093 
   1094 	time(&clock);
   1095 	tp = localtime(&clock);
   1096 	t__now = tp->tm_hour * 100 + tp->tm_min;	/* "navy" time */
   1097 
   1098 	/*
   1099 	 *	pick up retry time for failures and max grade
   1100 	 *	global variables Retrytime and MaxGrade are set here
   1101 	 */
   1102 	r = strrchr(s, ';');
   1103 
   1104 	/* set retry time */
   1105 	if (r != NULL) {
   1106 	    if (isdigit(r[1])) {
   1107 		if (sscanf(r+1, "%ld", &Retrytime) < 1)
   1108 			Retrytime = 5;	/* 5 minutes is error default */
   1109 		DEBUG(5, "Retry time set to %d minutes\n", Retrytime);
   1110 		Retrytime *= 60;	/* convert to seconds */
   1111 		*r = NULLCHAR;		/* blow away retry time field */
   1112 	    }
   1113 	} else
   1114 	    Retrytime = 0;	/* use exponential backoff */
   1115 
   1116 #ifdef MAXGRADE
   1117 	/* set grade */
   1118 	MaxGrade = NULLCHAR;			/* default */
   1119 	m = strrchr(s, '/');
   1120 	if (m != NULL) {
   1121 	    if (isalnum(m[1]))
   1122 		MaxGrade = m[1];	/* you asked for it! */
   1123 	    *m = NULLCHAR;		/* blow away max grade field */
   1124 	    DEBUG(5, "Max Grade set to %c\n", MaxGrade);
   1125 	}
   1126 
   1127 	/* test grade */
   1128 	if (MaxGrade != NULLCHAR) {
   1129 	    grade = iswrk(CNULL);
   1130 	    if (grade == NULLCHAR || MaxGrade < grade) {
   1131 		DEBUG(4, "No work of grade %c -- no call\n", MaxGrade);
   1132 		return(0);
   1133 	    }
   1134 	}
   1135 #endif /* MAXGRADE */
   1136 
   1137 
   1138 	while (checkdate(s, tp, t__now) == 0) {
   1139 		s = strchr(s, ',');
   1140 		if (s == CNULL)
   1141 			return(0);
   1142 		s++;
   1143 	}
   1144 	return(1);
   1145 }
   1146 
   1147 /* worker function for ifdate() */
   1148 static int
   1149 checkdate(s, tp, t__now)
   1150 char *s;
   1151 struct tm	*tp;
   1152 int	t__now;
   1153 {
   1154 	static char *days[] = {
   1155 		"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
   1156 	};
   1157 	int	i;
   1158 
   1159 	/*
   1160 	 * check day of week
   1161 	 */
   1162 
   1163 	while (isalpha(*s)) {
   1164 		if (PREFIX("Any", s))
   1165 			return(checktime(s, t__now));
   1166 
   1167 		if (PREFIX("Wk", s) && tp->tm_wday >= 1 && tp->tm_wday <= 5)
   1168 			return(checktime(s, t__now));
   1169 
   1170 		for (i = 0; days[i]; i++)
   1171 			if (PREFIX(days[i], s) && tp->tm_wday == i)
   1172 				return(checktime(s, t__now));
   1173 		s++;
   1174 	}
   1175 
   1176 	return(0);	/* day match failed */
   1177 }
   1178 
   1179 /* day match ok -- check time */
   1180 static int
   1181 checktime(s, t__now)
   1182 char *s;
   1183 int	t__now;
   1184 {
   1185 	int	t__low, t__high;
   1186 
   1187 	while (isalpha(*s))	/* flush day stuff */
   1188 		s++;
   1189 
   1190 	if ((sscanf(s, "%d-%d", &t__low, &t__high) < 2))
   1191 		return(1);	/* time match ok (default) */
   1192 
   1193 	if (t__low == t__high)
   1194 		return(1);
   1195 
   1196 	/* 0000 crossover? */
   1197 	if (t__low < t__high) {
   1198 		if (t__low <= t__now && t__now <= t__high)
   1199 			return(1);
   1200 	} else {
   1201 		if (t__low <= t__now || t__now <= t__high)
   1202 			return(1);
   1203 	}
   1204 
   1205 	return(0);
   1206 }
   1207 
   1208 /*
   1209  *	char *
   1210  *	fdig(cp)	find first digit in string
   1211  *
   1212  *	return - pointer to first digit in string or end of string
   1213  */
   1214 
   1215 GLOBAL char *
   1216 fdig(cp)
   1217 char *cp;
   1218 {
   1219 	char *c;
   1220 
   1221 	for (c = cp; *c; c++)
   1222 		if (*c >= '0' && *c <= '9')
   1223 			break;
   1224 	return(c);
   1225 }
   1226 
   1227 
   1228 #ifdef FASTTIMER
   1229 /*	Sleep in increments of 60ths of second.	*/
   1230 GLOBAL void
   1231 nap (time)
   1232 int time;
   1233 {
   1234 	static int fd;
   1235 
   1236 	if (fd == 0)
   1237 		fd = open (FASTTIMER, 0);
   1238 
   1239 	(void) (*Read)(fd, 0, time);
   1240 	return;
   1241 }
   1242 
   1243 #endif /* FASTTIMER */
   1244 
   1245 #if defined(BSD4_2) || defined(ATTSVR4)
   1246 
   1247 	/* nap(n) -- sleep for 'n' ticks of 1/60th sec each. */
   1248 	/* This version uses the select system call */
   1249 
   1250 
   1251 GLOBAL void
   1252 nap(n)
   1253 unsigned n;
   1254 {
   1255 	struct timeval tv;
   1256 
   1257 	if (n==0)
   1258 		return;
   1259 	tv.tv_sec = n/60;
   1260 	tv.tv_usec = ((n%60)*1000000L)/60;
   1261 	(void) select(32, 0, 0, 0, &tv);
   1262 	return;
   1263 }
   1264 
   1265 #endif /* BSD4_2 || ATTSVR4 */
   1266 
   1267 #ifdef NONAP
   1268 
   1269 /*	nap(n) where n is ticks
   1270  *
   1271  *	loop using n/HZ part of a second
   1272  *	if n represents more than 1 second, then
   1273  *	use sleep(time) where time is the equivalent
   1274  *	seconds rounded off to full seconds
   1275  *	NOTE - this is a rough approximation and chews up
   1276  *	processor resource!
   1277  */
   1278 
   1279 GLOBAL void
   1280 nap(n)
   1281 unsigned n;
   1282 {
   1283 	struct tms	tbuf;
   1284 	long endtime;
   1285 	int i;
   1286 
   1287 	if (n > HZ) {
   1288 		/* > second, use sleep, rounding time */
   1289 		sleep( (int) (((n)+HZ/2)/HZ) );
   1290 		return;
   1291 	}
   1292 
   1293 	/* use timing loop for < 1 second */
   1294 	endtime = times(&tbuf) + 3*n/4;	/* use 3/4 because of scheduler! */
   1295 	while (times(&tbuf) < endtime) {
   1296 	    for (i=0; i<1000; i++, (void) (i*i))
   1297 		;
   1298 	}
   1299 	return;
   1300 }
   1301 
   1302 #endif /* NONAP */
   1303 
   1304 /*
   1305 
   1306  * altconn - place a telephone call to system
   1307  * from cu when telephone number or direct line used
   1308  *
   1309  * return codes:
   1310  *	FAIL - connection failed
   1311  *	>0  - file no.  -  connect ok
   1312  * When a failure occurs, Uerror is set.
   1313  */
   1314 GLOBAL int
   1315 altconn(call)
   1316 struct call *call;
   1317 {
   1318 	int fn = FAIL;
   1319 	char *alt[7];
   1320 	EXTERN char *Myline;
   1321 
   1322 	alt[F_NAME] = "dummy";	/* to replace the Systems file fields  */
   1323 	alt[F_TIME] = "Any";	/* needed for getto(); [F_TYPE] and    */
   1324 	alt[F_TYPE] = "";	/* [F_PHONE] assignment below          */
   1325 	alt[F_CLASS] = call->speed;
   1326 	alt[F_PHONE] = "";
   1327 	alt[F_LOGIN] = "";
   1328 	alt[6] = NULL;
   1329 
   1330 	CDEBUG(4,"altconn called\r\n%s", "");
   1331 
   1332 	/* cu -l dev ...					*/
   1333 	/* if is "/dev/device", strip off "/dev/" because must	*/
   1334 	/* exactly match entries in Devices file, which usually	*/
   1335 	/* omit the "/dev/".  if doesn't begin with "/dev/",	*/
   1336 	/* either they've omitted the "/dev/" or it's a non-	*/
   1337 	/* standard path name.  in either case, leave it as is	*/
   1338 
   1339 	if(call->line != NULL ) {
   1340 		if ( strncmp(call->line, "/dev/", 5) == 0 ) {
   1341 			Myline = (call->line + 5);
   1342 		} else {
   1343 			Myline = call->line;
   1344 		}
   1345 	}
   1346 
   1347 	/* cu  ... telno */
   1348 	if(call->telno != NULL) {
   1349 		alt[F_PHONE] = call->telno;
   1350 		alt[F_TYPE] = "ACU";
   1351 	} else {
   1352 	/* cu direct line */
   1353 		alt[F_TYPE] = "Direct";
   1354 	}
   1355 	if (call->type != NULL)
   1356 		alt[F_TYPE] = call->type;
   1357 	fn = getto(alt);
   1358 	CDEBUG(4, "getto ret %d\n", fn);
   1359 
   1360 	return(fn);
   1361 
   1362 }
   1363