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