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