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