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