chat.c revision 41568
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 condition.
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 * -----------------
17 *	added -T and -U option and \T and \U substitution to pass a phone
18 *	number into chat script. Two are needed for some ISDN TA applications.
19 *	Keith Dart <kdart@cisco.com>
20 *
21 *
22 *	Added SAY keyword to send output to stderr.
23 *      This allows to turn ECHO OFF and to output specific, user selected,
24 *      text to give progress messages. This best works when stderr
25 *      exists (i.e.: pppd in nodetach mode).
26 *
27 * 	Added HANGUP directives to allow for us to be called
28 *      back. When HANGUP is set to NO, chat will not hangup at HUP signal.
29 *      We rely on timeouts in that case.
30 *
31 *      Added CLR_ABORT to clear previously set ABORT string. This has been
32 *      dictated by the HANGUP above as "NO CARRIER" (for example) must be
33 *      an ABORT condition until we know the other host is going to close
34 *      the connection for call back. As soon as we have completed the
35 *      first stage of the call back sequence, "NO CARRIER" is a valid, non
36 *      fatal string. As soon as we got called back (probably get "CONNECT"),
37 *      we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
38 *      Note that CLR_ABORT packs the abort_strings[] array so that we do not
39 *      have unused entries not being reclaimed.
40 *
41 *      In the same vein as above, added CLR_REPORT keyword.
42 *
43 *      Allow for comments. Line starting with '#' are comments and are
44 *      ignored. If a '#' is to be expected as the first character, the
45 *      expect string must be quoted.
46 *
47 *
48 *		Francis Demierre <Francis@SwissMail.Com>
49 * 		Thu May 15 17:15:40 MET DST 1997
50 *
51 *
52 *      Added -r "report file" switch & REPORT keyword.
53 *              Robert Geer <bgeer@xmission.com>
54 *
55 *      Added -s "use stderr" and -S "don't use syslog" switches.
56 *              June 18, 1997
57 *              Karl O. Pinc <kop@meme.com>
58 *
59 *
60 *	Added -e "echo" switch & ECHO keyword
61 *		Dick Streefland <dicks@tasking.nl>
62 *
63 *
64 *	Considerable updates and modifications by
65 *		Al Longyear <longyear@pobox.com>
66 *		Paul Mackerras <paulus@cs.anu.edu.au>
67 *
68 *
69 *	The original author is:
70 *
71 *		Karl Fox <karl@MorningStar.Com>
72 *		Morning Star Technologies, Inc.
73 *		1760 Zollinger Road
74 *		Columbus, OH  43221
75 *		(614)451-1883
76 *
77 *
78 */
79
80#ifndef lint
81static const char rcsid[] =
82	"$Id: chat.c,v 1.12 1998/06/23 21:58:41 peter Exp $";
83#endif
84
85#include <stdio.h>
86#include <ctype.h>
87#include <time.h>
88#include <fcntl.h>
89#include <signal.h>
90#include <errno.h>
91#include <string.h>
92#include <stdlib.h>
93#include <unistd.h>
94#include <sys/types.h>
95#include <sys/stat.h>
96#include <syslog.h>
97
98#ifndef TERMIO
99#undef	TERMIOS
100#define TERMIOS
101#endif
102
103#ifdef TERMIO
104#include <termio.h>
105#endif
106#ifdef TERMIOS
107#include <termios.h>
108#endif
109
110#define	STR_LEN	1024
111
112#ifndef SIGTYPE
113#define SIGTYPE void
114#endif
115
116#undef __P
117#undef __V
118
119#ifdef __STDC__
120#include <stdarg.h>
121#define __V(x)	x
122#define __P(x)	x
123#else
124#include <varargs.h>
125#define __V(x)	(va_alist) va_dcl
126#define __P(x)	()
127#define const
128#endif
129
130#ifndef O_NONBLOCK
131#define O_NONBLOCK	O_NDELAY
132#endif
133
134#ifdef SUNOS
135extern int sys_nerr;
136extern char *sys_errlist[];
137#define memmove(to, from, n)	bcopy(from, to, n)
138#define strerror(n)		((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
139				 "unknown error")
140#endif
141
142/*************** Micro getopt() *********************************************/
143#define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
144				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
145				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
146#define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
147				(_O=4,(char*)0):(char*)0)
148#define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
149#define	ARG(c,v)	(c?(--c,*v++):(char*)0)
150
151static int _O = 0;		/* Internal state */
152/*************** Micro getopt() *********************************************/
153
154#define	MAX_ABORTS		50
155#define	MAX_REPORTS		50
156#define	DEFAULT_CHAT_TIMEOUT	45
157
158int echo          = 0;
159int verbose       = 0;
160int to_log        = 1;
161int to_stderr     = 0;
162int Verbose       = 0;
163int quiet         = 0;
164int report        = 0;
165int exit_code     = 0;
166FILE* report_fp   = (FILE *) 0;
167char *report_file = (char *) 0;
168char *chat_file   = (char *) 0;
169char *phone_num   = (char *) 0;
170char *phone_num2  = (char *) 0;
171int timeout       = DEFAULT_CHAT_TIMEOUT;
172
173int have_tty_parameters = 0;
174
175#ifdef TERMIO
176#define term_parms struct termio
177#define get_term_param(param) ioctl(0, TCGETA, param)
178#define set_term_param(param) ioctl(0, TCSETA, param)
179struct termio saved_tty_parameters;
180#endif
181
182#ifdef TERMIOS
183#define term_parms struct termios
184#define get_term_param(param) tcgetattr(0, param)
185#define set_term_param(param) tcsetattr(0, TCSANOW, param)
186struct termios saved_tty_parameters;
187#endif
188
189char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
190	fail_buffer[50];
191int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
192int clear_abort_next = 0;
193
194char *report_string[MAX_REPORTS] ;
195char  report_buffer[50] ;
196int n_reports = 0, report_next = 0, report_gathering = 0 ;
197int clear_report_next = 0;
198
199int say_next = 0, hup_next = 0;
200
201void *dup_mem __P((void *b, size_t c));
202void *copy_of __P((char *s));
203static void usage __P((void));
204void logf __P((const char *fmt, ...));
205void fatal __P((int code, const char *fmt, ...));
206SIGTYPE sigalrm __P((int signo));
207SIGTYPE sigint __P((int signo));
208SIGTYPE sigterm __P((int signo));
209SIGTYPE sighup __P((int signo));
210void unalarm __P((void));
211void init __P((void));
212void set_tty_parameters __P((void));
213void echo_stderr __P((int));
214void break_sequence __P((void));
215void terminate __P((int status));
216void do_file __P((char *chat_file));
217int  get_string __P((register char *string));
218int  put_string __P((register char *s));
219int  write_char __P((int c));
220int  put_char __P((int c));
221int  get_char __P((void));
222void chat_send __P((register char *s));
223char *character __P((int c));
224void chat_expect __P((register char *s));
225char *clean __P((register char *s, int sending));
226void break_sequence __P((void));
227void terminate __P((int status));
228void pack_array __P((char **array, int end));
229char *expect_strtok __P((char *, char *));
230int vfmtmsg __P((char *, int, const char *, va_list));	/* vsprintf++ */
231
232int main __P((int, char *[]));
233
234void *dup_mem(b, c)
235void *b;
236size_t c;
237{
238    void *ans = malloc (c);
239    if (!ans)
240	fatal(2, "memory error!");
241
242    memcpy (ans, b, c);
243    return ans;
244}
245
246void *copy_of (s)
247char *s;
248{
249    return dup_mem (s, strlen (s) + 1);
250}
251
252/*
253 * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \
254 * [ -r report-file ] \
255 *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
256 *
257 *	Perform a UUCP-dialer-like chat script on stdin and stdout.
258 */
259int
260main(argc, argv)
261     int argc;
262     char **argv;
263{
264    int option;
265    char *arg;
266
267    tzset();
268
269    while ((option = OPTION(argc, argv)) != 0) {
270	switch (option) {
271	case 'e':
272	    ++echo;
273	    break;
274
275	case 'v':
276	    ++verbose;
277	    break;
278
279	case 'V':
280	    ++Verbose;
281	    break;
282
283	case 's':
284	    ++to_stderr;
285	    break;
286
287	case 'S':
288	    to_log = 0;
289	    break;
290
291	case 'f':
292	    if ((arg = OPTARG(argc, argv)) != NULL)
293		    chat_file = copy_of(arg);
294	    else
295		usage();
296	    break;
297
298	case 't':
299	    if ((arg = OPTARG(argc, argv)) != NULL)
300		timeout = atoi(arg);
301	    else
302		usage();
303	    break;
304
305	case 'r':
306	    arg = OPTARG (argc, argv);
307	    if (arg) {
308		if (report_fp != NULL)
309		    fclose (report_fp);
310		report_file = copy_of (arg);
311		report_fp   = fopen (report_file, "a");
312		if (report_fp != NULL) {
313		    if (verbose)
314			fprintf (report_fp, "Opening \"%s\"...\n",
315				 report_file);
316		    report = 1;
317		}
318	    }
319	    break;
320
321	case 'T':
322	    if ((arg = OPTARG(argc, argv)) != NULL)
323		phone_num = copy_of(arg);
324	    else
325		usage();
326	    break;
327
328	case 'U':
329	    if ((arg = OPTARG(argc, argv)) != NULL)
330		phone_num2 = copy_of(arg);
331	    else
332		usage();
333	    break;
334
335	default:
336	    usage();
337	    break;
338	}
339    }
340/*
341 * Default the report file to the stderr location
342 */
343    if (report_fp == NULL)
344	report_fp = stderr;
345
346    if (to_log) {
347#ifdef ultrix
348	openlog("chat", LOG_PID);
349#else
350	openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
351
352	if (verbose)
353	    setlogmask(LOG_UPTO(LOG_INFO));
354	else
355	    setlogmask(LOG_UPTO(LOG_WARNING));
356#endif
357    }
358
359    init();
360
361    if (chat_file != NULL) {
362	arg = ARG(argc, argv);
363	if (arg != NULL)
364	    usage();
365	else
366	    do_file (chat_file);
367    } else {
368	while ((arg = ARG(argc, argv)) != NULL) {
369	    chat_expect(arg);
370
371	    if ((arg = ARG(argc, argv)) != NULL)
372		chat_send(arg);
373	}
374    }
375
376    terminate(0);
377    return 0;
378}
379
380/*
381 *  Process a chat script when read from a file.
382 */
383
384void do_file (chat_file)
385char *chat_file;
386{
387    int linect, sendflg;
388    char *sp, *arg, quote;
389    char buf [STR_LEN];
390    FILE *cfp;
391
392    cfp = fopen (chat_file, "r");
393    if (cfp == NULL)
394	fatal(1, "%s -- open failed: %m", chat_file);
395
396    linect = 0;
397    sendflg = 0;
398
399    while (fgets(buf, STR_LEN, cfp) != NULL) {
400	sp = strchr (buf, '\n');
401	if (sp)
402	    *sp = '\0';
403
404	linect++;
405	sp = buf;
406
407        /* lines starting with '#' are comments. If a real '#'
408           is to be expected, it should be quoted .... */
409        if ( *sp == '#' )
410	    continue;
411
412	while (*sp != '\0') {
413	    if (*sp == ' ' || *sp == '\t') {
414		++sp;
415		continue;
416	    }
417
418	    if (*sp == '"' || *sp == '\'') {
419		quote = *sp++;
420		arg = sp;
421		while (*sp != quote) {
422		    if (*sp == '\0')
423			fatal(1, "unterminated quote (line %d)", linect);
424
425		    if (*sp++ == '\\') {
426			if (*sp != '\0')
427			    ++sp;
428		    }
429		}
430	    }
431	    else {
432		arg = sp;
433		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
434		    ++sp;
435	    }
436
437	    if (*sp != '\0')
438		*sp++ = '\0';
439
440	    if (sendflg)
441		chat_send (arg);
442	    else
443		chat_expect (arg);
444	    sendflg = !sendflg;
445	}
446    }
447    fclose (cfp);
448}
449
450/*
451 *	We got an error parsing the command line.
452 */
453static void
454usage()
455{
456    fprintf(stderr, "\
457Usage: chat [-e] [-v] [-V] [-t timeout] [-r report-file] [-T phone-number]\n\
458     [-U phone-number2] {-f chat-file | chat-script}\n");
459    exit(1);
460}
461
462char line[1024];
463
464/*
465 * Send a message to syslog and/or stderr.
466 */
467void logf __V((const char *fmt, ...))
468{
469    va_list args;
470
471#ifdef __STDC__
472    va_start(args, fmt);
473#else
474    char *fmt;
475    va_start(args);
476    fmt = va_arg(args, char *);
477#endif
478
479    vfmtmsg(line, sizeof(line), fmt, args);
480    if (to_log)
481	syslog(LOG_INFO, "%s", line);
482    if (to_stderr)
483	fprintf(stderr, "%s\n", line);
484}
485
486/*
487 *	Print an error message and terminate.
488 */
489
490void fatal __V((int code, const char *fmt, ...))
491{
492    va_list args;
493
494#ifdef __STDC__
495    va_start(args, fmt);
496#else
497    int code;
498    char *fmt;
499    va_start(args);
500    code = va_arg(args, int);
501    fmt = va_arg(args, char *);
502#endif
503
504    vfmtmsg(line, sizeof(line), fmt, args);
505    if (to_log)
506	syslog(LOG_ERR, "%s", line);
507    if (to_stderr)
508	fprintf(stderr, "%s\n", line);
509    terminate(code);
510}
511
512int alarmed = 0;
513
514SIGTYPE sigalrm(signo)
515int signo;
516{
517    int flags;
518
519    alarm(1);
520    alarmed = 1;		/* Reset alarm to avoid race window */
521    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
522
523    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
524	fatal(2, "Can't get file mode flags on stdin: %m");
525
526    if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
527	fatal(2, "Can't set file mode flags on stdin: %m");
528
529    if (verbose)
530	logf("alarm");
531}
532
533void unalarm()
534{
535    int flags;
536
537    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
538	fatal(2, "Can't get file mode flags on stdin: %m");
539
540    if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
541	fatal(2, "Can't set file mode flags on stdin: %m");
542}
543
544SIGTYPE sigint(signo)
545int signo;
546{
547    fatal(2, "SIGINT");
548}
549
550SIGTYPE sigterm(signo)
551int signo;
552{
553    fatal(2, "SIGTERM");
554}
555
556SIGTYPE sighup(signo)
557int signo;
558{
559    fatal(2, "SIGHUP");
560}
561
562void init()
563{
564    signal(SIGINT, sigint);
565    signal(SIGTERM, sigterm);
566    signal(SIGHUP, sighup);
567
568    set_tty_parameters();
569    signal(SIGALRM, sigalrm);
570    alarm(0);
571    alarmed = 0;
572}
573
574void set_tty_parameters()
575{
576#if defined(get_term_param)
577    term_parms t;
578
579    if (get_term_param (&t) < 0)
580	fatal(2, "Can't get terminal parameters: %m");
581
582    saved_tty_parameters = t;
583    have_tty_parameters  = 1;
584
585    t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
586    t.c_oflag      = 0;
587    t.c_lflag      = 0;
588    t.c_cc[VERASE] =
589    t.c_cc[VKILL]  = 0;
590    t.c_cc[VMIN]   = 1;
591    t.c_cc[VTIME]  = 0;
592
593    if (set_term_param (&t) < 0)
594	fatal(2, "Can't set terminal parameters: %m");
595#endif
596}
597
598void break_sequence()
599{
600#ifdef TERMIOS
601    tcsendbreak (0, 0);
602#endif
603}
604
605void terminate(status)
606int status;
607{
608    echo_stderr(-1);
609    if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
610/*
611 * Allow the last of the report string to be gathered before we terminate.
612 */
613	if (report_gathering) {
614	    int c, rep_len;
615
616	    rep_len = strlen(report_buffer);
617	    while (rep_len + 1 <= sizeof(report_buffer)) {
618		alarm(1);
619		c = get_char();
620		alarm(0);
621		if (c < 0 || iscntrl(c))
622		    break;
623		report_buffer[rep_len] = c;
624		++rep_len;
625	    }
626	    report_buffer[rep_len] = 0;
627	    fprintf (report_fp, "chat:  %s\n", report_buffer);
628	}
629	if (verbose)
630	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
631	fclose (report_fp);
632	report_fp = (FILE *) NULL;
633    }
634
635#if defined(get_term_param)
636    if (have_tty_parameters) {
637	if (set_term_param (&saved_tty_parameters) < 0)
638	    fatal(2, "Can't restore terminal parameters: %m");
639    }
640#endif
641
642    exit(status);
643}
644
645/*
646 *	'Clean up' this string.
647 */
648char *clean(s, sending)
649register char *s;
650int sending;  /* set to 1 when sending (putting) this string. */
651{
652    char temp[STR_LEN], cur_chr;
653    register char *s1, *phchar;
654    int add_return = sending;
655#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
656
657    s1 = temp;
658    while (*s) {
659	cur_chr = *s++;
660	if (cur_chr == '^') {
661	    cur_chr = *s++;
662	    if (cur_chr == '\0') {
663		*s1++ = '^';
664		break;
665	    }
666	    cur_chr &= 0x1F;
667	    if (cur_chr != 0) {
668		*s1++ = cur_chr;
669	    }
670	    continue;
671	}
672
673	if (cur_chr != '\\') {
674	    *s1++ = cur_chr;
675	    continue;
676	}
677
678	cur_chr = *s++;
679	if (cur_chr == '\0') {
680	    if (sending) {
681		*s1++ = '\\';
682		*s1++ = '\\';
683	    }
684	    break;
685	}
686
687	switch (cur_chr) {
688	case 'b':
689	    *s1++ = '\b';
690	    break;
691
692	case 'c':
693	    if (sending && *s == '\0')
694		add_return = 0;
695	    else
696		*s1++ = cur_chr;
697	    break;
698
699	case '\\':
700	case 'K':
701	case 'p':
702	case 'd':
703	    if (sending)
704		*s1++ = '\\';
705
706	    *s1++ = cur_chr;
707	    break;
708
709	case 'T':
710	    if (sending && phone_num) {
711		for ( phchar = phone_num; *phchar != '\0'; phchar++)
712		    *s1++ = *phchar;
713	    }
714	    else {
715		*s1++ = '\\';
716		*s1++ = 'T';
717	    }
718	    break;
719
720	case 'U':
721	    if (sending && phone_num2) {
722		for ( phchar = phone_num2; *phchar != '\0'; phchar++)
723		    *s1++ = *phchar;
724	    }
725	    else {
726		*s1++ = '\\';
727		*s1++ = 'U';
728	    }
729	    break;
730
731	case 'q':
732	    quiet = 1;
733	    break;
734
735	case 'r':
736	    *s1++ = '\r';
737	    break;
738
739	case 'n':
740	    *s1++ = '\n';
741	    break;
742
743	case 's':
744	    *s1++ = ' ';
745	    break;
746
747	case 't':
748	    *s1++ = '\t';
749	    break;
750
751	case 'N':
752	    if (sending) {
753		*s1++ = '\\';
754		*s1++ = '\0';
755	    }
756	    else
757		*s1++ = 'N';
758	    break;
759
760	default:
761	    if (isoctal (cur_chr)) {
762		cur_chr &= 0x07;
763		if (isoctal (*s)) {
764		    cur_chr <<= 3;
765		    cur_chr |= *s++ - '0';
766		    if (isoctal (*s)) {
767			cur_chr <<= 3;
768			cur_chr |= *s++ - '0';
769		    }
770		}
771
772		if (cur_chr != 0 || sending) {
773		    if (sending && (cur_chr == '\\' || cur_chr == 0))
774			*s1++ = '\\';
775		    *s1++ = cur_chr;
776		}
777		break;
778	    }
779
780	    if (sending)
781		*s1++ = '\\';
782	    *s1++ = cur_chr;
783	    break;
784	}
785    }
786
787    if (add_return)
788	*s1++ = '\r';
789
790    *s1++ = '\0'; /* guarantee closure */
791    *s1++ = '\0'; /* terminate the string */
792    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
793}
794
795/*
796 * A modified version of 'strtok'. This version skips \ sequences.
797 */
798
799char *expect_strtok (s, term)
800     char *s, *term;
801{
802    static  char *str   = "";
803    int	    escape_flag = 0;
804    char   *result;
805
806/*
807 * If a string was specified then do initial processing.
808 */
809    if (s)
810	str = s;
811
812/*
813 * If this is the escape flag then reset it and ignore the character.
814 */
815    if (*str)
816	result = str;
817    else
818	result = (char *) 0;
819
820    while (*str) {
821	if (escape_flag) {
822	    escape_flag = 0;
823	    ++str;
824	    continue;
825	}
826
827	if (*str == '\\') {
828	    ++str;
829	    escape_flag = 1;
830	    continue;
831	}
832
833/*
834 * If this is not in the termination string, continue.
835 */
836	if (strchr (term, *str) == (char *) 0) {
837	    ++str;
838	    continue;
839	}
840
841/*
842 * This is the terminator. Mark the end of the string and stop.
843 */
844	*str++ = '\0';
845	break;
846    }
847    return (result);
848}
849
850/*
851 * Process the expect string
852 */
853
854void chat_expect (s)
855char *s;
856{
857    char *expect;
858    char *reply;
859
860    if (strcmp(s, "HANGUP") == 0) {
861	++hup_next;
862        return;
863    }
864
865    if (strcmp(s, "ABORT") == 0) {
866	++abort_next;
867	return;
868    }
869
870    if (strcmp(s, "CLR_ABORT") == 0) {
871	++clear_abort_next;
872	return;
873    }
874
875    if (strcmp(s, "REPORT") == 0) {
876	++report_next;
877	return;
878    }
879
880    if (strcmp(s, "CLR_REPORT") == 0) {
881	++clear_report_next;
882	return;
883    }
884
885    if (strcmp(s, "TIMEOUT") == 0) {
886	++timeout_next;
887	return;
888    }
889
890    if (strcmp(s, "ECHO") == 0) {
891	++echo_next;
892	return;
893    }
894
895    if (strcmp(s, "SAY") == 0) {
896	++say_next;
897	return;
898    }
899
900/*
901 * Fetch the expect and reply string.
902 */
903    for (;;) {
904	expect = expect_strtok (s, "-");
905	s      = (char *) 0;
906
907	if (expect == (char *) 0)
908	    return;
909
910	reply = expect_strtok (s, "-");
911
912/*
913 * Handle the expect string. If successful then exit.
914 */
915	if (get_string (expect))
916	    return;
917
918/*
919 * If there is a sub-reply string then send it. Otherwise any condition
920 * is terminal.
921 */
922	if (reply == (char *) 0 || exit_code != 3)
923	    break;
924
925	chat_send (reply);
926    }
927
928/*
929 * The expectation did not occur. This is terminal.
930 */
931    if (fail_reason)
932	logf("Failed (%s)", fail_reason);
933    else
934	logf("Failed");
935    terminate(exit_code);
936}
937
938/*
939 * Translate the input character to the appropriate string for printing
940 * the data.
941 */
942
943char *character(c)
944int c;
945{
946    static char string[10];
947    char *meta;
948
949    meta = (c & 0x80) ? "M-" : "";
950    c &= 0x7F;
951
952    if (c < 32)
953	sprintf(string, "%s^%c", meta, (int)c + '@');
954    else if (c == 127)
955	sprintf(string, "%s^?", meta);
956    else
957	sprintf(string, "%s%c", meta, c);
958
959    return (string);
960}
961
962/*
963 *  process the reply string
964 */
965void chat_send (s)
966register char *s;
967{
968    if (say_next) {
969	say_next = 0;
970	s = clean(s,0);
971	write(2, s, strlen(s));
972        free(s);
973	return;
974    }
975
976    if (hup_next) {
977        hup_next = 0;
978	if (strcmp(s, "OFF") == 0)
979           signal(SIGHUP, SIG_IGN);
980        else
981           signal(SIGHUP, sighup);
982        return;
983    }
984
985    if (echo_next) {
986	echo_next = 0;
987	echo = (strcmp(s, "ON") == 0);
988	return;
989    }
990
991    if (abort_next) {
992	char *s1;
993
994	abort_next = 0;
995
996	if (n_aborts >= MAX_ABORTS)
997	    fatal(2, "Too many ABORT strings");
998
999	s1 = clean(s, 0);
1000
1001	if (strlen(s1) > strlen(s)
1002	    || strlen(s1) + 1 > sizeof(fail_buffer))
1003	    fatal(1, "Illegal or too-long ABORT string ('%v')", s);
1004
1005	abort_string[n_aborts++] = s1;
1006
1007	if (verbose)
1008	    logf("abort on (%v)", s);
1009	return;
1010    }
1011
1012    if (clear_abort_next) {
1013	char *s1;
1014	int   i;
1015        int   old_max;
1016	int   pack = 0;
1017
1018	clear_abort_next = 0;
1019
1020	s1 = clean(s, 0);
1021
1022	if (strlen(s1) > strlen(s)
1023	    || strlen(s1) + 1 > sizeof(fail_buffer))
1024	    fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
1025
1026        old_max = n_aborts;
1027	for (i=0; i < n_aborts; i++) {
1028	    if ( strcmp(s1,abort_string[i]) == 0 ) {
1029		free(abort_string[i]);
1030		abort_string[i] = NULL;
1031		pack++;
1032		n_aborts--;
1033		if (verbose)
1034		    logf("clear abort on (%v)", s);
1035	    }
1036	}
1037        free(s1);
1038	if (pack)
1039	    pack_array(abort_string,old_max);
1040	return;
1041    }
1042
1043    if (report_next) {
1044	char *s1;
1045
1046	report_next = 0;
1047	if (n_reports >= MAX_REPORTS)
1048	    fatal(2, "Too many REPORT strings");
1049
1050	s1 = clean(s, 0);
1051
1052	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
1053	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1054
1055	report_string[n_reports++] = s1;
1056
1057	if (verbose)
1058	    logf("report (%v)", s);
1059	return;
1060    }
1061
1062    if (clear_report_next) {
1063	char *s1;
1064	int   i;
1065	int   old_max;
1066	int   pack = 0;
1067
1068	clear_report_next = 0;
1069
1070	s1 = clean(s, 0);
1071
1072	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
1073	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1074
1075	old_max = n_reports;
1076	for (i=0; i < n_reports; i++) {
1077	    if ( strcmp(s1,report_string[i]) == 0 ) {
1078		free(report_string[i]);
1079		report_string[i] = NULL;
1080		pack++;
1081		n_reports--;
1082		if (verbose)
1083		    logf("clear report (%v)", s);
1084	    }
1085	}
1086        free(s1);
1087        if (pack)
1088	    pack_array(report_string,old_max);
1089
1090	return;
1091    }
1092
1093    if (timeout_next) {
1094	timeout_next = 0;
1095	timeout = atoi(s);
1096
1097	if (timeout <= 0)
1098	    timeout = DEFAULT_CHAT_TIMEOUT;
1099
1100	if (verbose)
1101	    logf("timeout set to %d seconds", timeout);
1102
1103	return;
1104    }
1105
1106    if (strcmp(s, "EOT") == 0)
1107	s = "^D\\c";
1108    else if (strcmp(s, "BREAK") == 0)
1109	s = "\\K\\c";
1110
1111    if (!put_string(s))
1112	fatal(1, "Failed");
1113}
1114
1115int get_char()
1116{
1117    int status;
1118    char c;
1119
1120    status = read(0, &c, 1);
1121
1122    switch (status) {
1123    case 1:
1124	return ((int)c & 0x7F);
1125
1126    default:
1127	logf("warning: read() on stdin returned %d", status);
1128
1129    case -1:
1130	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1131	    fatal(2, "Can't get file mode flags on stdin: %m");
1132
1133	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1134	    fatal(2, "Can't set file mode flags on stdin: %m");
1135
1136	return (-1);
1137    }
1138}
1139
1140int put_char(c)
1141int c;
1142{
1143    int status;
1144    char ch = c;
1145
1146    usleep(10000);		/* inter-character typing delay (?) */
1147
1148    status = write(1, &ch, 1);
1149
1150    switch (status) {
1151    case 1:
1152	return (0);
1153
1154    default:
1155	logf("warning: write() on stdout returned %d", status);
1156
1157    case -1:
1158	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1159	    fatal(2, "Can't get file mode flags on stdin, %m");
1160
1161	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1162	    fatal(2, "Can't set file mode flags on stdin: %m");
1163
1164	return (-1);
1165    }
1166}
1167
1168int write_char (c)
1169int c;
1170{
1171    if (alarmed || put_char(c) < 0) {
1172	alarm(0);
1173	alarmed = 0;
1174
1175	if (verbose) {
1176	    if (errno == EINTR || errno == EWOULDBLOCK)
1177		logf(" -- write timed out");
1178	    else
1179		logf(" -- write failed: %m");
1180	}
1181	return (0);
1182    }
1183    return (1);
1184}
1185
1186int put_string (s)
1187register char *s;
1188{
1189    quiet = 0;
1190    s = clean(s, 1);
1191
1192    if (verbose) {
1193	if (quiet)
1194	    logf("send (??????)");
1195	else
1196	    logf("send (%v)", s);
1197    }
1198
1199    alarm(timeout); alarmed = 0;
1200
1201    while (*s) {
1202	register char c = *s++;
1203
1204	if (c != '\\') {
1205	    if (!write_char (c))
1206		return 0;
1207	    continue;
1208	}
1209
1210	c = *s++;
1211	switch (c) {
1212	case 'd':
1213	    sleep(1);
1214	    break;
1215
1216	case 'K':
1217	    break_sequence();
1218	    break;
1219
1220	case 'p':
1221	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
1222	    break;
1223
1224	default:
1225	    if (!write_char (c))
1226		return 0;
1227	    break;
1228	}
1229    }
1230
1231    alarm(0);
1232    alarmed = 0;
1233    return (1);
1234}
1235
1236/*
1237 *	Echo a character to stderr.
1238 *	When called with -1, a '\n' character is generated when
1239 *	the cursor is not at the beginning of a line.
1240 */
1241void echo_stderr(n)
1242int n;
1243{
1244    static int need_lf;
1245    char *s;
1246
1247    switch (n) {
1248    case '\r':		/* ignore '\r' */
1249	break;
1250    case -1:
1251	if (need_lf == 0)
1252	    break;
1253	/* fall through */
1254    case '\n':
1255	write(2, "\n", 1);
1256	need_lf = 0;
1257	break;
1258    default:
1259	s = character(n);
1260	write(2, s, strlen(s));
1261	need_lf = 1;
1262	break;
1263    }
1264}
1265
1266/*
1267 *	'Wait for' this string to appear on this file descriptor.
1268 */
1269int get_string(string)
1270register char *string;
1271{
1272    char temp[STR_LEN];
1273    int c, printed = 0, len, minlen;
1274    register char *s = temp, *end = s + STR_LEN;
1275    char *logged = temp;
1276
1277    fail_reason = (char *)0;
1278    string = clean(string, 0);
1279    len = strlen(string);
1280    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1281
1282    if (verbose)
1283	logf("expect (%v)", string);
1284
1285    if (len > STR_LEN) {
1286	logf("expect string is too long");
1287	exit_code = 1;
1288	return 0;
1289    }
1290
1291    if (len == 0) {
1292	if (verbose)
1293	    logf("got it");
1294	return (1);
1295    }
1296
1297    alarm(timeout);
1298    alarmed = 0;
1299
1300    while ( ! alarmed && (c = get_char()) >= 0) {
1301	int n, abort_len, report_len;
1302
1303	if (echo)
1304	    echo_stderr(c);
1305	if (verbose && c == '\n') {
1306	    if (s == logged)
1307		logf("");	/* blank line */
1308	    else
1309		logf("%0.*v", s - logged, logged);
1310	    logged = s + 1;
1311	}
1312
1313	*s++ = c;
1314
1315	if (verbose && s >= logged + 80) {
1316	    logf("%0.*v", s - logged, logged);
1317	    logged = s;
1318	}
1319
1320	if (Verbose) {
1321	   if (c == '\n')
1322	       fputc( '\n', stderr );
1323	   else if (c != '\r')
1324	       fprintf( stderr, "%s", character(c) );
1325	}
1326
1327	if (!report_gathering) {
1328	    for (n = 0; n < n_reports; ++n) {
1329		if ((report_string[n] != (char*) NULL) &&
1330		    s - temp >= (report_len = strlen(report_string[n])) &&
1331		    strncmp(s - report_len, report_string[n], report_len) == 0) {
1332		    time_t time_now   = time ((time_t*) NULL);
1333		    struct tm* tm_now = localtime (&time_now);
1334
1335		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1336		    strcat (report_buffer, report_string[n]);
1337
1338		    report_string[n] = (char *) NULL;
1339		    report_gathering = 1;
1340		    break;
1341		}
1342	    }
1343	}
1344	else {
1345	    if (!iscntrl (c)) {
1346		int rep_len = strlen (report_buffer);
1347		report_buffer[rep_len]     = c;
1348		report_buffer[rep_len + 1] = '\0';
1349	    }
1350	    else {
1351		report_gathering = 0;
1352		fprintf (report_fp, "chat:  %s\n", report_buffer);
1353	    }
1354	}
1355
1356	if (s - temp >= len &&
1357	    c == string[len - 1] &&
1358	    strncmp(s - len, string, len) == 0) {
1359	    if (verbose) {
1360		if (s > logged)
1361		    logf("%0.*v", s - logged, logged);
1362		logf(" -- got it\n");
1363	    }
1364
1365	    alarm(0);
1366	    alarmed = 0;
1367	    return (1);
1368	}
1369
1370	for (n = 0; n < n_aborts; ++n) {
1371	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1372		strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
1373		if (verbose) {
1374		    if (s > logged)
1375			logf("%0.*v", s - logged, logged);
1376		    logf(" -- failed");
1377		}
1378
1379		alarm(0);
1380		alarmed = 0;
1381		exit_code = n + 4;
1382		strcpy(fail_reason = fail_buffer, abort_string[n]);
1383		return (0);
1384	    }
1385	}
1386
1387	if (s >= end) {
1388	    if (logged < s - minlen) {
1389		logf("%0.*v", s - logged, logged);
1390		logged = s;
1391	    }
1392	    s -= minlen;
1393	    memmove(temp, s, minlen);
1394	    logged = temp + (logged - s);
1395	    s = temp + minlen;
1396	}
1397
1398	if (alarmed && verbose)
1399	    logf("warning: alarm synchronization problem");
1400    }
1401
1402    alarm(0);
1403
1404    if (verbose && printed) {
1405	if (alarmed)
1406	    logf(" -- read timed out");
1407	else
1408	    logf(" -- read failed: %m");
1409    }
1410
1411    exit_code = 3;
1412    alarmed   = 0;
1413    return (0);
1414}
1415
1416/*
1417 * Gross kludge to handle Solaris versions >= 2.6 having usleep.
1418 */
1419#ifdef SOL2
1420#include <sys/param.h>
1421#if MAXUID > 65536		/* then this is Solaris 2.6 or later */
1422#undef NO_USLEEP
1423#endif
1424#endif /* SOL2 */
1425
1426#ifdef NO_USLEEP
1427#include <sys/types.h>
1428#include <sys/time.h>
1429
1430/*
1431  usleep -- support routine for 4.2BSD system call emulations
1432  last edit:  29-Oct-1984     D A Gwyn
1433  */
1434
1435extern int	  select();
1436
1437int
1438usleep( usec )				  /* returns 0 if ok, else -1 */
1439    long		usec;		/* delay in microseconds */
1440{
1441    static struct {		/* `timeval' */
1442	long	tv_sec;		/* seconds */
1443	long	tv_usec;	/* microsecs */
1444    } delay;	    		/* _select() timeout */
1445
1446    delay.tv_sec  = usec / 1000000L;
1447    delay.tv_usec = usec % 1000000L;
1448
1449    return select(0, (long *)0, (long *)0, (long *)0, &delay);
1450}
1451#endif
1452
1453void
1454pack_array (array, end)
1455    char **array; /* The address of the array of string pointers */
1456    int    end;   /* The index of the next free entry before CLR_ */
1457{
1458    int i, j;
1459
1460    for (i = 0; i < end; i++) {
1461	if (array[i] == NULL) {
1462	    for (j = i+1; j < end; ++j)
1463		if (array[j] != NULL)
1464		    array[i++] = array[j];
1465	    for (; i < end; ++i)
1466		array[i] = NULL;
1467	    break;
1468	}
1469    }
1470}
1471
1472/*
1473 * vfmtmsg - format a message into a buffer.  Like vsprintf except we
1474 * also specify the length of the output buffer, and we handle the
1475 * %m (error message) format.
1476 * Doesn't do floating-point formats.
1477 * Returns the number of chars put into buf.
1478 */
1479#define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)
1480
1481int
1482vfmtmsg(buf, buflen, fmt, args)
1483    char *buf;
1484    int buflen;
1485    const char *fmt;
1486    va_list args;
1487{
1488    int c, i, n;
1489    int width, prec, fillch;
1490    int base, len, neg, quoted;
1491    unsigned long val = 0;
1492    char *str, *buf0;
1493    const char *f;
1494    unsigned char *p;
1495    char num[32];
1496    static char hexchars[] = "0123456789abcdef";
1497
1498    buf0 = buf;
1499    --buflen;
1500    while (buflen > 0) {
1501	for (f = fmt; *f != '%' && *f != 0; ++f)
1502	    ;
1503	if (f > fmt) {
1504	    len = f - fmt;
1505	    if (len > buflen)
1506		len = buflen;
1507	    memcpy(buf, fmt, len);
1508	    buf += len;
1509	    buflen -= len;
1510	    fmt = f;
1511	}
1512	if (*fmt == 0)
1513	    break;
1514	c = *++fmt;
1515	width = prec = 0;
1516	fillch = ' ';
1517	if (c == '0') {
1518	    fillch = '0';
1519	    c = *++fmt;
1520	}
1521	if (c == '*') {
1522	    width = va_arg(args, int);
1523	    c = *++fmt;
1524	} else {
1525	    while (isdigit(c)) {
1526		width = width * 10 + c - '0';
1527		c = *++fmt;
1528	    }
1529	}
1530	if (c == '.') {
1531	    c = *++fmt;
1532	    if (c == '*') {
1533		prec = va_arg(args, int);
1534		c = *++fmt;
1535	    } else {
1536		while (isdigit(c)) {
1537		    prec = prec * 10 + c - '0';
1538		    c = *++fmt;
1539		}
1540	    }
1541	}
1542	str = 0;
1543	base = 0;
1544	neg = 0;
1545	++fmt;
1546	switch (c) {
1547	case 'd':
1548	    i = va_arg(args, int);
1549	    if (i < 0) {
1550		neg = 1;
1551		val = -i;
1552	    } else
1553		val = i;
1554	    base = 10;
1555	    break;
1556	case 'o':
1557	    val = va_arg(args, unsigned int);
1558	    base = 8;
1559	    break;
1560	case 'x':
1561	    val = va_arg(args, unsigned int);
1562	    base = 16;
1563	    break;
1564	case 'p':
1565	    val = (unsigned long) va_arg(args, void *);
1566	    base = 16;
1567	    neg = 2;
1568	    break;
1569	case 's':
1570	    str = va_arg(args, char *);
1571	    break;
1572	case 'c':
1573	    num[0] = va_arg(args, int);
1574	    num[1] = 0;
1575	    str = num;
1576	    break;
1577	case 'm':
1578	    str = strerror(errno);
1579	    break;
1580	case 'v':		/* "visible" string */
1581	case 'q':		/* quoted string */
1582	    quoted = c == 'q';
1583	    p = va_arg(args, unsigned char *);
1584	    if (fillch == '0' && prec > 0) {
1585		n = prec;
1586	    } else {
1587		n = strlen((char *)p);
1588		if (prec > 0 && prec < n)
1589		    n = prec;
1590	    }
1591	    while (n > 0 && buflen > 0) {
1592		c = *p++;
1593		--n;
1594		if (!quoted && c >= 0x80) {
1595		    OUTCHAR('M');
1596		    OUTCHAR('-');
1597		    c -= 0x80;
1598		}
1599		if (quoted && (c == '"' || c == '\\'))
1600		    OUTCHAR('\\');
1601		if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
1602		    if (quoted) {
1603			OUTCHAR('\\');
1604			switch (c) {
1605			case '\t':	OUTCHAR('t');	break;
1606			case '\n':	OUTCHAR('n');	break;
1607			case '\b':	OUTCHAR('b');	break;
1608			case '\f':	OUTCHAR('f');	break;
1609			default:
1610			    OUTCHAR('x');
1611			    OUTCHAR(hexchars[c >> 4]);
1612			    OUTCHAR(hexchars[c & 0xf]);
1613			}
1614		    } else {
1615			if (c == '\t')
1616			    OUTCHAR(c);
1617			else {
1618			    OUTCHAR('^');
1619			    OUTCHAR(c ^ 0x40);
1620			}
1621		    }
1622		} else
1623		    OUTCHAR(c);
1624	    }
1625	    continue;
1626	default:
1627	    *buf++ = '%';
1628	    if (c != '%')
1629		--fmt;		/* so %z outputs %z etc. */
1630	    --buflen;
1631	    continue;
1632	}
1633	if (base != 0) {
1634	    str = num + sizeof(num);
1635	    *--str = 0;
1636	    while (str > num + neg) {
1637		*--str = hexchars[val % base];
1638		val = val / base;
1639		if (--prec <= 0 && val == 0)
1640		    break;
1641	    }
1642	    switch (neg) {
1643	    case 1:
1644		*--str = '-';
1645		break;
1646	    case 2:
1647		*--str = 'x';
1648		*--str = '0';
1649		break;
1650	    }
1651	    len = num + sizeof(num) - 1 - str;
1652	} else {
1653	    len = strlen(str);
1654	    if (prec > 0 && len > prec)
1655		len = prec;
1656	}
1657	if (width > 0) {
1658	    if (width > buflen)
1659		width = buflen;
1660	    if ((n = width - len) > 0) {
1661		buflen -= n;
1662		for (; n > 0; --n)
1663		    *buf++ = fillch;
1664	    }
1665	}
1666	if (len > buflen)
1667	    len = buflen;
1668	memcpy(buf, str, len);
1669	buf += len;
1670	buflen -= len;
1671    }
1672    *buf = 0;
1673    return buf - buf0;
1674}
1675