xref: /illumos-gate/usr/src/cmd/bnu/conn.c (revision 657a8c206b913d1ee578fd725f0b25eca5b77253)
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