xref: /freebsd/usr.bin/chat/chat.c (revision 952d112864d8008aa87278a30a539d888a8493cd)
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.6 1997/02/22 19:54:23 peter 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 	have_tty_parameters = 0;
546 	return;
547         }
548 
549     saved_tty_parameters = t;
550     have_tty_parameters  = 1;
551 
552     t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
553     t.c_oflag      = 0;
554     t.c_lflag      = 0;
555     t.c_cc[VERASE] =
556     t.c_cc[VKILL]  = 0;
557     t.c_cc[VMIN]   = 1;
558     t.c_cc[VTIME]  = 0;
559 
560     if (set_term_param (&t) < 0)
561         {
562 	sysfatal("Can't set terminal parameters");
563         }
564 #endif
565     }
566 
567 void break_sequence()
568     {
569 #ifdef TERMIOS
570     tcsendbreak (0, 0);
571 #endif
572     }
573 
574 void terminate(status)
575 int status;
576     {
577     if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
578         {
579 	if (verbose)
580 	    {
581 	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
582 	    }
583 	fclose (report_fp);
584 	report_fp = (FILE*) NULL;
585         }
586 
587 #if defined(get_term_param)
588     if (have_tty_parameters)
589         {
590 	if (set_term_param (&saved_tty_parameters) < 0)
591 	    {
592 	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
593 	    exit(1);
594 	    }
595         }
596 #endif
597 
598     exit(status);
599     }
600 
601 /*
602  *	'Clean up' this string.
603  */
604 char *clean(s, sending)
605 register char *s;
606 int sending;
607     {
608     char temp[STR_LEN], cur_chr;
609     register char *s1;
610     int add_return = sending;
611 #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
612 
613     s1 = temp;
614     while (*s)
615 	{
616 	cur_chr = *s++;
617 	if (cur_chr == '^')
618 	    {
619 	    cur_chr = *s++;
620 	    if (cur_chr == '\0')
621 		{
622 		*s1++ = '^';
623 		break;
624 		}
625 	    cur_chr &= 0x1F;
626 	    if (cur_chr != 0)
627 	        {
628 		*s1++ = cur_chr;
629 	        }
630 	    continue;
631 	    }
632 
633 	if (cur_chr != '\\')
634 	    {
635 	    *s1++ = cur_chr;
636 	    continue;
637 	    }
638 
639 	cur_chr = *s++;
640 	if (cur_chr == '\0')
641 	    {
642 	    if (sending)
643 		{
644 		*s1++ = '\\';
645 		*s1++ = '\\';
646 		}
647 	    break;
648 	    }
649 
650 	switch (cur_chr)
651 	    {
652 	case 'b':
653 	    *s1++ = '\b';
654 	    break;
655 
656 	case 'c':
657 	    if (sending && *s == '\0')
658 	        {
659 		add_return = 0;
660 	        }
661 	    else
662 	        {
663 		*s1++ = cur_chr;
664 	        }
665 	    break;
666 
667 	case '\\':
668 	case 'K':
669 	case 'p':
670 	case 'd':
671 	    if (sending)
672 	        {
673 		*s1++ = '\\';
674 	        }
675 
676 	    *s1++ = cur_chr;
677 	    break;
678 
679 	case 'q':
680 	    quiet = ! quiet;
681 	    break;
682 
683 	case 'r':
684 	    *s1++ = '\r';
685 	    break;
686 
687 	case 'n':
688 	    *s1++ = '\n';
689 	    break;
690 
691 	case 's':
692 	    *s1++ = ' ';
693 	    break;
694 
695 	case 't':
696 	    *s1++ = '\t';
697 	    break;
698 
699 	case 'N':
700 	    if (sending)
701 		{
702 		*s1++ = '\\';
703 		*s1++ = '\0';
704 		}
705 	    else
706 	        {
707 		*s1++ = 'N';
708 	        }
709 	    break;
710 
711 	default:
712 	    if (isoctal (cur_chr))
713 		{
714 		cur_chr &= 0x07;
715 		if (isoctal (*s))
716 		    {
717 		    cur_chr <<= 3;
718 		    cur_chr |= *s++ - '0';
719 		    if (isoctal (*s))
720 			{
721 			cur_chr <<= 3;
722 			cur_chr |= *s++ - '0';
723 			}
724 		    }
725 
726 		if (cur_chr != 0 || sending)
727 		    {
728 		    if (sending && (cur_chr == '\\' || cur_chr == 0))
729 		        {
730 			*s1++ = '\\';
731 		        }
732 		    *s1++ = cur_chr;
733 		    }
734 		break;
735 		}
736 
737 	    if (sending)
738 	        {
739 		*s1++ = '\\';
740 	        }
741 	    *s1++ = cur_chr;
742 	    break;
743 	    }
744 	}
745 
746     if (add_return)
747         {
748 	*s1++ = '\r';
749         }
750 
751     *s1++ = '\0'; /* guarantee closure */
752     *s1++ = '\0'; /* terminate the string */
753     return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
754     }
755 
756 /*
757  * Process the expect string
758  */
759 void chat_expect(s)
760 register char *s;
761     {
762     if (strcmp(s, "ABORT") == 0)
763 	{
764 	++abort_next;
765 	return;
766 	}
767 
768     if (strcmp(s, "REPORT") == 0)
769 	{
770 	++report_next;
771 	return;
772 	}
773 
774     if (strcmp(s, "TIMEOUT") == 0)
775 	{
776 	++timeout_next;
777 	return;
778 	}
779 
780     while (*s)
781 	{
782 	register char *hyphen;
783 
784 	for (hyphen = s; *hyphen; ++hyphen)
785 	    {
786 	    if (*hyphen == '-')
787 	        {
788 		if (hyphen == s || hyphen[-1] != '\\')
789 		    {
790 		    break;
791 		    }
792 	        }
793 	    }
794 
795 	if (*hyphen == '-')
796 	    {
797 	    *hyphen = '\0';
798 
799 	    if (get_string(s))
800 	        {
801 		return;
802 	        }
803 	    else
804 		{
805 		s = hyphen + 1;
806 
807 		for (hyphen = s; *hyphen; ++hyphen)
808 		    {
809 		    if (*hyphen == '-')
810 		        {
811 			if (hyphen == s || hyphen[-1] != '\\')
812 			    {
813 			    break;
814 			    }
815 		        }
816 		    }
817 
818 		if (*hyphen == '-')
819 		    {
820 		    *hyphen = '\0';
821 
822 		    chat_send(s);
823 		    s = hyphen + 1;
824 		    }
825 		else
826 		    {
827 		    chat_send(s);
828 		    return;
829 		    }
830 		}
831 	    }
832 	else
833 	    {
834 	    if (get_string(s))
835 	        {
836 		return;
837 	        }
838 	    else
839 		{
840 		if (fail_reason)
841 		    {
842 		    syslog(LOG_INFO, "Failed (%s)", fail_reason);
843 		    }
844 		else
845 		    {
846 		    syslog(LOG_INFO, "Failed");
847 		    }
848 
849 		terminate(exit_code);
850 		}
851 	    }
852 	}
853     }
854 
855 char *character(c)
856 int c;
857     {
858     static char string[10];
859     char *meta;
860 
861     meta = (c & 0x80) ? "M-" : "";
862     c &= 0x7F;
863 
864     if (c < 32)
865         {
866 	sprintf(string, "%s^%c", meta, (int)c + '@');
867         }
868     else
869         {
870 	if (c == 127)
871 	    {
872 	    sprintf(string, "%s^?", meta);
873 	    }
874 	else
875 	    {
876 	    sprintf(string, "%s%c", meta, c);
877 	    }
878         }
879 
880     return (string);
881     }
882 
883 /*
884  *  process the reply string
885  */
886 void chat_send (s)
887 register char *s;
888     {
889     if (abort_next)
890         {
891 	char *s1;
892 
893 	abort_next = 0;
894 
895 	if (n_aborts >= MAX_ABORTS)
896 	    {
897 	    fatal("Too many ABORT strings");
898 	    }
899 
900 	s1 = clean(s, 0);
901 
902 	if (strlen(s1) > strlen(s)
903 	    || strlen(s1) + 1 > sizeof(fail_buffer))
904 	    {
905 	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
906 	    die();
907 	    }
908 
909 	abort_string[n_aborts++] = s1;
910 
911 	if (verbose)
912 	    {
913 	    logf("abort on (");
914 
915 	    for (s1 = s; *s1; ++s1)
916 	        {
917 		logf(character(*s1));
918 	        }
919 
920 	    logf(")\n");
921 	    }
922 	return;
923 	}
924 
925     if (report_next)
926         {
927 	char *s1;
928 
929 	report_next = 0;
930 	if (n_reports >= MAX_REPORTS)
931 	    {
932 	    fatal("Too many REPORT strings");
933 	    }
934 
935 	s1 = clean(s, 0);
936 
937 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
938 	    {
939 	    syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
940 	    die();
941 	    }
942 
943 	report_string[n_reports++] = s1;
944 
945 	if (verbose)
946 	    {
947 	    logf("report (");
948 	    s1 = s;
949 	    while (*s1)
950 	        {
951 		logf(character(*s1));
952 		++s1;
953 	        }
954 	    logf(")\n");
955 	    }
956 	return;
957         }
958 
959     if (timeout_next)
960         {
961 	timeout_next = 0;
962 	timeout = atoi(s);
963 
964 	if (timeout <= 0)
965 	    {
966 	    timeout = DEFAULT_CHAT_TIMEOUT;
967 	    }
968 
969 	if (verbose)
970 	    {
971 	    syslog(LOG_INFO, "timeout set to %d seconds", timeout);
972 	    }
973 	return;
974         }
975 
976     if (strcmp(s, "EOT") == 0)
977         {
978 	s = "^D\\c";
979         }
980     else
981         {
982 	if (strcmp(s, "BREAK") == 0)
983 	    {
984 	    s = "\\K\\c";
985 	    }
986         }
987 
988     if (!put_string(s))
989         {
990 	syslog(LOG_INFO, "Failed");
991 	terminate(1);
992         }
993     }
994 
995 int get_char()
996     {
997     int status;
998     char c;
999 
1000     status = read(0, &c, 1);
1001 
1002     switch (status)
1003         {
1004     case 1:
1005 	return ((int)c & 0x7F);
1006 
1007     default:
1008 	syslog(LOG_WARNING, "warning: read() on stdin returned %d",
1009 	       status);
1010 
1011     case -1:
1012 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1013 	    {
1014 	    sysfatal("Can't get file mode flags on stdin");
1015 	    }
1016 	else
1017 	    {
1018 	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1019 	        {
1020 		sysfatal("Can't set file mode flags on stdin");
1021 	        }
1022 	    }
1023 
1024 	return (-1);
1025         }
1026     }
1027 
1028 int put_char(c)
1029 int c;
1030     {
1031     int status;
1032     char ch = c;
1033 
1034     usleep(10000);		/* inter-character typing delay (?) */
1035 
1036     status = write(1, &ch, 1);
1037 
1038     switch (status)
1039         {
1040     case 1:
1041 	return (0);
1042 
1043     default:
1044 	syslog(LOG_WARNING, "warning: write() on stdout returned %d",
1045 	       status);
1046 
1047     case -1:
1048 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1049 	    {
1050 	    sysfatal("Can't get file mode flags on stdin");
1051 	    }
1052 	else
1053 	    {
1054 	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1055 	        {
1056 		sysfatal("Can't set file mode flags on stdin");
1057 	        }
1058 	    }
1059 
1060 	return (-1);
1061         }
1062     }
1063 
1064 int write_char (c)
1065 int c;
1066     {
1067     if (alarmed || put_char(c) < 0)
1068 	{
1069 	extern int errno;
1070 
1071 	alarm(0);
1072 	alarmed = 0;
1073 
1074 	if (verbose)
1075 	    {
1076 	    if (errno == EINTR || errno == EWOULDBLOCK)
1077 	        {
1078 		syslog(LOG_INFO, " -- write timed out");
1079 	        }
1080 	    else
1081 	        {
1082 		syslog(LOG_INFO, " -- write failed: %m");
1083 	        }
1084 	    }
1085 	return (0);
1086 	}
1087     return (1);
1088     }
1089 
1090 int put_string (s)
1091 register char *s;
1092     {
1093     s = clean(s, 1);
1094 
1095     if (verbose)
1096 	{
1097 	logf("send (");
1098 
1099 	if (quiet)
1100 	    {
1101 	    logf("??????");
1102 	    }
1103 	else
1104 	    {
1105 	    register char *s1 = s;
1106 
1107 	    for (s1 = s; *s1; ++s1)
1108 	        {
1109 		logf(character(*s1));
1110 	        }
1111 	    }
1112 
1113 	logf(")\n");
1114 	}
1115 
1116     alarm(timeout); alarmed = 0;
1117 
1118     while (*s)
1119 	{
1120 	register char c = *s++;
1121 
1122 	if (c != '\\')
1123 	    {
1124 	    if (!write_char (c))
1125 	        {
1126 		return 0;
1127 	        }
1128 	    continue;
1129 	    }
1130 
1131 	c = *s++;
1132 	switch (c)
1133 	    {
1134 	case 'd':
1135 	    sleep(1);
1136 	    break;
1137 
1138 	case 'K':
1139 	    break_sequence();
1140 	    break;
1141 
1142 	case 'p':
1143 	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
1144 	    break;
1145 
1146 	default:
1147 	    if (!write_char (c))
1148 		return 0;
1149 	    break;
1150 	    }
1151 	}
1152 
1153     alarm(0);
1154     alarmed = 0;
1155     return (1);
1156     }
1157 
1158 /*
1159  *	'Wait for' this string to appear on this file descriptor.
1160  */
1161 int get_string(string)
1162 register char *string;
1163     {
1164     char temp[STR_LEN];
1165     int c, printed = 0, len, minlen;
1166     register char *s = temp, *end = s + STR_LEN;
1167 
1168     fail_reason = (char *)0;
1169     string = clean(string, 0);
1170     len = strlen(string);
1171     minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1172 
1173     if (verbose)
1174 	{
1175 	register char *s1;
1176 
1177 	logf("expect (");
1178 
1179 	for (s1 = string; *s1; ++s1)
1180 	    {
1181 	    logf(character(*s1));
1182 	    }
1183 
1184 	logf(")\n");
1185 	}
1186 
1187     if (len > STR_LEN)
1188 	{
1189 	syslog(LOG_INFO, "expect string is too long");
1190 	exit_code = 1;
1191 	return 0;
1192 	}
1193 
1194     if (len == 0)
1195 	{
1196 	if (verbose)
1197 	    {
1198 	    syslog(LOG_INFO, "got it");
1199 	    }
1200 
1201 	return (1);
1202 	}
1203 
1204     alarm(timeout);
1205     alarmed = 0;
1206 
1207     while ( ! alarmed && (c = get_char()) >= 0)
1208 	{
1209 	int n, abort_len, report_len;
1210 
1211 	if (verbose)
1212 	    {
1213 	    if (c == '\n')
1214 	        {
1215 		logf("\n");
1216 	        }
1217 	    else
1218 	        {
1219 		logf(character(c));
1220 	        }
1221 	    }
1222 
1223 	*s++ = c;
1224 
1225 	if (s - temp >= len &&
1226 	    c == string[len - 1] &&
1227 	    strncmp(s - len, string, len) == 0)
1228 	    {
1229 	    if (verbose)
1230 		{
1231 		logf(" -- got it\n");
1232 		}
1233 
1234 	    alarm(0);
1235 	    alarmed = 0;
1236 	    return (1);
1237 	    }
1238 
1239 	for (n = 0; n < n_aborts; ++n)
1240 	    {
1241 	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1242 		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
1243 	        {
1244 		if (verbose)
1245 		    {
1246 		    logf(" -- failed\n");
1247 		    }
1248 
1249 		alarm(0);
1250 		alarmed = 0;
1251 		exit_code = n + 4;
1252 		strcpy(fail_reason = fail_buffer, abort_string[n]);
1253 		return (0);
1254 	        }
1255 	    }
1256 
1257 	if (!report_gathering)
1258 	    {
1259 	    for (n = 0; n < n_reports; ++n)
1260 	        {
1261 		if ((report_string[n] != (char*) NULL) &&
1262 		    s - temp >= (report_len = strlen(report_string[n])) &&
1263 		    strncmp(s - report_len, report_string[n], report_len) == 0)
1264 		    {
1265 		    time_t time_now   = time ((time_t*) NULL);
1266 		    struct tm* tm_now = localtime (&time_now);
1267 
1268 		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1269 		    strcat (report_buffer, report_string[n]);
1270 
1271 		    report_string[n] = (char *) NULL;
1272 		    report_gathering = 1;
1273 		    break;
1274 		    }
1275 	        }
1276 	    }
1277 	else
1278 	    {
1279 	    if (!iscntrl (c))
1280 	        {
1281 		int rep_len = strlen (report_buffer);
1282 		report_buffer[rep_len]     = c;
1283 		report_buffer[rep_len + 1] = '\0';
1284 	        }
1285 	    else
1286 	        {
1287 		report_gathering = 0;
1288 		fprintf (report_fp, "chat:  %s\n", report_buffer);
1289 	        }
1290 	    }
1291 
1292 	if (s >= end)
1293 	    {
1294 	    strncpy (temp, s - minlen, minlen);
1295 	    s = temp + minlen;
1296 	    }
1297 
1298 	if (alarmed && verbose)
1299 	    {
1300 	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
1301 	    }
1302 	}
1303 
1304     alarm(0);
1305 
1306     if (verbose && printed)
1307 	{
1308 	if (alarmed)
1309 	    {
1310 	    logf(" -- read timed out\n");
1311 	    }
1312 	else
1313 	    {
1314 	    logflush();
1315 	    syslog(LOG_INFO, " -- read failed: %m");
1316 	    }
1317 	}
1318 
1319     exit_code = 3;
1320     alarmed   = 0;
1321     return (0);
1322     }
1323 
1324 #ifdef NO_USLEEP
1325 #include <sys/types.h>
1326 #include <sys/time.h>
1327 
1328 /*
1329   usleep -- support routine for 4.2BSD system call emulations
1330   last edit:  29-Oct-1984     D A Gwyn
1331   */
1332 
1333 extern int	  select();
1334 
1335 int
1336 usleep( usec )				  /* returns 0 if ok, else -1 */
1337     long		usec;		/* delay in microseconds */
1338 {
1339     static struct			/* `timeval' */
1340         {
1341 	long	tv_sec;		/* seconds */
1342 	long	tv_usec;	/* microsecs */
1343         } delay;	    /* _select() timeout */
1344 
1345     delay.tv_sec  = usec / 1000000L;
1346     delay.tv_usec = usec % 1000000L;
1347 
1348     return select( 0, (long *)0, (long *)0, (long *)0, &delay );
1349 }
1350 #endif
1351