xref: /freebsd/usr.bin/chat/chat.c (revision 8e6b01171e30297084bb0b4457c4183c2746aacc)
1 /*
2  *	Chat -- a program for automatic session establishment (i.e. dial
3  *		the phone and log in).
4  *
5  *	This software is in the public domain.
6  *
7  *	Please send all bug reports, requests for information, etc. to:
8  *
9  *		Al Longyear (longyear@netcom.com)
10  *		(I was the last person to change this code.)
11  *
12  *	The original author is:
13  *
14  *		Karl Fox <karl@MorningStar.Com>
15  *		Morning Star Technologies, Inc.
16  *		1760 Zollinger Road
17  *		Columbus, OH  43221
18  *		(614)451-1883
19  */
20 
21 static char rcsid[] = "$Id: chat.c,v 1.2 1994/12/19 01:02:11 ache Exp $";
22 
23 #include <stdio.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <syslog.h>
32 
33 #ifndef TERMIO
34 #undef	TERMIOS
35 #define TERMIOS
36 #endif
37 
38 #ifdef sun
39 # if defined(SUNOS) && SUNOS >= 41
40 # ifndef HDB
41 #  define	HDB
42 # endif
43 # endif
44 #endif
45 
46 #ifdef TERMIO
47 #include <termio.h>
48 #endif
49 #ifdef TERMIOS
50 #include <termios.h>
51 #endif
52 
53 #define	STR_LEN	1024
54 
55 #ifndef SIGTYPE
56 #define SIGTYPE void
57 #endif
58 
59 #ifdef __STDC__
60 #undef __P
61 #define __P(x)	x
62 #else
63 #define __P(x)	()
64 #define const
65 #endif
66 
67 /*************** Micro getopt() *********************************************/
68 #define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
69 				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
70 				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
71 #define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
72 				(_O=4,(char*)0):(char*)0)
73 #define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
74 #define	ARG(c,v)	(c?(--c,*v++):(char*)0)
75 
76 static int _O = 0;		/* Internal state */
77 /*************** Micro getopt() *********************************************/
78 
79 char *program_name;
80 
81 #ifndef LOCK_DIR
82 # if defined(__NetBSD__) || defined(__FreeBSD__)
83 # define	PIDSTRING
84 # define	LOCK_DIR	"/var/spool/lock"
85 # else
86 #  ifdef HDB
87 #   define	PIDSTRING
88 #   define	LOCK_DIR	"/usr/spool/locks"
89 #  else /* HDB */
90 #   define	LOCK_DIR	"/usr/spool/uucp"
91 #  endif /* HDB */
92 # endif
93 #endif /* LOCK_DIR */
94 
95 #define	MAX_ABORTS		50
96 #define	DEFAULT_CHAT_TIMEOUT	45
97 
98 int verbose = 0;
99 int quiet = 0;
100 char *lock_file = (char *)0;
101 char *chat_file = (char *)0;
102 int timeout = DEFAULT_CHAT_TIMEOUT;
103 
104 int have_tty_parameters = 0;
105 #ifdef TERMIO
106 struct termio saved_tty_parameters;
107 #endif
108 #ifdef TERMIOS
109 struct termios saved_tty_parameters;
110 #endif
111 
112 char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
113 	fail_buffer[50];
114 int n_aborts = 0, abort_next = 0, timeout_next = 0;
115 
116 void *dup_mem __P((void *b, size_t c));
117 void *copy_of __P((char *s));
118 void usage __P((void));
119 void logf __P((const char *str));
120 void logflush __P((void));
121 void fatal __P((const char *msg));
122 void sysfatal __P((const char *msg));
123 SIGTYPE sigalrm __P((int signo));
124 SIGTYPE sigint __P((int signo));
125 SIGTYPE sigterm __P((int signo));
126 SIGTYPE sighup __P((int signo));
127 void unalarm __P((void));
128 void init __P((void));
129 void set_tty_parameters __P((void));
130 void break_sequence __P((void));
131 void terminate __P((int status));
132 void do_file __P((char *chat_file));
133 void lock __P((void));
134 void delay __P((void));
135 int  get_string __P((register char *string));
136 int  put_string __P((register char *s));
137 int  write_char __P((int c));
138 int  put_char __P((char c));
139 int  get_char __P((void));
140 void chat_send __P((register char *s));
141 char *character __P((char c));
142 void chat_expect __P((register char *s));
143 char *clean __P((register char *s, int sending));
144 void unlock __P((void));
145 void lock __P((void));
146 void break_sequence __P((void));
147 void terminate __P((int status));
148 void die __P((void));
149 
150 void *dup_mem(b, c)
151 void *b;
152 size_t c;
153     {
154     void *ans = malloc (c);
155     if (!ans)
156 	fatal ("memory error!\n");
157     memcpy (ans, b, c);
158     return ans;
159     }
160 
161 void *copy_of (s)
162 char *s;
163     {
164     return dup_mem (s, strlen (s) + 1);
165     }
166 
167 /*
168  *	chat [ -v ] [ -t timeout ] [ -l lock-file ] [ -f chat-file ] \
169  *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
170  *
171  *	Perform a UUCP-dialer-like chat script on stdin and stdout.
172  */
173 int
174 main(argc, argv)
175 int argc;
176 char **argv;
177     {
178     int option;
179     char *arg;
180 
181     program_name = *argv;
182 
183     while (option = OPTION(argc, argv))
184 	switch (option)
185 	    {
186 	    case 'v':
187 		++verbose;
188 		break;
189 
190 	    case 'f':
191 		if (arg = OPTARG(argc, argv))
192 		    chat_file = copy_of(arg);
193 		else
194 		    usage();
195 
196 		break;
197 
198 	    case 'l':
199 		if (arg = OPTARG(argc, argv))
200 		    lock_file = copy_of(arg);
201 		else
202 		    usage();
203 
204 		break;
205 
206 	    case 't':
207 		if (arg = OPTARG(argc, argv))
208 		    timeout = atoi(arg);
209 		else
210 		    usage();
211 
212 		break;
213 
214 	    default:
215 		usage();
216 	    }
217 
218 #ifdef ultrix
219     openlog("chat", LOG_PID);
220 #else
221     openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
222 
223     if (verbose) {
224 	setlogmask(LOG_UPTO(LOG_INFO));
225     } else {
226 	setlogmask(LOG_UPTO(LOG_WARNING));
227     }
228 #endif
229 
230     init();
231 
232     if (chat_file != NULL)
233 	{
234 	arg = ARG(argc, argv);
235 	if (arg != NULL)
236 	    usage();
237 	else
238 	    do_file (chat_file);
239 	}
240     else
241 	{
242 	while (arg = ARG(argc, argv))
243 	    {
244 	    chat_expect(arg);
245 
246 	    if (arg = ARG(argc, argv))
247 		chat_send(arg);
248 	    }
249 	}
250 
251     terminate(0);
252     }
253 
254 /*
255  *  Process a chat script when read from a file.
256  */
257 
258 void do_file (chat_file)
259 char *chat_file;
260     {
261     int linect, len, sendflg;
262     char *sp, *arg, quote;
263     char buf [STR_LEN];
264     FILE *cfp;
265 
266     if ((cfp = fopen (chat_file, "r")) == NULL)
267 	{
268 	syslog (LOG_ERR, "%s -- open failed: %m", chat_file);
269 	terminate (1);
270 	}
271 
272     linect = 0;
273     sendflg = 0;
274 
275     while (fgets(buf, STR_LEN, cfp) != NULL)
276 	{
277 	sp = strchr (buf, '\n');
278 	if (sp)
279 	    *sp = '\0';
280 
281 	linect++;
282 	sp = buf;
283 	while (*sp != '\0')
284 	    {
285 	    if (*sp == ' ' || *sp == '\t')
286 		{
287 		++sp;
288 		continue;
289 		}
290 
291 	    if (*sp == '"' || *sp == '\'')
292 		{
293 		quote = *sp++;
294 		arg = sp;
295 		while (*sp != quote)
296 		    {
297 		    if (*sp == '\0')
298 			{
299 			syslog (LOG_ERR, "unterminated quote (line %d)",
300 				linect);
301 			terminate (1);
302 			}
303 
304 		    if (*sp++ == '\\')
305 			if (*sp != '\0')
306 			    ++sp;
307 		    }
308 		}
309 	    else
310 		{
311 		arg = sp;
312 		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
313 		    ++sp;
314 		}
315 
316 	    if (*sp != '\0')
317 		*sp++ = '\0';
318 
319 	    if (sendflg)
320 		{
321 		chat_send (arg);
322 		}
323 	    else
324 		{
325 		chat_expect (arg);
326 		}
327 	    sendflg = !sendflg;
328 	    }
329 	}
330     fclose (cfp);
331     }
332 
333 /*
334  *	We got an error parsing the command line.
335  */
336 void usage()
337     {
338     fprintf(stderr, "\
339 Usage: %s [-v] [-l lock-file] [-t timeout] {-f chat-file || chat-script}\n",
340 	    program_name);
341     exit(1);
342     }
343 
344 char line[256];
345 char *p;
346 
347 void logf (str)
348 const char *str;
349     {
350     p = line + strlen(line);
351     strcat (p, str);
352 
353     if (str[strlen(str)-1] == '\n')
354 	{
355 	syslog (LOG_INFO, "%s", line);
356 	line[0] = 0;
357 	}
358     }
359 
360 void logflush()
361     {
362     if (line[0] != 0)
363 	{
364 	syslog(LOG_INFO, "%s", line);
365 	line[0] = 0;
366         }
367     }
368 
369 /*
370  *	Unlock and terminate with an error.
371  */
372 void die()
373     {
374     unlock();
375     terminate(1);
376     }
377 
378 /*
379  *	Print an error message and terminate.
380  */
381 
382 void fatal (msg)
383 const char *msg;
384     {
385     syslog(LOG_ERR, "%s", msg);
386     unlock();
387     terminate(1);
388     }
389 
390 /*
391  *	Print an error message along with the system error message and
392  *	terminate.
393  */
394 
395 void sysfatal (msg)
396 const char *msg;
397     {
398     syslog(LOG_ERR, "%s: %m", msg);
399     unlock();
400     terminate(1);
401     }
402 
403 int alarmed = 0;
404 
405 SIGTYPE sigalrm(signo)
406 int signo;
407     {
408     int flags;
409 
410     alarm(1);
411     alarmed = 1;		/* Reset alarm to avoid race window */
412     signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
413 
414     logflush();
415     if ((flags = fcntl(0, F_GETFL, 0)) == -1)
416 	sysfatal("Can't get file mode flags on stdin");
417     else
418 	if (fcntl(0, F_SETFL, flags | FNDELAY) == -1)
419 	    sysfatal("Can't set file mode flags on stdin");
420 
421     if (verbose)
422 	{
423 	syslog(LOG_INFO, "alarm");
424 	}
425     }
426 
427 void unalarm()
428     {
429     int flags;
430 
431     if ((flags = fcntl(0, F_GETFL, 0)) == -1)
432 	sysfatal("Can't get file mode flags on stdin");
433     else
434 	if (fcntl(0, F_SETFL, flags & ~FNDELAY) == -1)
435 	    sysfatal("Can't set file mode flags on stdin");
436     }
437 
438 SIGTYPE sigint(signo)
439 int signo;
440     {
441     fatal("SIGINT");
442     }
443 
444 SIGTYPE sigterm(signo)
445 int signo;
446     {
447     fatal("SIGTERM");
448     }
449 
450 SIGTYPE sighup(signo)
451 int signo;
452     {
453     fatal("SIGHUP");
454     }
455 
456 void init()
457     {
458     signal(SIGINT, sigint);
459     signal(SIGTERM, sigterm);
460     signal(SIGHUP, sighup);
461 
462     if (lock_file)
463 	lock();
464 
465     set_tty_parameters();
466     signal(SIGALRM, sigalrm);
467     alarm(0);
468     alarmed = 0;
469     }
470 
471 void set_tty_parameters()
472     {
473 #ifdef TERMIO
474     struct termio t;
475 
476     if (ioctl(0, TCGETA, &t) < 0)
477 	sysfatal("Can't get terminal parameters");
478 #endif
479 #ifdef TERMIOS
480     struct termios t;
481 
482     if (tcgetattr(0, &t) < 0)
483 	sysfatal("Can't get terminal parameters");
484 #endif
485 
486     saved_tty_parameters = t;
487     have_tty_parameters = 1;
488 
489     t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
490     t.c_oflag  = 0;
491     t.c_lflag  = 0;
492     t.c_cc[VERASE] = t.c_cc[VKILL] = 0;
493     t.c_cc[VMIN] = 1;
494     t.c_cc[VTIME] = 0;
495 
496 #ifdef TERMIO
497     if (ioctl(0, TCSETA, &t) < 0)
498 	sysfatal("Can't set terminal parameters");
499 #endif
500 #ifdef TERMIOS
501     if (tcsetattr(0, TCSANOW, &t) < 0)
502 	sysfatal("Can't set terminal parameters");
503 #endif
504     }
505 
506 void break_sequence()
507     {
508 #ifdef TERMIOS
509     tcsendbreak (0, 0);
510 #endif
511     }
512 
513 void terminate(status)
514 int status;
515     {
516     if (have_tty_parameters &&
517 #ifdef TERMIO
518         ioctl(0, TCSETA, &saved_tty_parameters) < 0
519 #endif
520 #ifdef TERMIOS
521 	tcsetattr(0, TCSANOW, &saved_tty_parameters) < 0
522 #endif
523 	) {
524 	syslog(LOG_ERR, "Can't restore terminal parameters: %m");
525 	unlock();
526 	exit(1);
527         }
528     exit(status);
529     }
530 
531 /*
532  *	Create a lock file for the named lock device
533  */
534 void lock()
535     {
536     int fd, pid;
537 # ifdef PIDSTRING
538     char hdb_lock_buffer[12];
539 # endif
540 
541     lock_file = strcat(strcat(strcpy(malloc(strlen(LOCK_DIR)
542 				       + 1 + strlen(lock_file) + 1),
543 				LOCK_DIR), "/"), lock_file);
544 
545     if ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0)
546 	{
547 	char *s = lock_file;
548 	lock_file = (char *)0;	/* Don't remove someone else's lock file! */
549 	syslog(LOG_ERR, "Can't get lock file '%s': %m", s);
550 	die();
551 	}
552 
553 # ifdef PIDSTRING
554     sprintf(hdb_lock_buffer, "%10d\n", getpid());
555     write(fd, hdb_lock_buffer, 11);
556 # else
557     pid = getpid();
558     write(fd, &pid, sizeof pid);
559 # endif
560 
561     close(fd);
562     }
563 
564 /*
565  *	Remove our lockfile
566  */
567 void unlock()
568     {
569     if (lock_file)
570 	{
571 	unlink(lock_file);
572 	lock_file = (char *)0;
573 	}
574     }
575 
576 /*
577  *	'Clean up' this string.
578  */
579 char *clean(s, sending)
580 register char *s;
581 int sending;
582     {
583     char temp[STR_LEN], cur_chr;
584     register char *s1;
585     int add_return = sending;
586 #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
587 
588     s1 = temp;
589     while (*s)
590 	{
591 	cur_chr = *s++;
592 	if (cur_chr == '^')
593 	    {
594 	    cur_chr = *s++;
595 	    if (cur_chr == '\0')
596 		{
597 		*s1++ = '^';
598 		break;
599 		}
600 	    cur_chr &= 0x1F;
601 	    if (cur_chr != 0)
602 		*s1++ = cur_chr;
603 	    continue;
604 	    }
605 
606 	if (cur_chr != '\\')
607 	    {
608 	    *s1++ = cur_chr;
609 	    continue;
610 	    }
611 
612 	cur_chr = *s++;
613 	if (cur_chr == '\0')
614 	    {
615 	    if (sending)
616 		{
617 		*s1++ = '\\';
618 		*s1++ = '\\';
619 		}
620 	    break;
621 	    }
622 
623 	switch (cur_chr)
624 	    {
625 	case 'b':
626 	    *s1++ = '\b';
627 	    break;
628 
629 	case 'c':
630 	    if (sending && *s == '\0')
631 		add_return = 0;
632 	    else
633 		*s1++ = cur_chr;
634 	    break;
635 
636 	case '\\':
637 	case 'K':
638 	case 'p':
639 	case 'd':
640 	    if (sending)
641 		*s1++ = '\\';
642 
643 	    *s1++ = cur_chr;
644 	    break;
645 
646 	case 'q':
647 	    quiet = ! quiet;
648 	    break;
649 
650 	case 'r':
651 	    *s1++ = '\r';
652 	    break;
653 
654 	case 'n':
655 	    *s1++ = '\n';
656 	    break;
657 
658 	case 's':
659 	    *s1++ = ' ';
660 	    break;
661 
662 	case 't':
663 	    *s1++ = '\t';
664 	    break;
665 
666 	case 'N':
667 	    if (sending)
668 		{
669 		*s1++ = '\\';
670 		*s1++ = '\0';
671 		}
672 	    else
673 		*s1++ = 'N';
674 	    break;
675 
676 	default:
677 	    if (isoctal (cur_chr))
678 		{
679 		cur_chr &= 0x07;
680 		if (isoctal (*s))
681 		    {
682 		    cur_chr <<= 3;
683 		    cur_chr |= *s++ - '0';
684 		    if (isoctal (*s))
685 			{
686 			cur_chr <<= 3;
687 			cur_chr |= *s++ - '0';
688 			}
689 		    }
690 
691 		if (cur_chr != 0 || sending)
692 		    {
693 		    if (sending && (cur_chr == '\\' || cur_chr == 0))
694 			*s1++ = '\\';
695 		    *s1++ = cur_chr;
696 		    }
697 		break;
698 		}
699 
700 	    if (sending)
701 		*s1++ = '\\';
702 	    *s1++ = cur_chr;
703 	    break;
704 	    }
705 	}
706 
707     if (add_return)
708 	*s1++ = '\r';
709 
710     *s1++ = '\0'; /* guarantee closure */
711     *s1++ = '\0'; /* terminate the string */
712     return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
713     }
714 
715 /*
716  * Process the expect string
717  */
718 void chat_expect(s)
719 register char *s;
720     {
721     if (strcmp(s, "ABORT") == 0)
722 	{
723 	++abort_next;
724 	return;
725 	}
726 
727     if (strcmp(s, "TIMEOUT") == 0)
728 	{
729 	++timeout_next;
730 	return;
731 	}
732 
733     while (*s)
734 	{
735 	register char *hyphen;
736 
737 	for (hyphen = s; *hyphen; ++hyphen)
738 	    if (*hyphen == '-')
739 		if (hyphen == s || hyphen[-1] != '\\')
740 		    break;
741 
742 	if (*hyphen == '-')
743 	    {
744 	    *hyphen = '\0';
745 
746 	    if (get_string(s))
747 		return;
748 	    else
749 		{
750 		s = hyphen + 1;
751 
752 		for (hyphen = s; *hyphen; ++hyphen)
753 		    if (*hyphen == '-')
754 			if (hyphen == s || hyphen[-1] != '\\')
755 			    break;
756 
757 		if (*hyphen == '-')
758 		    {
759 		    *hyphen = '\0';
760 
761 		    chat_send(s);
762 		    s = hyphen + 1;
763 		    }
764 		else
765 		    {
766 		    chat_send(s);
767 		    return;
768 		    }
769 		}
770 	    }
771 	else
772 	    if (get_string(s))
773 		return;
774 	    else
775 		{
776 		if (fail_reason)
777 		    syslog(LOG_INFO, "Failed (%s)", fail_reason);
778 		else
779 		    syslog(LOG_INFO, "Failed");
780 
781 		unlock();
782 		terminate(1);
783 		}
784 	}
785     }
786 
787 char *character(c)
788 char c;
789     {
790     static char string[10];
791     char *meta;
792 
793     meta = (c & 0x80) ? "M-" : "";
794     c &= 0x7F;
795 
796     if (c < 32)
797 	sprintf(string, "%s^%c", meta, (int)c + '@');
798     else
799 	if (c == 127)
800 	    sprintf(string, "%s^?", meta);
801 	else
802 	    sprintf(string, "%s%c", meta, c);
803 
804     return (string);
805     }
806 
807 /*
808  *  process the reply string
809  */
810 void chat_send (s)
811 register char *s;
812     {
813     if (abort_next)
814 	{
815 	char *s1;
816 
817 	abort_next = 0;
818 
819 	if (n_aborts >= MAX_ABORTS)
820 	    fatal("Too many ABORT strings");
821 
822 	s1 = clean(s, 0);
823 
824 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
825 	    {
826 	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
827 	    die();
828 	    }
829 
830 	abort_string[n_aborts++] = s1;
831 
832 	if (verbose)
833 	    {
834 	    logf("abort on (");
835 
836 	    for (s1 = s; *s1; ++s1)
837 		logf(character(*s1));
838 
839 	    logf(")\n");
840 	    }
841 	}
842     else
843 	if (timeout_next)
844 	    {
845 	    timeout_next = 0;
846 	    timeout = atoi(s);
847 
848 	    if (timeout <= 0)
849 		timeout = DEFAULT_CHAT_TIMEOUT;
850 
851 	    if (verbose)
852 		{
853 		syslog(LOG_INFO, "timeout set to %d seconds", timeout);
854 		}
855 	    }
856 	else
857 	    {
858 	    if (strcmp(s, "EOT") == 0)
859 		s = "^D\\c";
860 	    else
861 		if (strcmp(s, "BREAK") == 0)
862 		    s = "\\K\\c";
863 	    if ( ! put_string(s))
864 		{
865 		syslog(LOG_INFO, "Failed");
866 		unlock();
867 		terminate(1);
868 		}
869 	    }
870     }
871 
872 int get_char()
873     {
874     int status;
875     char c;
876 
877     status = read(0, &c, 1);
878 
879     switch (status)
880 	{
881 	case 1:
882 	    return ((int)c & 0x7F);
883 
884 	default:
885 	    syslog(LOG_WARNING, "warning: read() on stdin returned %d",
886 		   status);
887 
888 	case -1:
889 	    if ((status = fcntl(0, F_GETFL, 0)) == -1)
890 		sysfatal("Can't get file mode flags on stdin");
891 	    else
892 		if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
893 		    sysfatal("Can't set file mode flags on stdin");
894 
895 	    return (-1);
896 	}
897     }
898 
899 int put_char(c)
900 char c;
901     {
902     int status;
903 
904     delay();
905 
906     status = write(1, &c, 1);
907 
908     switch (status)
909 	{
910 	case 1:
911 	    return (0);
912 
913 	default:
914 	    syslog(LOG_WARNING, "warning: write() on stdout returned %d",
915 		   status);
916 
917 	case -1:
918 	    if ((status = fcntl(0, F_GETFL, 0)) == -1)
919 		sysfatal("Can't get file mode flags on stdin");
920 	    else
921 		if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
922 		    sysfatal("Can't set file mode flags on stdin");
923 
924 	    return (-1);
925 	}
926     }
927 
928 int write_char (c)
929 int c;
930     {
931     if (alarmed || put_char(c) < 0)
932 	{
933 	extern int errno;
934 
935 	alarm(0); alarmed = 0;
936 
937 	if (verbose)
938 	    {
939 	    if (errno == EINTR || errno == EWOULDBLOCK)
940 		syslog(LOG_INFO, " -- write timed out");
941 	    else
942 		syslog(LOG_INFO, " -- write failed: %m");
943 	    }
944 	return (0);
945 	}
946     return (1);
947     }
948 
949 int put_string (s)
950 register char *s;
951     {
952     s = clean(s, 1);
953 
954     if (verbose)
955 	{
956 	logf("send (");
957 
958 	if (quiet)
959 	    logf("??????");
960 	else
961 	    {
962 	    register char *s1 = s;
963 
964 	    for (s1 = s; *s1; ++s1)
965 		logf(character(*s1));
966 	    }
967 
968 	logf(")\n");
969 	}
970 
971     alarm(timeout); alarmed = 0;
972 
973     while (*s)
974 	{
975 	register char c = *s++;
976 
977 	if (c != '\\')
978 	    {
979 	    if (!write_char (c))
980 		return 0;
981 	    continue;
982 	    }
983 
984 	c = *s++;
985 	switch (c)
986 	    {
987 	case 'd':
988 	    sleep(1);
989 	    break;
990 
991 	case 'K':
992 	    break_sequence();
993 	    break;
994 
995 	case 'p':
996 	    usleep(10000); /* 1/100th of a second. */
997 	    break;
998 
999 	default:
1000 	    if (!write_char (c))
1001 		return 0;
1002 	    break;
1003 	    }
1004 	}
1005 
1006     alarm(0);
1007     alarmed = 0;
1008     return (1);
1009     }
1010 
1011 /*
1012  *	'Wait for' this string to appear on this file descriptor.
1013  */
1014 int get_string(string)
1015 register char *string;
1016     {
1017     char temp[STR_LEN];
1018     int c, printed = 0, len, minlen;
1019     register char *s = temp, *end = s + STR_LEN;
1020 
1021     fail_reason = (char *)0;
1022     string = clean(string, 0);
1023     len = strlen(string);
1024     minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1025 
1026     if (verbose)
1027 	{
1028 	register char *s1;
1029 
1030 	logf("expect (");
1031 
1032 	for (s1 = string; *s1; ++s1)
1033 	    logf(character(*s1));
1034 
1035 	logf(")\n");
1036 	}
1037 
1038     if (len > STR_LEN)
1039 	{
1040 	syslog(LOG_INFO, "expect string is too long");
1041 	return 0;
1042 	}
1043 
1044     if (len == 0)
1045 	{
1046 	if (verbose)
1047 	    {
1048 	    syslog(LOG_INFO, "got it");
1049 	    }
1050 
1051 	return (1);
1052 	}
1053 
1054     alarm(timeout); alarmed = 0;
1055 
1056     while ( ! alarmed && (c = get_char()) >= 0)
1057 	{
1058 	int n, abort_len;
1059 
1060 	if (verbose)
1061 	    {
1062 	    if (c == '\n')
1063 		logf("\n");
1064 	    else
1065 		logf(character(c));
1066 	    }
1067 
1068 	*s++ = c;
1069 
1070 	if (s - temp >= len &&
1071 	    c == string[len - 1] &&
1072 	    strncmp(s - len, string, len) == 0)
1073 	    {
1074 	    if (verbose)
1075 		{
1076 		logf(" -- got it\n");
1077 		}
1078 
1079 	    alarm(0); alarmed = 0;
1080 	    return (1);
1081 	    }
1082 
1083 	for (n = 0; n < n_aborts; ++n)
1084 	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1085 		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
1086 		{
1087 		if (verbose)
1088 		    {
1089 		    logf(" -- failed\n");
1090 		    }
1091 
1092 		alarm(0); alarmed = 0;
1093 		strcpy(fail_reason = fail_buffer, abort_string[n]);
1094 		return (0);
1095 		}
1096 
1097 	if (s >= end)
1098 	    {
1099 	    strncpy(temp, s - minlen, minlen);
1100 	    s = temp + minlen;
1101 	    }
1102 
1103 	if (alarmed && verbose)
1104 	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
1105 	}
1106 
1107     alarm(0);
1108 
1109     if (verbose && printed)
1110 	{
1111 	if (alarmed)
1112 	    logf(" -- read timed out\n");
1113 	else
1114 	    {
1115 	    logflush();
1116 	    syslog(LOG_INFO, " -- read failed: %m");
1117 	    }
1118 	}
1119 
1120     alarmed = 0;
1121     return (0);
1122     }
1123 
1124 #ifdef ultrix
1125 #undef NO_USLEEP
1126 #include <sys/types.h>
1127 #include <sys/time.h>
1128 
1129 /*
1130   usleep -- support routine for 4.2BSD system call emulations
1131   last edit:  29-Oct-1984     D A Gwyn
1132   */
1133 
1134 extern int	  select();
1135 
1136 int
1137 usleep( usec )				  /* returns 0 if ok, else -1 */
1138     long		usec;		/* delay in microseconds */
1139 {
1140     static struct			/* `timeval' */
1141 	{
1142 	    long	tv_sec;		/* seconds */
1143 	    long	tv_usec;	/* microsecs */
1144 	}   delay;	    /* _select() timeout */
1145 
1146     delay.tv_sec = usec / 1000000L;
1147     delay.tv_usec = usec % 1000000L;
1148 
1149     return select( 0, (long *)0, (long *)0, (long *)0, &delay );
1150 }
1151 #endif
1152 
1153 /*
1154  *	Delay an amount appropriate for between typed characters.
1155  */
1156 void delay()
1157     {
1158 # ifdef NO_USLEEP
1159     register int i;
1160 
1161     for (i = 0; i < 30000; ++i)		/* ... did we just say appropriate? */
1162 	;
1163 # else /* NO_USLEEP */
1164     usleep(100);
1165 # endif /* NO_USLEEP */
1166     }
1167