xref: /original-bsd/usr.bin/uucp/uucico/conn.c (revision 1db732ef)
1 #ifndef lint
2 static char sccsid[] = "@(#)conn.c	5.10 (Berkeley) 01/24/86";
3 #endif
4 
5 #include <signal.h>
6 #include "uucp.h"
7 #include <setjmp.h>
8 #include <ctype.h>
9 #include <errno.h>
10 #ifdef	USG
11 #include <termio.h>
12 #include <fcntl.h>
13 #endif
14 #ifndef	USG
15 #include <sgtty.h>
16 #endif
17 #ifdef BSD4_2
18 #include <sys/time.h>
19 #else
20 #include <time.h>
21 #endif
22 
23 #define MAXC 1000
24 
25 extern jmp_buf Sjbuf;
26 jmp_buf Cjbuf;
27 extern int errno, onesys;
28 extern char *sys_errlist[];
29 extern char MaxGrade, DefMaxGrade;
30 
31 /* Parity control during login procedure */
32 #define	P_ZERO	0
33 #define	P_ONE	1
34 #define	P_EVEN	2
35 #define	P_ODD	3
36 
37 #define ABORT -2
38 
39 char 	*AbortOn = NULL;
40 char	par_tab[128];	/* must be power of two */
41 int	linebaudrate;	/* used for the sleep test in pk1.c */
42 int next_fd = -1;	/* predicted fd to close interrupted opens */
43 
44 char *PCP = "PCP";	/* PC Pursuit device type */
45 /*
46  *	catch alarm routine for "expect".
47  */
48 alarmtr()
49 {
50 	signal(SIGALRM, alarmtr);
51 	if (next_fd >= 0) {
52 		if (close(next_fd))
53 			logent("FAIL", "ACU LINE CLOSE");
54 		next_fd = -1;
55 	}
56 	longjmp(Sjbuf, 1);
57 }
58 
59 /* This template is for seismo to call ihnp4
60  * the 3 lines marked ---> will be overwritten for the appropriate city
61  */
62 #define PCP_BAUD	3
63 #define PCP_PHONE	4
64 #define PCP_CALLBACK	8
65 #define PCP_CITY	10
66 #define PCP_RPHONE	12
67 #define NPCFIELDS	15
68 
69 static char *PCFlds[] = {
70 	"PC-PURSUIT",
71 	"Any",
72 	"ACU",
73 	"1200",
74 	CNULL,	/* <--- **** Welcome to Telenet PC Pursuit ***** */
75 	"ABORT",
76 	"Good",	/* Abort of Good bye! */
77 	")", 	/* <--- Enter your 7-digit phone number (xxx-xxxx) */
78 	CNULL,	/* ---> 528-1234 */
79 	"call?", 	/* <--- Which city do you wish to call? */
80 	CNULL,	/* ---> CHICAGO */
81 	")", 	/* <--- Enter the phone number you wish to call (xxx-xxxx) */
82 	CNULL,	/* ---> 690-7171 */
83 	"R)?", 	/* <--- You are #1 in the queue. Do you want to wait, or Restart (Y/N/R)? */
84 	"Y",
85 	CNULL 	/* <--- .....Good Bye! */
86 };
87 
88 static char PCP_brand[20];
89 
90 /*
91  *	place a telephone call to system and login, etc.
92  *
93  *	return codes:
94  *		CF_SYSTEM: don't know system
95  *		CF_TIME: wrong time to call
96  *		CF_DIAL: call failed
97  *		CF_NODEV: no devices available to place call
98  *		CF_LOGIN: login/password dialog failed
99  *
100  *		>0  - file no.  -  connect ok
101  */
102 
103 int Dcf = -1;
104 char *Flds[MAXC/10];
105 char LineType[10];
106 extern int LocalOnly;
107 
108 conn(system)
109 char *system;
110 {
111 	int nf;
112 	char info[MAXC], wkpre[NAMESIZE], file[NAMESIZE];
113 	register FILE *fsys;
114 	int fcode = 0;
115 
116 	nf = 0;
117 
118 	fsys = fopen(SYSFILE, "r");
119 	ASSERT(fsys != NULL, "CAN'T OPEN", SYSFILE, 0);
120 
121 	DEBUG(4, "finds (%s) called\n", system);
122 keeplooking:
123 	while((nf = finds(fsys, system, info, Flds)) > 0) {
124 		strncpy(LineType, Flds[F_LINE], 10);
125 		if (LocalOnly) {
126 			if (strcmp("TCP", LineType)
127 				&& strcmp("DIR", LineType)
128 				&& strcmp("LOCAL", LineType) ) {
129 					fcode = CF_TIME;
130 					continue;
131 			}
132 		}
133 		sprintf(wkpre, "%c.%.*s", CMDPRE, SYSNSIZE, Rmtname);
134 		if (!onesys && MaxGrade != DefMaxGrade &&
135 			!iswrk(file, "chk", Spool, wkpre))  {
136 				fcode = CF_TIME;
137 				continue;
138 		}
139 		/* For GTE's PC Pursuit */
140 		if (snccmp(LineType, PCP) == SAME) {
141 			FILE *dfp;
142 			int status;
143 			static struct Devices dev;
144 			dfp = fopen(DEVFILE, "r");
145 			ASSERT(dfp != NULL, "Can't open", DEVFILE, 0);
146 			while ((status=rddev(dfp, &dev)) != FAIL
147 				&& strcmp(PCP, dev.D_type) != SAME)
148 					;
149 			fclose(dfp);
150 			if (status == FAIL)
151 				continue;
152 			if (mlock(PCP) == FAIL) {
153 				fcode = CF_NODEV;
154 				logent("DEVICE", "NO");
155 				continue;
156 			}
157 			PCFlds[PCP_BAUD] = dev.D_class;
158 			PCFlds[PCP_PHONE] = dev.D_calldev;
159 			PCFlds[PCP_CALLBACK] = dev.D_arg[D_CHAT];
160 			PCFlds[PCP_CITY] = Flds[F_CLASS];
161 			PCFlds[PCP_RPHONE] = Flds[F_PHONE];
162 			strncpy(PCP_brand, dev.D_brand, sizeof(PCP_brand));
163 			if ((fcode = getto(PCFlds)) < 0)
164 				continue;
165 			Dcf = fcode;
166 			fcode = login(NPCFIELDS, PCFlds, Dcf);
167 			clsacu(); /* Hang up, they'll call back */
168 			if (fcode != SUCCESS) {
169 				fcode = CF_DIAL;
170 				continue;
171 			}
172 			Flds[F_CLASS] = dev.D_class;
173 			Flds[F_PHONE] = dev.D_line;
174 
175 		} /* end PC Pursuit */
176 		if ((fcode = getto(Flds)) > 0)
177 			break;
178 	}
179 
180 	if (nf <= 0) {
181 		fclose(fsys);
182 		return fcode ? fcode : nf;
183 	}
184 
185 	Dcf = fcode;
186 
187 	if (fcode >= 0 && snccmp(LineType, PCP) == SAME) {
188 		AbortOn = "Good";	/* .... Good Bye */
189 		fcode = expect("****~300", Dcf);
190 		if (fcode != SUCCESS) {
191 			DEBUG(4, "\nexpect timed out\n", CNULL);
192 			fcode = CF_DIAL;
193 		}
194 	}
195 	if (fcode >= 0) {
196 		DEBUG(4, "login %s\n", "called");
197 		fcode = login(nf, Flds, Dcf);
198 	}
199 	if (fcode < 0) {
200 		clsacu();
201 		if (fcode == ABORT) {
202 			fcode = CF_DIAL;
203 			goto  keeplooking;
204 		} else {
205 			fclose(fsys);
206 			return CF_LOGIN;
207 		}
208 	}
209 	fclose(fsys);
210 	fioclex(Dcf);
211 	return Dcf;
212 }
213 
214 /*
215  *	connect to remote machine
216  *
217  *	return codes:
218  *		>0  -  file number - ok
219  *		FAIL  -  failed
220  */
221 
222 getto(flds)
223 register char *flds[];
224 {
225 	register struct condev *cd;
226 	int nulldev(), diropn();
227 	char *line;
228 
229 	DEBUG(4, "getto: call no. %s ", flds[F_PHONE]);
230 	DEBUG(4, "for sys %s\n", flds[F_NAME]);
231 
232 	if (snccmp(flds[F_LINE], "LOCAL") == SAME)
233 		line = "ACU";
234 	else
235 		line = flds[F_LINE];
236 #ifdef DIALINOUT
237 	if (snccmp(line, "ACU") != SAME)
238 		reenable();
239 #endif DIALINOUT
240 	CU_end = nulldev;
241 	if (snccmp(line, PCP) == SAME) {
242 		for(cd = condevs; cd->CU_meth != NULL; cd++) {
243 			if (snccmp(PCP_brand, cd->CU_brand) == SAME) {
244 				CU_end = cd->CU_clos;
245 				return diropn(flds);
246 			}
247 		}
248 		logent(PCP_brand, "UNSUPPORTED ACU TYPE");
249 	} else {
250 		for (cd = condevs; cd->CU_meth != NULL; cd++) {
251 			if (snccmp(cd->CU_meth, line) == SAME) {
252 				DEBUG(4, "Using %s to call\n", cd->CU_meth);
253 				return (*(cd->CU_gen))(flds);
254 			}
255 		}
256 		DEBUG(1, "Can't find %s, assuming DIR\n", flds[F_LINE]);
257 	}
258 	return diropn(flds);	/* search failed, so use direct */
259 }
260 
261 /*
262  *	close call unit
263  *
264  *	return codes:  none
265  */
266 
267 int (*CU_end)() = nulldev;
268 clsacu()
269 {
270 	/* make *sure* Dcf is no longer exclusive.
271 	 * Otherwise dual call-in/call-out modems could get stuck.
272 	 * Unfortunately, doing this here is not ideal, but it is the
273 	 * easiest place to put the call.
274 	 * Hopefully everyone honors the LCK protocol, of course
275 	 */
276 #ifdef	TIOCNXCL
277 	if (!IsTcpIp && Dcf >= 0 && ioctl(Dcf, TIOCNXCL, STBNULL) < 0)
278 		DEBUG(5, "clsacu ioctl %s\n", sys_errlist[errno]);
279 #endif
280 	if  (setjmp(Sjbuf))
281 		logent(Rmtname, "CLOSE TIMEOUT");
282 	else {
283 		signal(SIGALRM, alarmtr);
284 		alarm(20);
285 		(*(CU_end))(Dcf);
286 		alarm(0);
287 	}
288 	if (close(Dcf) == 0) {
289 		DEBUG(4, "fd %d NOT CLOSED by CU_clos\n", Dcf);
290 		logent("clsacu", "NOT CLOSED by CU_clos");
291 	}
292 	Dcf = -1;
293 	CU_end = nulldev;
294 }
295 
296 /*
297  *	expand phone number for given prefix and number
298  */
299 
300 exphone(in, out)
301 register char *in, *out;
302 {
303 	FILE *fn;
304 	char pre[MAXPH], npart[MAXPH], tpre[MAXPH], p[MAXPH];
305 	char buf[BUFSIZ];
306 	register char *s1;
307 
308 	if (!isascii(*in) || !isalpha(*in)) {
309 		strcpy(out, in);
310 		return;
311 	}
312 
313 	s1=pre;
314 	while (isascii(*in) && isalpha(*in))
315 		*s1++ = *in++;
316 	*s1 = '\0';
317 	s1 = npart;
318 	while (*in != '\0')
319 		*s1++ = *in++;
320 	*s1 = '\0';
321 
322 	tpre[0] = '\0';
323 	if ((fn = fopen(DIALFILE, "r")) == NULL)
324 		DEBUG(2, "CAN'T OPEN %s\n", DIALFILE);
325 	else {
326 		while (cfgets(buf, BUFSIZ, fn)) {
327 			if (sscanf(buf, "%s%s", p, tpre) != 2)
328 				continue;
329 			if (strcmp(p, pre) == SAME)
330 				goto found;
331 			tpre[0] = '\0';
332 		}
333 		DEBUG(2, "CAN'T FIND dialcodes prefix '%s'\n", pre);
334 	found:;
335 		fclose(fn);
336 	}
337 
338 	strcpy(out, tpre);
339 	strcat(out, npart);
340 }
341 
342 /*
343  *	read and decode a line from device file
344  *
345  *	return code - FAIL at end-of file; 0 otherwise
346  */
347 
348 rddev(fp, dev)
349 register struct Devices *dev;
350 FILE *fp;
351 {
352 	register int na;
353 
354 	if (!cfgets(dev->D_argbfr, sizeof(dev->D_argbfr), fp))
355 		return FAIL;
356 	na = getargs(dev->D_argbfr, dev->D_arg, 20);
357 	ASSERT(na >= 4, "BAD DEVICE ENTRY", dev->D_argbfr, 0);
358 	if (na == 4) {
359 		dev->D_brand = "";
360 		na++;
361 	}
362 	dev->D_speed = atoi(fdig(dev->D_class));
363 	dev->D_numargs = na;
364 	return 0;
365 }
366 
367 /*
368  *	set system attribute vector
369  *
370  *	return codes:
371  *		>0  -  number of arguments in vector - succeeded
372  *		CF_SYSTEM  -  system name not found
373  *		CF_TIME  -  wrong time to call
374  */
375 
376 finds(fsys, sysnam, info, flds)
377 char *sysnam, info[], *flds[];
378 FILE *fsys;
379 {
380 	int na;
381 	int fcode = 0;
382 
383 	/* format of fields
384 	 *	0 name;
385 	 *	1 time
386 	 *	2 acu/hardwired
387 	 *	3 speed
388 	 *	etc
389 	 */
390 	while (cfgets(info, MAXC, fsys) != NULL) {
391 		na = getargs(info, flds, MAXC/10);
392 		if (strncmp(sysnam, flds[F_NAME], MAXBASENAME) != SAME)
393 			continue;
394 		if (ifdate(flds[F_TIME]) != FAIL)
395 			/*  found a good entry  */
396 			return na;
397 		DEBUG(2, "Wrong time ('%s') to call\n", flds[F_TIME]);
398 		fcode = CF_TIME;
399 	}
400 	return fcode ? fcode : CF_SYSTEM;
401 }
402 
403 /*
404  *	do login conversation
405  *
406  *	return codes:  SUCCESS  |  FAIL
407  */
408 
409 login(nf, flds, fn)
410 register char *flds[];
411 int nf, fn;
412 {
413 	register char *want, *altern;
414 	int k, ok;
415 
416 	ASSERT(nf > 4, "TOO FEW LOG FIELDS", CNULL, nf);
417 	if (setjmp(Cjbuf))
418 		return FAIL;
419 	AbortOn = NULL;
420 	for (k = F_LOGIN; k < nf; k += 2) {
421 		want = flds[k];
422 		ok = FAIL;
423 		while (ok != SUCCESS) {
424 			altern = index(want, '-');
425 			if (altern != NULL)
426 				*altern++ = '\0';
427 			if (strcmp(want, "ABORT") == 0) {
428 				AbortOn = flds[k+1];
429 				DEBUG(4, "ABORT ON: %s\n", AbortOn);
430 				goto nextfield;
431 			}
432 			DEBUG(4, "wanted \"%s\"\n", want);
433 			ok = expect(want, fn);
434 			DEBUG(4, "got: %s\n", ok ? "?" : "that");
435 			if (ok == FAIL) {
436 				if (altern == NULL) {
437 					logent("LOGIN", _FAILED);
438 					return FAIL;
439 				}
440 				want = index(altern, '-');
441 				if (want != NULL)
442 					*want++ = '\0';
443 				sendthem(altern, fn);
444 			} else
445 				if (ok == ABORT) {
446 					logent("LOGIN ABORTED", _FAILED);
447 					return ABORT;
448 				}
449 		}
450 		sleep(1);
451 		if (k+1 < nf)
452 			sendthem(flds[k+1], fn);
453 nextfield: ;
454 	}
455 	return SUCCESS;
456 }
457 
458 
459 /* conditional table generation to support odd speeds */
460 struct sg_spds {int sp_val, sp_name;} spds[] = {
461 #ifdef B50
462 	{  50,	 B50},
463 #endif
464 #ifdef B75
465 	{  75,	 B75},
466 #endif
467 #ifdef B110
468 	{ 110,	B110},
469 #endif
470 #ifdef B150
471 	{ 150,	B150},
472 #endif
473 #ifdef B200
474 	{ 200,	B200},
475 #endif
476 #ifdef B300
477 	{ 300,  B300},
478 #endif
479 #ifdef B600
480 	{600,	B600},
481 #endif
482 #ifdef B1200
483 	{1200, B1200},
484 #endif
485 #ifdef B1800
486 	{1800, B1800},
487 #endif
488 #ifdef B2000
489 	{2000, B2000},
490 #endif
491 #ifdef B2400
492 	{2400, B2400},
493 #endif
494 #ifdef B3600
495 	{3600, B3600},
496 #endif
497 #ifdef B4800
498 	{4800, B4800},
499 #endif
500 #ifdef B7200
501 	{7200, B7200},
502 #endif
503 #ifdef B9600
504 	{9600, B9600},
505 #endif
506 #ifdef B19200
507 	{19200, B19200},
508 #endif
509 #ifdef EXTA
510 	{19200, EXTA},
511 #endif
512 	{0, 0}
513 };
514 
515 /*
516  *	set speed/echo/mode...
517  *
518  *	return codes:  none
519  */
520 
521 fixline(tty, spwant)
522 int tty, spwant;
523 {
524 #ifdef	USG
525 	struct termio ttbuf;
526 #else	!USG
527 	struct sgttyb ttbuf;
528 #endif !USG
529 	register struct sg_spds *ps;
530 	int speed = -1;
531 
532 	for (ps = spds; ps->sp_val; ps++)
533 		if (ps->sp_val == spwant)
534 			speed = ps->sp_name;
535 	ASSERT(speed >= 0, "BAD SPEED", CNULL, speed);
536 #ifdef	USG
537 	if (ioctl(tty, TCGETA, &ttbuf) < 0)
538 		return FAIL;
539 	/* ttbuf.sg_flags = (ANYP|RAW);
540 	ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed; */
541 	ttbuf.c_iflag = (ushort)0;
542 	ttbuf.c_oflag = (ushort)0;
543 	ttbuf.c_cflag = (speed|CS8|HUPCL|CREAD);
544 	ttbuf.c_lflag = (ushort)0;
545 	ttbuf.c_cc[VMIN] = 6;
546 	ttbuf.c_cc[VTIME] = 1;
547 	if (ioctl(tty, TCSETA, &ttbuf) < 0)
548 		return FAIL;
549 #else	!USG
550 	if (ioctl(tty, TIOCGETP, &ttbuf) < 0)
551 		return FAIL;
552 	ttbuf.sg_flags = (ANYP|RAW);
553 	ttbuf.sg_ispeed = ttbuf.sg_ospeed = speed;
554 	if (ioctl(tty, TIOCSETP, &ttbuf) < 0)
555 		return FAIL;
556 #endif
557 #ifndef	USG
558 	if (ioctl(tty, TIOCHPCL, STBNULL) < 0)
559 		return FAIL;
560 	if (ioctl(tty, TIOCEXCL, STBNULL) < 0)
561 		return FAIL;
562 #endif
563 	linebaudrate = spwant;
564 	return SUCCESS;
565 }
566 
567 #define MR 100
568 
569 /*
570  *	look for expected string
571  *
572  *	return codes:
573  *		0  -  found
574  *		FAIL  -  lost line or too many characters read
575  *		some character  -  timed out
576  */
577 
578 expect(str, fn)
579 register char *str;
580 int fn;
581 {
582 	char rdvec[MR];
583 	register char *rp = rdvec, *strptr;
584 	int kr, cnt_char;
585 	char nextch;
586 	int timo = MAXMSGTIME;
587 
588 	if (*str == '\0' || strcmp(str, "\"\"") == SAME)
589 		return SUCCESS;
590 	/* Cleanup str, convert \0xx strings to one char  */
591 	for (strptr = str; *strptr; strptr++) {
592 		if (*strptr == '\\')
593 			switch(*++strptr) {
594 			case 's':
595 				DEBUG(5, "BLANK\n", CNULL);
596 				*strptr = ' ';
597 				break;
598 			default:
599 				strptr--;  /* back up to backslash */
600 				sscanf(strptr + 1,"%o", &cnt_char);
601 				DEBUG(6, "BACKSLASHED %02xH\n", cnt_char);
602 				*strptr = (char) (cnt_char);
603 				strcpy(&strptr[1], &strptr[4]);
604 			}
605 	}
606 
607 	strptr = index(str, '~');
608 	if (strptr != NULL) {
609 		*strptr++ = '\0';
610 		timo = atoi(strptr);
611 		if (timo <= 0)
612 			timo = MAXMSGTIME;
613 	}
614 
615 	if (setjmp(Sjbuf))
616 		return FAIL;
617 	signal(SIGALRM, alarmtr);
618 	alarm(timo);
619 	*rp = 0;
620 	while (notin(str, rdvec)) {
621 		int c;
622 		if(AbortOn != NULL && !notin(AbortOn, rdvec)) {
623 			DEBUG(1, "Call aborted on '%s'\n", AbortOn);
624 			alarm(0);
625 			return ABORT;
626 		}
627 		kr = read(fn, &nextch, 1);
628 		if (kr <= 0) {
629 			alarm(0);
630 			DEBUG(4, "lost line kr - %d\n, ", kr);
631 			logent("LOGIN", "LOST LINE");
632 			return FAIL;
633 		}
634 		c = nextch & 0177;
635 		if (c == '\0')
636 			continue;
637 		DEBUG(4, (isprint(c) || isspace(c)) ? "%c" : "\\%03o", c);
638 		*rp++ = c;
639 		if (rp >= rdvec + MR) {
640 			register char *p;
641 			for (p = rdvec+MR/2; p < rp; p++)
642 				*(p-MR/2) = *p;
643 			rp -= MR/2;
644 		}
645 		*rp = '\0';
646 	}
647 	alarm(0);
648 	return SUCCESS;
649 }
650 
651 
652 /*
653  * Determine next file descriptor that would be allocated.
654  * This permits later closing of a file whose open was interrupted.
655  * It is a UNIX kernel problem, but it has to be handled.
656  * unc!smb (Steve Bellovin) probably first discovered it.
657  */
658 getnextfd()
659 {
660 	close(next_fd = open("/", 0));
661 }
662 
663 /*
664  *	send line of login sequence
665  *
666  *	return codes:  none
667  */
668 sendthem(str, fn)
669 register char *str;
670 int fn;
671 {
672 	register char *strptr;
673 	int i, n, cr = 1;
674 	register char c;
675 	static int p_init = 0;
676 
677 	DEBUG(5, "send \"%s\"\n", str);
678 
679 	if (!p_init) {
680 		p_init++;
681 		bld_partab(P_EVEN);
682 	}
683 
684 	if (prefix("BREAK", str)) {
685 		sscanf(&str[5], "%1d", &i);
686 		if (i <= 0 || i > 10)
687 			i = 3;
688 		/* send break */
689 		genbrk(fn, i);
690 		return;
691 	}
692 
693 	if (prefix("PAUSE", str)) {
694 		sscanf(&str[5], "%1d", &i);
695 		if (i <= 0 || i > 10)
696 			i = 3;
697 		/* pause for a while */
698 		sleep((unsigned)i);
699 		return;
700 	}
701 
702 	if (strcmp(str, "EOT") == SAME) {
703 		p_chwrite(fn, '\04');
704 		return;
705 	}
706 
707 	/* Send a '\n' */
708 	if (strcmp(str, "LF") == SAME) {
709 		p_chwrite(fn, '\n');
710 		return;
711 	}
712 
713 	/* Send a '\r' */
714 	if (strcmp(str, "CR") == SAME) {
715 		p_chwrite(fn, '\r');
716 		return;
717 	}
718 
719 	/* Set parity as needed */
720 	if (strcmp(str, "P_ZERO") == SAME) {
721 		bld_partab(P_ZERO);
722 		return;
723 	}
724 	if (strcmp(str, "P_ONE") == SAME) {
725 		bld_partab(P_ONE);
726 		return;
727 	}
728 	if (strcmp(str, "P_EVEN") == SAME) {
729 		bld_partab(P_EVEN);
730 		return;
731 	}
732 	if (strcmp(str, "P_ODD") == SAME) {
733 		bld_partab(P_ODD);
734 		return;
735 	}
736 
737 	/* If "", just send '\r' */
738 	if (strcmp(str, "\"\"") == SAME) {
739 		p_chwrite(fn, '\r');
740 		return;
741 	}
742 
743 	strptr = str;
744 	while ((c = *strptr++) != '\0') {
745 		if (c == '\\') {
746 			switch(*strptr++) {
747 			case '\0':
748 				DEBUG(5, "TRAILING BACKSLASH IGNORED\n", CNULL);
749 				--strptr;
750 				continue;
751 			case 's':
752 				DEBUG(5, "BLANK\n", CNULL);
753 				c = ' ';
754 				break;
755 			case 'd':
756 				DEBUG(5, "DELAY\n", CNULL);
757 				sleep(1);
758 				continue;
759 			case 'n':
760 				DEBUG(5, "NEW LINE\n", CNULL);
761 				c = '\n';
762 				break;
763 			case 'r':
764 				DEBUG(5, "RETURN\n", CNULL);
765 				c = '\r';
766 				break;
767 			case 'b':
768 				if (isdigit(*strptr)) {
769 					i = (*strptr++ - '0');
770 					if (i <= 0 || i > 10)
771 						i = 3;
772 				} else
773 					i = 3;
774 				/* send break */
775 				genbrk(fn, i);
776 				if (*strptr == '\0')
777 					cr = 0;
778 				continue;
779 			case 'c':
780 				if (*strptr == '\0') {
781 					DEBUG(5, "NO CR\n", CNULL);
782 					cr = 0;
783 				} else
784 					DEBUG(5, "NO CR - IGNORED NOT EOL\n", CNULL);
785 				continue;
786 #define isoctal(x)	((x >= '0') && (x <= '7'))
787 			default:
788 				if (isoctal(strptr[-1])) {
789 					i = 0;
790 					n = 0;
791 					--strptr;
792 					while (isoctal(*strptr) && ++n <= 3)
793 						i = i * 8 + (*strptr++ - '0');
794 					DEBUG(5, "\\%o\n", i);
795 					p_chwrite(fn, (char)i);
796 					continue;
797 				}
798 			}
799 		}
800 		p_chwrite(fn, c);
801 	}
802 
803 	if (cr)
804 		p_chwrite(fn, '\r');
805 	return;
806 }
807 
808 p_chwrite(fd, c)
809 int fd;
810 char c;
811 {
812 	c = par_tab[c&0177];
813 	if (write(fd, &c, 1) != 1) {
814 		logent(sys_errlist[errno], "BAD WRITE");
815 		longjmp(Cjbuf, 2);
816 	}
817 }
818 
819 /*
820  * generate parity table for use by p_chwrite.
821  */
822 bld_partab(type)
823 int type;
824 {
825 	register int i, j, n;
826 
827 	for (i = 0; i < sizeof(par_tab); i++) {
828 		n = 0;
829 		for (j = i&(sizeof(par_tab)-1); j; j = (j-1)&j)
830 			n++;
831 		par_tab[i] = i;
832 		if (type == P_ONE
833 		 || (type == P_EVEN && (n&01) != 0)
834 		 || (type == P_ODD && (n&01) == 0))
835 			par_tab[i] |= sizeof(par_tab);
836 	}
837 }
838 
839 /*
840  *	check for occurrence of substring "sh"
841  *
842  *	return codes:
843  *		0  -  found the string
844  *		1  -  not in the string
845  */
846 notin(sh, lg)
847 register char *sh, *lg;
848 {
849 	while (*lg != '\0') {
850 		if (wprefix(sh, lg))
851 			return 0;
852 		else
853 			lg++;
854 	}
855 	return 1;
856 }
857 
858 /*
859  *	Allow multiple date specifications separated by ','.
860  */
861 ifdate(p)
862 register char *p;
863 {
864 	register char *np;
865 	register int ret, g;
866 	int rtime, i;
867 
868 	/*  pick up retry time for failures  */
869 	/*  global variable Retrytime is set here  */
870 	if ((np = index(p, ';')) == NULL) {
871 		Retrytime = RETRYTIME;
872 	} else {
873 		i = sscanf(np+1, "%d", &rtime);
874 		if (i < 1 || rtime < 0)
875 			rtime = 5;
876 		Retrytime  = rtime * 60;
877 	}
878 
879 	ret = FAIL;
880 	MaxGrade = '\0';
881 	do {
882 		np = strpbrk(p, ",|");	/* prefer , but allow | for compat */
883 		if (np)
884 			*np = '\0';
885 		g = ifadate(p);
886 		DEBUG(11,"ifadate returns %o\n", g);
887 		if (g != FAIL) {
888 			ret = SUCCESS;
889 			if (g > MaxGrade)
890 				MaxGrade = g;
891 		}
892 		if (np)
893 			*np = ',';
894 		p = np + 1;
895 	} while (np);
896 	if (MaxGrade == '\0')
897 		MaxGrade = DefMaxGrade;
898 	return ret;
899 }
900 
901 /*
902  *	this routine will check a string (string)
903  *	like "MoTu0800-1730" to see if the present
904  *	time is within the given limits.
905  *	SIDE EFFECT - Retrytime is set
906  *
907  *	return codes:
908  *		0  -  not within limits
909  *		1  -  within limits
910  */
911 
912 ifadate(string)
913 char *string;
914 {
915 	static char *days[]={
916 		"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
917 	};
918 	time_t clock;
919 	register char *s = string;
920 	int i, tl, th, tn, dayok=0;
921 	struct tm *localtime();
922 	struct tm *tp;
923 	char *p, MGrade;
924 
925 	if ((p = index(s, '/')) == NULL)
926 		MGrade = DefMaxGrade;
927 	else
928 		MGrade = p[1];
929 
930 	time(&clock);
931 	tp = localtime(&clock);
932 	while (isascii(*s) && isalpha(*s)) {
933 		for (i = 0; days[i]; i++) {
934 			if (prefix(days[i], s))
935 				if (tp->tm_wday == i)
936 					dayok = 1;
937 		}
938 
939 		if (prefix("Wk", s))
940 			if (tp->tm_wday >= 1 && tp->tm_wday <= 5)
941 				dayok = 1;
942 		if (prefix("Any", s))
943 			dayok = 1;
944 		if (prefix("Evening", s)) {
945 			/* Sat or Sun */
946 			if (tp->tm_wday == 6 || tp->tm_wday == 0
947 				|| tp->tm_hour >= 17 || tp->tm_hour < 8)
948 					dayok = 1;
949 		}
950 		if (prefix("Night", s)) {
951 			if (tp->tm_wday == 6  /* Sat */
952 				|| tp->tm_hour >= 23 || tp->tm_hour < 8
953 					/* Sunday before 5pm */
954 				|| (tp->tm_wday == 0 && tp->tm_hour < 17))
955 					dayok = 1;
956 		}
957 		if (prefix("NonPeak", s)) { /* For Tymnet and PC Pursuit */
958 			/* Sat or Sun */
959 			if (tp->tm_wday == 6 || tp->tm_wday == 0
960 				|| tp->tm_hour >= 18 || tp->tm_hour < 7)
961 					dayok = 1;
962 		}
963 		s++;
964 	}
965 
966 	if (dayok == 0 && s != string)
967 		return FAIL;
968 	i = sscanf(s, "%d-%d", &tl, &th);
969   	if (i < 2)
970   		return MGrade;
971 	tn = tp->tm_hour * 100 + tp->tm_min;
972   	if (th < tl) { 		/* crosses midnight */
973   		if (tl <= tn || tn < th)
974   			return MGrade;
975   	} else {
976 		if (tl <= tn && tn < th)
977 			return MGrade;
978 	}
979 	return FAIL;
980 }
981 
982 /*
983  *	find first digit in string
984  *
985  *	return - pointer to first digit in string or end of string
986  */
987 char *
988 fdig(cp)
989 register char *cp;
990 {
991 	register char *c;
992 
993 	for (c = cp; *c; c++)
994 		if (*c >= '0' && *c <= '9')
995 			break;
996 	return c;
997 }
998 
999 /*
1000  * Compare strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
1001  * Strings are compared as if they contain all capital letters.
1002  */
1003 snccmp(s1, s2)
1004 register char *s1, *s2;
1005 {
1006 	char c1, c2;
1007 
1008 	if (islower(*s1))
1009 		c1 = toupper(*s1);
1010 	else
1011 		c1 = *s1;
1012 	if (islower(*s2))
1013 		c2 = toupper(*s2);
1014 	else
1015 		c2 = *s2;
1016 
1017 	while (c1 == c2) {
1018 		if (*s1++ == '\0')
1019 			return 0;
1020 		s2++;
1021 		if (islower(*s1))
1022 			c1 = toupper(*s1);
1023 		else
1024 			c1 = *s1;
1025 		if (islower(*s2))
1026 			c2 = toupper(*s2);
1027 		else
1028 			c2 = *s2;
1029 	}
1030 	return c1 - c2;
1031 }
1032 
1033 /*
1034  * Compare strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
1035  * Strings are compared as if they contain all capital letters.
1036  */
1037 sncncmp(s1, s2, n)
1038 register char *s1, *s2;
1039 register int n;
1040 {
1041 	char c1, c2;
1042 
1043 	if (islower(*s1))
1044 		c1 = toupper(*s1);
1045 	else
1046 		c1 = *s1;
1047 	if (islower(*s2))
1048 		c2 = toupper(*s2);
1049 	else
1050 		c2 = *s2;
1051 
1052 	while ( --n >= 0 && c1 == c2) {
1053 		if (*s1++ == '\0')
1054 			return 0;
1055 		s2++;
1056 		if (islower(*s1))
1057 			c1 = toupper(*s1);
1058 		else
1059 			c1 = *s1;
1060 		if (islower(*s2))
1061 			c2 = toupper(*s2);
1062 		else
1063 			c2 = *s2;
1064 	}
1065 	return n<0 ? 0 : (c1 - c2);
1066 }
1067 /*
1068  * do chat script
1069  * occurs after local port is opened,
1070  * before 'dialing' the other machine.
1071  */
1072 dochat(dev, flds, fd)
1073 register struct Devices *dev;
1074 char *flds[];
1075 int fd;
1076 {
1077 	register int i;
1078 	register char *p;
1079 	char bfr[sizeof(dev->D_argbfr)];
1080 
1081 	if (dev->D_numargs <= 5)
1082 		return(0);
1083 	DEBUG(4, "dochat called %d\n", dev->D_numargs);
1084 	for (i = 0; i < dev->D_numargs-5; i++) {
1085 		sprintf(bfr, dev->D_arg[D_CHAT+i], flds[F_PHONE]);
1086 		if (strcmp(bfr, dev->D_arg[D_CHAT+i])) {
1087 			p = malloc((unsigned)strlen(bfr)+1);
1088 			if (p != NULL) {
1089 				strcpy(p, bfr);
1090 				dev->D_arg[D_CHAT+i] = p;
1091 			}
1092 		}
1093 	}
1094 	/* following is a kludge because login() arglist is a kludge */
1095 	i = login(dev->D_numargs, &dev->D_arg[D_CHAT-5], fd);
1096 	/*
1097 	 * If login() last did a sendthem(), must pause so things can settle.
1098 	 * But don't bother if chat failed.
1099 	 */
1100 	if (i == 0 && (dev->D_numargs&01))
1101 		sleep(2);
1102 	return(i);
1103 }
1104 
1105 /*
1106  *	fix kill/echo/raw on line
1107  *
1108  *	return codes:  none
1109  */
1110 fixmode(tty)
1111 register int tty;
1112 {
1113 #ifdef	USG
1114 	struct termio ttbuf;
1115 #else	!USG
1116 	struct sgttyb ttbuf;
1117 #endif	!USG
1118 	register struct sg_spds *ps;
1119 	int speed;
1120 
1121 	if (IsTcpIp)
1122 		return;
1123 #ifdef	USG
1124 	ioctl(tty, TCGETA, &ttbuf);
1125 	ttbuf.c_iflag = ttbuf.c_oflag = ttbuf.c_lflag = (ushort)0;
1126 	speed = ttbuf.c_cflag &= (CBAUD);
1127 	ttbuf.c_cflag |= (CS8|CREAD);
1128 	ttbuf.c_cc[VMIN] = 6;
1129 	ttbuf.c_cc[VTIME] = 1;
1130 	ioctl(tty, TCSETA, &ttbuf);
1131 #else	!USG
1132 	ioctl(tty, TIOCGETP, &ttbuf);
1133 	ttbuf.sg_flags = (ANYP | RAW);
1134 	ioctl(tty, TIOCSETP, &ttbuf);
1135 	speed = ttbuf.sg_ispeed;
1136 	ioctl(tty, TIOCEXCL, STBNULL);
1137 #endif	!USG
1138 
1139 	for (ps = spds; ps->sp_val; ps++)
1140 		if (ps->sp_name == speed) {
1141 			linebaudrate = ps->sp_val;
1142 			DEBUG(9,"Incoming baudrate is %d\n", linebaudrate);
1143 			return;
1144 		}
1145 	ASSERT(linebaudrate >= 0, "BAD SPEED", CNULL, speed);
1146 }
1147 
1148