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