chat.c revision 11990
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
34static 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
86static int _O = 0;		/* Internal state */
87/*************** Micro getopt() *********************************************/
88
89char *program_name;
90
91#define	MAX_ABORTS		50
92#define	MAX_REPORTS		50
93#define	DEFAULT_CHAT_TIMEOUT	45
94
95int verbose       = 0;
96int quiet         = 0;
97int report        = 0;
98int exit_code     = 0;
99FILE* report_fp   = (FILE *) 0;
100char *report_file = (char *) 0;
101char *chat_file   = (char *) 0;
102int timeout       = DEFAULT_CHAT_TIMEOUT;
103
104int 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)
110struct 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)
117struct termios saved_tty_parameters;
118#endif
119
120char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
121	fail_buffer[50];
122int n_aborts = 0, abort_next = 0, timeout_next = 0;
123
124char *report_string[MAX_REPORTS] ;
125char  report_buffer[50] ;
126int n_reports = 0, report_next = 0, report_gathering = 0 ;
127
128void *dup_mem __P((void *b, size_t c));
129void *copy_of __P((char *s));
130void usage __P((void));
131void logf __P((const char *str));
132void logflush __P((void));
133void fatal __P((const char *msg));
134void sysfatal __P((const char *msg));
135SIGTYPE sigalrm __P((int signo));
136SIGTYPE sigint __P((int signo));
137SIGTYPE sigterm __P((int signo));
138SIGTYPE sighup __P((int signo));
139void unalarm __P((void));
140void init __P((void));
141void set_tty_parameters __P((void));
142void break_sequence __P((void));
143void terminate __P((int status));
144void do_file __P((char *chat_file));
145int  get_string __P((register char *string));
146int  put_string __P((register char *s));
147int  write_char __P((int c));
148int  put_char __P((int c));
149int  get_char __P((void));
150void chat_send __P((register char *s));
151char *character __P((int c));
152void chat_expect __P((register char *s));
153char *clean __P((register char *s, int sending));
154void break_sequence __P((void));
155void terminate __P((int status));
156void die __P((void));
157
158void *dup_mem(b, c)
159void *b;
160size_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
171void *copy_of (s)
172char *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 */
183int
184main(argc, argv)
185int argc;
186char **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
308void do_file (chat_file)
309char *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 */
397void usage()
398    {
399    fprintf(stderr, "\
400Usage: %s [-v] [-t timeout] [-r report-file] {-f chat-file | chat-script}\n",
401	    program_name);
402    exit(1);
403    }
404
405char line[256];
406char *p;
407
408void logf (str)
409const 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
421void 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 */
433void die()
434    {
435    terminate(1);
436    }
437
438/*
439 *	Print an error message and terminate.
440 */
441
442void fatal (msg)
443const 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
454void sysfatal (msg)
455const char *msg;
456    {
457    syslog(LOG_ERR, "%s: %m", msg);
458    terminate(2);
459    }
460
461int alarmed = 0;
462
463SIGTYPE sigalrm(signo)
464int 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
491void 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
508SIGTYPE sigint(signo)
509int signo;
510    {
511    fatal("SIGINT");
512    }
513
514SIGTYPE sigterm(signo)
515int signo;
516    {
517    fatal("SIGTERM");
518    }
519
520SIGTYPE sighup(signo)
521int signo;
522    {
523    fatal("SIGHUP");
524    }
525
526void 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
538void 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
566void break_sequence()
567    {
568#ifdef TERMIOS
569    tcsendbreak (0, 0);
570#endif
571    }
572
573void terminate(status)
574int 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 */
603char *clean(s, sending)
604register char *s;
605int 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 */
758void chat_expect(s)
759register 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
854char *character(c)
855int 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 */
885void chat_send (s)
886register 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
994int 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
1027int put_char(c)
1028int 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
1063int write_char (c)
1064int 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
1089int put_string (s)
1090register 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 */
1160int get_string(string)
1161register 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
1332extern int	  select();
1333
1334int
1335usleep( 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