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