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