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