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