xref: /titanic_52/usr/src/lib/libnsl/dial/conn.c (revision 6451fdbca2f79129a3a09d2fe3f6dd4d062bebff)
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 (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
24 /*	  All Rights Reserved	*/
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include "uucp.h"
34 static char _ProtoSys[40];	/* protocol string from Systems file entry */
35 static char _ProtoDev[40];	/* protocol string from Devices file entry */
36 static char _ProtoCfg[];	/* protocol string from Config  file entry */
37 
38 static jmp_buf Sjbuf;
39 static unsigned expecttime;
40 
41 static int	Modemctrl;
42 
43 static void alarmtr(int);
44 static void getProto(char *, char *);
45 static int finds(char *, char *[], int);
46 static int getto(char *[]);	/* make this static when ct uses altconn() */
47 static int chat(int, char *[], int, char *, char *);
48 static int rddev(char *, char *[], char *, int);
49 static int expect(char *, int);
50 static int wrstr(int, char *, int, int);
51 static int wrchr(int, char *, int);
52 static int processdev(char *[], char *[]);
53 static int getdevline(char *, int);
54 static int getsysline(char *, int);
55 static int sysaccess(int);
56 static int clear_hup(int);
57 #ifndef SMALL
58 static char *currsys(void);
59 static char *currdev(void);
60 #endif
61 static int wait_for_hangup(int);
62 static int expect_str(char *, int);
63 
64 static void sendthem(char *, int, char *, char *);
65 static void nap(unsigned int);
66 static int notin(char *, char *);
67 static int ifdate(char *);
68 static int classmatch(char *[], char *[]);
69 
70 static char *Myline = CNULL;	/* to force which line will be used */
71 static char *Mytype = CNULL;	/* to force selection of specific device type */
72 
73 /*
74  * conn - place a telephone call to system and login, etc.
75  *
76  * return codes:
77  *	FAIL - connection failed
78  *	>0  - file no.  -  connect ok
79  * When a failure occurs, Uerror is set.
80  */
81 
82 static int
83 conn(char *system)
84 {
85 	int nf, fn = FAIL;
86 	char *flds[F_MAX+1];
87 	static void sysreset(void);
88 
89 	CDEBUG(4, "conn(%s)\n", system);
90 	Uerror = 0;
91 	while ((nf = finds(system, flds, F_MAX)) > 0) {
92 		fn = getto(flds);
93 		CDEBUG(4, "getto ret %d\n", fn);
94 		if (fn < 0)
95 			continue;
96 		if (EQUALS(Progname, "uucico")) {
97 			if (chat(nf - F_LOGIN, flds + F_LOGIN, fn, "", "") ==
98 								SUCCESS) {
99 				sysreset();
100 				return (fn); /* successful return */
101 			}
102 
103 			/* login failed */
104 			DEBUG(6, "close caller (%d)\n", fn);
105 			fd_rmlock(fn);
106 			(void) close(fn);
107 			if (Dc[0] != NULLCHAR) {
108 				/*EMPTY*/
109 				DEBUG(6, "delock line (%s)\n", Dc);
110 			}
111 		} else {
112 			sysreset();
113 			return (fn);
114 		}
115 	}
116 
117 	/* finds or getto failed */
118 	sysreset();
119 	CDEBUG(1, "Call Failed: %s\n", UERRORTEXT);
120 	return (FAIL);
121 }
122 
123 /*
124  * getto - connect to remote machine
125  *
126  * return codes:
127  *	>0  -  file number - ok
128  *	FAIL  -  failed
129  */
130 
131 static int
132 getto(char *flds[])
133 {
134 	char *dev[D_MAX+2], devbuf[BUFSIZ];
135 	int status;
136 	int dcf = -1;
137 	int reread = 0;
138 	int tries = 0;	/* count of call attempts - for limit purposes */
139 	static void devreset(void);
140 
141 	CDEBUG(1, "Device Type %s wanted\n", flds[F_TYPE]);
142 	Uerror = 0;
143 	while (tries < TRYCALLS) {
144 		if ((status = rddev(flds[F_TYPE], dev, devbuf, D_MAX)) ==
145 								FAIL) {
146 			if (tries == 0 || ++reread >= TRYCALLS)
147 				break;
148 			devreset();
149 			continue;
150 		}
151 		/* check class, check (and possibly set) speed */
152 		if (classmatch(flds, dev) != SUCCESS) {
153 			DEBUG(7, "Skipping entry in '%s'", currdev());
154 			DEBUG(7, " - class (%s) not wanted.\n", dev[D_CLASS]);
155 			continue;
156 		}
157 		DEBUG(5, "Trying device entry from '%s'.\n", currdev());
158 		if ((dcf = processdev(flds, dev)) >= 0)
159 			break;
160 
161 		switch (Uerror) {
162 		case SS_CANT_ACCESS_DEVICE:
163 		case SS_DEVICE_FAILED:
164 		case SS_LOCKED_DEVICE:
165 			break;
166 		default:
167 			tries++;
168 			break;
169 		}
170 	}
171 	devreset();	/* reset devices file(s) */
172 	if (status == FAIL && !Uerror) {
173 		CDEBUG(1, "Requested Device Type Not Found\n%s", "");
174 		Uerror = SS_NO_DEVICE;
175 	}
176 	return (dcf);
177 }
178 
179 /*
180  * classmatch - process 'Any' in Devices and Systems and
181  *	determine the correct speed, or match for ==
182  */
183 
184 static int
185 classmatch(char *flds[], char *dev[])
186 {
187 	/* check class, check (and possibly set) speed */
188 	if (EQUALS(flds[F_CLASS], "Any") && EQUALS(dev[D_CLASS], "Any")) {
189 		dev[D_CLASS] = DEFAULT_BAUDRATE;
190 		return (SUCCESS);
191 	} else if (EQUALS(dev[D_CLASS], "Any")) {
192 		dev[D_CLASS] = flds[F_CLASS];
193 		return (SUCCESS);
194 	} else if (EQUALS(flds[F_CLASS], "Any") ||
195 					EQUALS(flds[F_CLASS], dev[D_CLASS]))
196 		return (SUCCESS);
197 	else
198 		return (FAIL);
199 }
200 
201 
202 /*
203  *	rddev - find and unpack a line from device file for this caller type
204  *	lines starting with whitespace of '#' are comments
205  *
206  *	return codes:
207  *		>0  -  number of arguments in vector - succeeded
208  *		FAIL - EOF
209  */
210 
211 static int
212 rddev(char *type, char *dev[], char *buf, int devcount)
213 {
214 	char *commap, d_type[BUFSIZ];
215 	int na;
216 
217 	while (getdevline(buf, BUFSIZ)) {
218 		if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' ||
219 					buf[0] == '\0' || buf[0] == '#')
220 			continue;
221 		na = getargs(buf, dev, devcount);
222 		ASSERT(na >= D_CALLER, "BAD LINE", buf, na);
223 
224 		if (strncmp(dev[D_LINE], "/dev/", 5) == 0) {
225 			/* since cu (altconn()) strips off leading */
226 			/* "/dev/",  do the same here.  */
227 			(void) strcpy(dev[D_LINE], &(dev[D_LINE][5]));
228 		}
229 
230 		/* may have ",M" subfield in D_LINE */
231 		Modemctrl = FALSE;
232 		if ((commap = strchr(dev[D_LINE], ',')) != NULL) {
233 			if (strcmp(commap, ",M") == SAME)
234 				Modemctrl = TRUE;
235 			*commap = '\0';
236 		}
237 
238 		/*
239 		 * D_TYPE field may have protocol subfield, which
240 		 * must be pulled off before comparing to desired type.
241 		 */
242 		(void) strcpy(d_type, dev[D_TYPE]);
243 		if ((commap = strchr(d_type, ',')) != NULL)
244 			*commap = '\0';
245 
246 		/* to force the requested device type to be used. */
247 		if ((Mytype != NULL) && (!EQUALS(Mytype, d_type)))
248 			continue;
249 		/* to force the requested line to be used */
250 		if ((Myline != NULL) && (!EQUALS(Myline, dev[D_LINE])))
251 			continue;
252 
253 		bsfix(dev);	/* replace \X fields */
254 
255 		if (EQUALS(d_type, type)) {
256 			getProto(_ProtoDev, dev[D_TYPE]);
257 			return (na);
258 		}
259 	}
260 	return (FAIL);
261 }
262 
263 
264 /*
265  * finds	- set system attribute vector
266  *
267  * input:
268  *	fsys - open Systems file descriptor
269  *	sysnam - system name to find
270  * output:
271  *	flds - attibute vector from Systems file
272  *	fldcount - number of fields in flds
273  * return codes:
274  *	>0  -  number of arguments in vector - succeeded
275  *	FAIL - failed
276  * Uerror set:
277  *	0 - found a line in Systems file
278  *	SS_BADSYSTEM - no line found in Systems file
279  *	SS_TIME_WRONG - wrong time to call
280  */
281 
282 static int
283 finds(char *sysnam, char *flds[], int fldcount)
284 {
285 	static char *info;	/* dynamically allocated BUFSIZ */
286 	int na;
287 
288 	/*
289 	 * format of fields
290 	 *	0 name;
291 	 *	1 time
292 	 *	2 acu/hardwired
293 	 *	3 speed
294 	 *	etc
295 	 */
296 
297 	if (sysnam == 0 || *sysnam == 0) {
298 		Uerror = SS_BADSYSTEM;
299 		return (FAIL);
300 	}
301 
302 	if (info == NULL) {
303 		info = malloc(BUFSIZ);
304 		if (info == NULL) {
305 			DEBUG(1, "malloc failed for info in finds\n", 0);
306 			return (0);
307 		}
308 	}
309 	while (getsysline(info, BUFSIZ)) {
310 		na = getargs(info, flds, fldcount);
311 		bsfix(flds);	/* replace \X fields */
312 		if (!EQUALSN(sysnam, flds[F_NAME], MAXBASENAME))
313 			continue;
314 		/* check if requested Mytype device type */
315 		if ((Mytype != CNULL) &&
316 			    (!EQUALSN(flds[F_TYPE], Mytype, strlen(Mytype)))) {
317 			DEBUG(7, "Skipping entry in '%s'", currsys());
318 			DEBUG(7, " - type (%s) not wanted.\n", flds[F_TYPE]);
319 			continue;
320 		} else {
321 			/*EMPTY*/
322 			DEBUG(5, "Trying entry from '%s'", currsys());
323 			DEBUG(5, " - device type %s.\n", flds[F_TYPE]);
324 		}
325 		/* OK if not uucico (ie. ct or cu) or the time is right */
326 		if (!EQUALS(Progname, "uucico") || ifdate(flds[F_TIME])) {
327 			/*  found a good entry  */
328 			getProto(_ProtoSys, flds[F_TYPE]);
329 			Uerror = 0;
330 			return (na);	/* FOUND OK LINE */
331 		}
332 		CDEBUG(1, "Wrong Time To Call: %s\n", flds[F_TIME]);
333 		Uerror = SS_TIME_WRONG;
334 	}
335 	if (!Uerror)
336 		Uerror = SS_BADSYSTEM;
337 	return (FAIL);
338 }
339 
340 /*
341  * getProto - get the protocol letters from the input string.
342  * input:
343  *	str - string from Systems/Devices/Config file,
344  *		a ',' delimits the protocol string
345  *		e.g. ACU,g or DK,d
346  * output:
347  *	str - the , (if present) will be replaced with NULLCHAR
348  *
349  * return:  none
350  */
351 
352 static void
353 getProto(char *save, char *str)
354 {
355 	char *p;
356 
357 	*save = NULLCHAR;
358 	if ((p = strchr(str, ',')) != NULL) {
359 		*p = NULLCHAR;
360 		(void) strcpy(save, p+1);
361 		DEBUG(7, "Protocol = %s\n", save);
362 	}
363 }
364 
365 /*
366  * chat -	do conversation
367  * input:
368  *	nf - number of fields in flds array
369  *	flds - fields from Systems file
370  *	fn - write file number
371  *	phstr1 - phone number to replace \D
372  *	phstr2 - phone number to replace \T
373  *
374  *	return codes:  0  |  FAIL
375  */
376 
377 static int
378 chat(int nf, char *flds[], int fn, char *phstr1, char *phstr2)
379 {
380 	char *want, *altern;
381 	int k, ok;
382 
383 	for (k = 0; k < nf; k += 2) {
384 		want = flds[k];
385 		ok = FAIL;
386 		while (ok != 0) {
387 			altern = index(want, '-');
388 			if (altern != NULL)
389 				*altern++ = NULLCHAR;
390 			ok = expect(want, fn);
391 			if (ok == 0)
392 				break;
393 			if (altern == NULL) {
394 				Uerror = SS_LOGIN_FAILED;
395 				logent(UERRORTEXT, "FAILED");
396 				return (FAIL);
397 			}
398 			want = index(altern, '-');
399 			if (want != NULL)
400 				*want++ = NULLCHAR;
401 			sendthem(altern, fn, phstr1, phstr2);
402 		}
403 		(void) sleep(2);
404 		if (flds[k+1])
405 			sendthem(flds[k+1], fn, phstr1, phstr2);
406 	}
407 	return (0);
408 }
409 
410 #define	MR 1000
411 
412 /*
413  *	expect(str, fn)	look for expected string w/ possible special chars
414  *	char *str;
415  *
416  *	return codes:
417  *		0  -  found
418  *		FAIL  -  lost line or too many characters read
419  *		some character  -  timed out
420  */
421 
422 static int
423 expect(char *str, int fn)
424 {
425 	char *bptr, *sptr;
426 	char    buf[BUFSIZ];
427 
428 	bptr = buf;
429 
430 	for (sptr = str; *sptr; sptr++) {
431 		if (*sptr == '\\') {
432 			switch (*++sptr) {
433 			case 'H':
434 				*bptr++ = '\0';
435 				if (expect_str(buf, fn) == FAIL) {
436 					return (FAIL);
437 				}
438 				if (wait_for_hangup(fn) == FAIL) {
439 					return (FAIL);
440 				}
441 				bptr = buf;
442 				continue;
443 			case '\\':
444 				*bptr++ = '\\';
445 				continue;
446 			default:
447 				*bptr++ = '\\';
448 				*bptr++ = *sptr;
449 				continue;
450 			}
451 		} else
452 			*bptr++ = *sptr;
453 	}
454 	*bptr = '\0';
455 	if (expect_str(buf, fn) == FAIL) {
456 		return (FAIL);
457 	}
458 	return (0);
459 }
460 
461 /*
462  *	expect_str(str, fn)	look for expected string, w/ no special chars
463  *
464  *	return codes:
465  *		0  -  found
466  *		FAIL  -  too many characters read
467  *		some character  -  timed out
468  */
469 
470 static int
471 expect_str(char *str, int fn)
472 {
473 	static char rdvec[MR];
474 	char *rp = rdvec;
475 	int kr, c;
476 	char nextch;
477 
478 	*rp = 0;
479 
480 	CDEBUG(4, "expect: (%s", "");
481 	for (c = 0; (kr = str[c]) != 0; c++)
482 		if (kr < 040) {
483 			/*EMPTY*/
484 			CDEBUG(4, "^%c", kr | 0100);
485 		} else {
486 			/*EMPTY*/
487 			CDEBUG(4, "%c", kr);
488 		}
489 	CDEBUG(4, ")\n%s", "");
490 
491 	if (EQUALS(str, "\"\"")) {
492 		CDEBUG(4, "got it\n%s", "");
493 		return (0);
494 	}
495 	if (*str == '\0')
496 		return (0);
497 	if (setjmp(Sjbuf))
498 		return (FAIL);
499 	(void) signal(SIGALRM, alarmtr);
500 	(void) alarm(expecttime);
501 	while (notin(str, rdvec)) {
502 		errno = 0;
503 		kr = (*Read)(fn, &nextch, 1);
504 		if (kr <= 0) {
505 			(void) alarm(0);
506 			CDEBUG(4, "lost line errno - %d\n", errno);
507 			logent("LOGIN", "LOST LINE");
508 			return (FAIL);
509 		}
510 		c = nextch & 0177;
511 		CDEBUG(4, "%s", c < 040 ? "^" : "");
512 		CDEBUG(4, "%c", c < 040 ? c | 0100 : c);
513 		if ((*rp = nextch & 0177) != NULLCHAR)
514 			rp++;
515 		if (rp >= rdvec + MR) {
516 			CDEBUG(4, "enough already\n%s", "");
517 			(void) alarm(0);
518 			return (FAIL);
519 		}
520 		*rp = NULLCHAR;
521 	}
522 	(void) alarm(0);
523 	CDEBUG(4, "got it\n%s", "");
524 	return (0);
525 }
526 
527 
528 /*
529  *	alarmtr()  -  catch alarm routine for "expect".
530  */
531 /*ARGSUSED*/
532 static void
533 alarmtr(int sig)
534 {
535 	CDEBUG(6, "timed out\n%s", "");
536 	longjmp(Sjbuf, 1);
537 }
538 
539 /*
540  *	wait_for_hangup() - wait for a hangup to occur on the given device
541  */
542 int
543 wait_for_hangup(int dcf)
544 {
545 	int rval;
546 	char buff[BUFSIZ];
547 
548 	CDEBUG(4, "Waiting for hangup\n%s", "");
549 	while ((rval = read(dcf, buff, BUFSIZ)) > 0)
550 		;
551 
552 	if (rval < 0)
553 		return (FAIL);
554 	CDEBUG(4, "Received hangup\n%s", "");
555 
556 	if (clear_hup(dcf) != SUCCESS) {
557 	    CDEBUG(4, "Unable to clear hup on device\n%s", "");
558 	    return (FAIL);
559 	}
560 	return (SUCCESS);
561 }
562 
563 /*
564  *	sendthem(str, fn, phstr1, phstr2)	send line of chat sequence
565  *	char *str, *phstr;
566  *
567  *	return codes:  none
568  */
569 
570 #define	FLUSH() { \
571 	if ((bptr - buf) > 0) \
572 		if (wrstr(fn, buf, bptr - buf, echocheck) != SUCCESS) \
573 			goto err; \
574 	bptr = buf; \
575 }
576 
577 static void
578 sendthem(char *str, int fn, char *phstr1, char *phstr2)
579 {
580 	int sendcr = 1, echocheck = 0;
581 	char	*sptr, *bptr;
582 	char	buf[BUFSIZ];
583 	struct termio	ttybuf;
584 
585 	/* should be EQUALS, but previous versions had BREAK n for integer n */
586 
587 	if (PREFIX("BREAK", str)) {
588 		/* send break */
589 		CDEBUG(5, "BREAK\n%s", "");
590 		(*genbrk)(fn);
591 		return;
592 	}
593 
594 	if (EQUALS(str, "EOT")) {
595 		CDEBUG(5, "EOT\n%s", "");
596 		(void) (*Write)(fn, EOTMSG, strlen(EOTMSG));
597 		return;
598 	}
599 
600 	if (EQUALS(str, "\"\"")) {
601 		CDEBUG(5, "\"\"\n%s", "");
602 		str += 2;
603 	}
604 
605 	bptr = buf;
606 	CDEBUG(5, "sendthem (%s", "");
607 	for (sptr = str; *sptr; sptr++) {
608 		if (*sptr == '\\') {
609 			switch (*++sptr) {
610 
611 			/* adjust switches */
612 			case 'c':	/* no CR after string */
613 				FLUSH();
614 				if (sptr[1] == NULLCHAR) {
615 					CDEBUG(5, "<NO CR>%s", "");
616 					sendcr = 0;
617 				} else {
618 					/*EMPTY*/
619 					CDEBUG(5, "<NO CR IGNORED>\n%s", "");
620 				}
621 				continue;
622 			}
623 
624 			/* stash in buf and continue */
625 			switch (*sptr) {
626 			case 'D':	/* raw phnum */
627 				(void) strcpy(bptr, phstr1);
628 				bptr += strlen(bptr);
629 				continue;
630 			case 'T':	/* translated phnum */
631 				(void) strcpy(bptr, phstr2);
632 				bptr += strlen(bptr);
633 				continue;
634 			case 'N':	/* null */
635 				*bptr++ = 0;
636 				continue;
637 			case 's':	/* space */
638 				*bptr++ = ' ';
639 				continue;
640 			case '\\':	/* backslash escapes itself */
641 				*bptr++ = *sptr;
642 				continue;
643 			default:	/* send the backslash */
644 				*bptr++ = '\\';
645 				*bptr++ = *sptr;
646 				continue;
647 
648 			/* flush buf, perform action, and continue */
649 			case 'E':	/* echo check on */
650 				FLUSH();
651 				CDEBUG(5, "ECHO CHECK ON\n%s", "");
652 				echocheck = 1;
653 				continue;
654 			case 'e':	/* echo check off */
655 				FLUSH();
656 				CDEBUG(5, "ECHO CHECK OFF\n%s", "");
657 				echocheck = 0;
658 				continue;
659 			case 'd':	/* sleep briefly */
660 				FLUSH();
661 				CDEBUG(5, "DELAY\n%s", "");
662 				(void) sleep(2);
663 				continue;
664 			case 'p':	/* pause momentarily */
665 				FLUSH();
666 				CDEBUG(5, "PAUSE\n%s", "");
667 				nap(HZ/4);	/* approximately 1/4 second */
668 				continue;
669 			case 'K':	/* inline break */
670 				FLUSH();
671 				CDEBUG(5, "BREAK\n%s", "");
672 				(*genbrk)(fn);
673 				continue;
674 			case 'M':	/* modem control - set CLOCAL */
675 			case 'm':	/* no modem control - clear CLOCAL */
676 				FLUSH();
677 				CDEBUG(5, ")\n%s CLOCAL ",
678 					(*sptr == 'M' ? "set" : "clear"));
679 				if ((*Ioctl)(fn, TCGETA, &ttybuf) != 0) {
680 					/*EMPTY*/
681 					CDEBUG(5,
682 					    "ignored. TCGETA failed, errno %d",
683 					    errno);
684 				} else {
685 					if (*sptr == 'M')
686 					ttybuf.c_cflag |= CLOCAL;
687 					else
688 					ttybuf.c_cflag &= ~CLOCAL;
689 					if ((*Ioctl)(fn, TCSETAW, &ttybuf) != 0)
690 						/*EMPTY*/
691 					CDEBUG(5,
692 					    "failed. TCSETAW failed, errno %d",
693 					    errno);
694 				}
695 				CDEBUG(5, "\n%s", "");
696 				continue;
697 			}
698 		} else
699 			*bptr++ = *sptr;
700 	}
701 	if (sendcr)
702 		*bptr++ = '\r';
703 	if ((bptr - buf) > 0)
704 		(void) wrstr(fn, buf, bptr - buf, echocheck);
705 
706 err:
707 	CDEBUG(5, ")\n%s", "");
708 }
709 
710 #undef FLUSH
711 
712 static int
713 wrstr(int fn, char *buf, int len, int echocheck)
714 {
715 	int	i;
716 	char dbuf[BUFSIZ], *dbptr = dbuf;
717 
718 	buf[len] = 0;
719 
720 	if (echocheck)
721 		return (wrchr(fn, buf, len));
722 
723 	if (Debug >= 5) {
724 		if (sysaccess(ACCESS_SYSTEMS) == 0) {
725 			/* Systems file access ok */
726 			for (i = 0; i < len; i++) {
727 				*dbptr = buf[i];
728 				if (*dbptr < 040) {
729 					*dbptr++ = '^';
730 					*dbptr = buf[i] | 0100;
731 				}
732 				dbptr++;
733 			}
734 			*dbptr = 0;
735 		} else
736 			(void) strcpy(dbuf, "????????");
737 		CDEBUG(5, "%s", dbuf);
738 	}
739 	if ((*Write)(fn, buf, len) != len)
740 		return (FAIL);
741 	return (SUCCESS);
742 }
743 
744 static int
745 wrchr(int fn, char *buf, int len)
746 {
747 	int	i, saccess;
748 	char	cin, cout;
749 
750 	saccess = (sysaccess(ACCESS_SYSTEMS) == 0); /* protect Systems file */
751 	if (setjmp(Sjbuf))
752 		return (FAIL);
753 	(void) signal(SIGALRM, alarmtr);
754 
755 	for (i = 0; i < len; i++) {
756 		cout = buf[i];
757 		if (saccess) {
758 			/*EMPTY*/
759 			CDEBUG(5, "%s", cout < 040 ? "^" : "");
760 			CDEBUG(5, "%c", cout < 040 ? cout | 0100 : cout);
761 		} else {
762 			/*EMPTY*/
763 			CDEBUG(5, "?%s", "");
764 		}
765 		if (((*Write)(fn, &cout, 1)) != 1)
766 			return (FAIL);
767 		do {
768 			(void) alarm(expecttime);
769 			if ((*Read)(fn, &cin, 1) != 1)
770 				return (FAIL);
771 			(void) alarm(0);
772 			cin &= 0177;
773 			if (saccess) {
774 				/*EMPTY*/
775 				CDEBUG(5, "%s", cin < 040 ? "^" : "");
776 				CDEBUG(5, "%c", cin < 040 ? cin | 0100 : cin);
777 			} else {
778 				/*EMPTY*/
779 				CDEBUG(5, "?%s", "");
780 			}
781 		} while (cout != (cin & 0177));
782 	}
783 	return (SUCCESS);
784 }
785 
786 
787 /*
788  *	notin(sh, lg)	check for occurrence of substring "sh"
789  *	char *sh, *lg;
790  *
791  *	return codes:
792  *		0  -  found the string
793  *		1  -  not in the string
794  */
795 
796 static int
797 notin(char *sh, char *lg)
798 {
799 	while (*lg != NULLCHAR) {
800 		if (PREFIX(sh, lg))
801 			return (0);
802 		else
803 			lg++;
804 	}
805 	return (1);
806 }
807 
808 
809 /*
810  *	ifdate(s)
811  *	char *s;
812  *
813  *	ifdate  -  this routine will check a string (s)
814  *	like "MoTu0800-1730" to see if the present
815  *	time is within the given limits.
816  *	SIDE EFFECT - Retrytime is set to number following ";"
817  *
818  *	String alternatives:
819  *		Wk - Mo thru Fr
820  *		zero or one time means all day
821  *		Any - any day
822  *
823  *	return codes:
824  *		0  -  not within limits
825  *		1  -  within limits
826  */
827 
828 static int
829 ifdate(char *s)
830 {
831 	static char *days[] = {
832 		"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
833 	};
834 	time_t	clock;
835 	int	t__now;
836 	char	*p;
837 	struct tm	*tp;
838 
839 	(void) time(&clock);
840 	tp = localtime(&clock);
841 	t__now = tp->tm_hour * 100 + tp->tm_min;	/* "navy" time */
842 
843 	/*
844 	 *	pick up retry time for failures
845 	 *	global variable Retrytime is set here
846 	 */
847 	if ((p = rindex(s, ';')) != NULL)
848 		if (isdigit(p[1])) {
849 		if (sscanf(p+1, "%ld", &Retrytime) < 1)
850 			Retrytime = 5;	/* 5 minutes is error default */
851 		Retrytime  *= 60;
852 		*p = NULLCHAR;
853 		}
854 
855 	while (*s) {
856 		int	i, dayok;
857 
858 		for (dayok = 0; (!dayok) && isalpha(*s); s++) {
859 			if (PREFIX("Any", s))
860 				dayok = 1;
861 			else if (PREFIX("Wk", s)) {
862 				if (tp->tm_wday >= 1 && tp->tm_wday <= 5)
863 					dayok = 1;
864 			} else
865 				for (i = 0; days[i]; i++)
866 					if (PREFIX(days[i], s))
867 						if (tp->tm_wday == i)
868 							dayok = 1;
869 		}
870 
871 		if (dayok) {
872 			int	t__low, t__high;
873 
874 			while (isalpha(*s))	/* flush remaining day stuff */
875 				s++;
876 
877 			if ((sscanf(s, "%d-%d", &t__low, &t__high) < 2) ||
878 							(t__low == t__high))
879 				return (1);
880 
881 			/* 0000 crossover? */
882 			if (t__low < t__high) {
883 				if (t__low <= t__now && t__now <= t__high)
884 					return (1);
885 			} else if (t__low <= t__now || t__now <= t__high)
886 				return (1);
887 
888 			/* aim at next time slot */
889 			if ((s = index(s, ',')) == NULL)
890 				break;
891 		}
892 		if (*s)
893 			s++;
894 	}
895 	return (0);
896 }
897 
898 /*
899  *	char *
900  *	fdig(cp)	find first digit in string
901  *
902  *	return - pointer to first digit in string or end of string
903  */
904 
905 static char *
906 fdig(char *cp)
907 {
908 	char *c;
909 
910 	for (c = cp; *c; c++)
911 		if (*c >= '0' && *c <= '9')
912 			break;
913 	return (c);
914 }
915 
916 	/* nap(n) -- sleep for 'n' ticks of 1/60th sec each. */
917 	/* This version uses the select system call */
918 
919 
920 static void
921 nap(unsigned int n)
922 {
923 	struct timeval tv;
924 
925 	if (n == 0)
926 		return;
927 	tv.tv_sec = n/60;
928 	tv.tv_usec = ((n%60)*1000000L)/60;
929 	(void) select(32, 0, 0, 0, &tv);
930 }
931