xref: /freebsd/usr.bin/chat/chat.c (revision 544c5e5b533574625e9921209ee3ceb1ee3d22d0)
19b1aec48SLars Fredriksen /*
29b1aec48SLars Fredriksen  *	Chat -- a program for automatic session establishment (i.e. dial
39b1aec48SLars Fredriksen  *		the phone and log in).
49b1aec48SLars Fredriksen  *
59f65f104SPeter Wemm  * Standard termination codes:
69f65f104SPeter Wemm  *  0 - successful completion of the script
79f65f104SPeter Wemm  *  1 - invalid argument, expect string too large, etc.
801c855caSPeter Wemm  *  2 - error on an I/O operation or fatal error condition.
99f65f104SPeter Wemm  *  3 - timeout waiting for a simple string.
109f65f104SPeter Wemm  *  4 - the first string declared as "ABORT"
119f65f104SPeter Wemm  *  5 - the second string declared as "ABORT"
129f65f104SPeter Wemm  *  6 - ... and so on for successive ABORT strings.
139f65f104SPeter Wemm  *
149b1aec48SLars Fredriksen  *	This software is in the public domain.
159b1aec48SLars Fredriksen  *
163d793cf1SPeter Wemm  * -----------------
1701c855caSPeter Wemm  *	added -T and -U option and \T and \U substitution to pass a phone
1801c855caSPeter Wemm  *	number into chat script. Two are needed for some ISDN TA applications.
1901c855caSPeter Wemm  *	Keith Dart <kdart@cisco.com>
2001c855caSPeter Wemm  *
219b1aec48SLars Fredriksen  *
223d793cf1SPeter Wemm  *	Added SAY keyword to send output to stderr.
233d793cf1SPeter Wemm  *      This allows to turn ECHO OFF and to output specific, user selected,
243d793cf1SPeter Wemm  *      text to give progress messages. This best works when stderr
253d793cf1SPeter Wemm  *      exists (i.e.: pppd in nodetach mode).
263d793cf1SPeter Wemm  *
273d793cf1SPeter Wemm  * 	Added HANGUP directives to allow for us to be called
283d793cf1SPeter Wemm  *      back. When HANGUP is set to NO, chat will not hangup at HUP signal.
293d793cf1SPeter Wemm  *      We rely on timeouts in that case.
303d793cf1SPeter Wemm  *
313d793cf1SPeter Wemm  *      Added CLR_ABORT to clear previously set ABORT string. This has been
323d793cf1SPeter Wemm  *      dictated by the HANGUP above as "NO CARRIER" (for example) must be
333d793cf1SPeter Wemm  *      an ABORT condition until we know the other host is going to close
343d793cf1SPeter Wemm  *      the connection for call back. As soon as we have completed the
353d793cf1SPeter Wemm  *      first stage of the call back sequence, "NO CARRIER" is a valid, non
363d793cf1SPeter Wemm  *      fatal string. As soon as we got called back (probably get "CONNECT"),
373d793cf1SPeter Wemm  *      we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
383d793cf1SPeter Wemm  *      Note that CLR_ABORT packs the abort_strings[] array so that we do not
393d793cf1SPeter Wemm  *      have unused entries not being reclaimed.
403d793cf1SPeter Wemm  *
413d793cf1SPeter Wemm  *      In the same vein as above, added CLR_REPORT keyword.
423d793cf1SPeter Wemm  *
433d793cf1SPeter Wemm  *      Allow for comments. Line starting with '#' are comments and are
443d793cf1SPeter Wemm  *      ignored. If a '#' is to be expected as the first character, the
453d793cf1SPeter Wemm  *      expect string must be quoted.
463d793cf1SPeter Wemm  *
473d793cf1SPeter Wemm  *
483d793cf1SPeter Wemm  *		Francis Demierre <Francis@SwissMail.Com>
493d793cf1SPeter Wemm  * 		Thu May 15 17:15:40 MET DST 1997
503d793cf1SPeter Wemm  *
519b1aec48SLars Fredriksen  *
529f65f104SPeter Wemm  *      Added -r "report file" switch & REPORT keyword.
539f65f104SPeter Wemm  *              Robert Geer <bgeer@xmission.com>
549f65f104SPeter Wemm  *
5501c855caSPeter Wemm  *      Added -s "use stderr" and -S "don't use syslog" switches.
5601c855caSPeter Wemm  *              June 18, 1997
5701c855caSPeter Wemm  *              Karl O. Pinc <kop@meme.com>
5801c855caSPeter Wemm  *
593d793cf1SPeter Wemm  *
603d793cf1SPeter Wemm  *	Added -e "echo" switch & ECHO keyword
613d793cf1SPeter Wemm  *		Dick Streefland <dicks@tasking.nl>
623d793cf1SPeter Wemm  *
633d793cf1SPeter Wemm  *
643d793cf1SPeter Wemm  *	Considerable updates and modifications by
653d793cf1SPeter Wemm  *		Al Longyear <longyear@pobox.com>
663d793cf1SPeter Wemm  *		Paul Mackerras <paulus@cs.anu.edu.au>
673d793cf1SPeter Wemm  *
683d793cf1SPeter Wemm  *
699b1aec48SLars Fredriksen  *	The original author is:
709b1aec48SLars Fredriksen  *
719b1aec48SLars Fredriksen  *		Karl Fox <karl@MorningStar.Com>
729b1aec48SLars Fredriksen  *		Morning Star Technologies, Inc.
739b1aec48SLars Fredriksen  *		1760 Zollinger Road
749b1aec48SLars Fredriksen  *		Columbus, OH  43221
759b1aec48SLars Fredriksen  *		(614)451-1883
769f65f104SPeter Wemm  *
773d793cf1SPeter Wemm  *
789b1aec48SLars Fredriksen  */
799b1aec48SLars Fredriksen 
80ee6b974cSMark Murray #include <sys/cdefs.h>
81ee6b974cSMark Murray __FBSDID("$FreeBSD$");
829b1aec48SLars Fredriksen 
839b1aec48SLars Fredriksen #include <sys/types.h>
849b1aec48SLars Fredriksen #include <sys/stat.h>
85ee6b974cSMark Murray #include <ctype.h>
86ee6b974cSMark Murray #include <errno.h>
87ee6b974cSMark Murray #include <fcntl.h>
88ee6b974cSMark Murray #include <signal.h>
89ee6b974cSMark Murray #include <stdarg.h>
90ee6b974cSMark Murray #include <stdio.h>
91ee6b974cSMark Murray #include <stdlib.h>
92ee6b974cSMark Murray #include <string.h>
939b1aec48SLars Fredriksen #include <syslog.h>
949b1aec48SLars Fredriksen #include <termios.h>
95ee6b974cSMark Murray #include <time.h>
96ee6b974cSMark Murray #include <unistd.h>
979b1aec48SLars Fredriksen 
989b1aec48SLars Fredriksen #define	STR_LEN	1024
999b1aec48SLars Fredriksen 
1009b1aec48SLars Fredriksen #ifndef SIGTYPE
1019b1aec48SLars Fredriksen #define SIGTYPE void
1029b1aec48SLars Fredriksen #endif
1039b1aec48SLars Fredriksen 
1049f65f104SPeter Wemm #ifndef O_NONBLOCK
1059f65f104SPeter Wemm #define O_NONBLOCK	O_NDELAY
1069f65f104SPeter Wemm #endif
1079f65f104SPeter Wemm 
1089b1aec48SLars Fredriksen #define	MAX_ABORTS		50
1099f65f104SPeter Wemm #define	MAX_REPORTS		50
1109b1aec48SLars Fredriksen #define	DEFAULT_CHAT_TIMEOUT	45
1119b1aec48SLars Fredriksen 
1123d793cf1SPeter Wemm int echo          = 0;
1139b1aec48SLars Fredriksen int verbose       = 0;
11401c855caSPeter Wemm int to_log        = 1;
11501c855caSPeter Wemm int to_stderr     = 0;
1163d793cf1SPeter Wemm int Verbose       = 0;
1179b1aec48SLars Fredriksen int quiet         = 0;
1189f65f104SPeter Wemm int exit_code     = 0;
1199f65f104SPeter Wemm FILE* report_fp   = (FILE *) 0;
1209f65f104SPeter Wemm char *report_file = (char *) 0;
1219b1aec48SLars Fredriksen char *chat_file   = (char *) 0;
12201c855caSPeter Wemm char *phone_num   = (char *) 0;
12301c855caSPeter Wemm char *phone_num2  = (char *) 0;
1249b1aec48SLars Fredriksen int timeout       = DEFAULT_CHAT_TIMEOUT;
1259b1aec48SLars Fredriksen 
126ee6b974cSMark Murray static char blank[] = "";
127ee6b974cSMark Murray 
1289b1aec48SLars Fredriksen int have_tty_parameters = 0;
1299f65f104SPeter Wemm 
1309f65f104SPeter Wemm #define term_parms struct termios
1319f65f104SPeter Wemm #define get_term_param(param) tcgetattr(0, param)
1329f65f104SPeter Wemm #define set_term_param(param) tcsetattr(0, TCSANOW, param)
1339b1aec48SLars Fredriksen struct termios saved_tty_parameters;
1349b1aec48SLars Fredriksen 
1359b1aec48SLars Fredriksen char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
1369b1aec48SLars Fredriksen 	fail_buffer[50];
1373d793cf1SPeter Wemm int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
1383d793cf1SPeter Wemm int clear_abort_next = 0;
1399b1aec48SLars Fredriksen 
1409f65f104SPeter Wemm char *report_string[MAX_REPORTS] ;
1419f65f104SPeter Wemm char  report_buffer[50] ;
1429f65f104SPeter Wemm int n_reports = 0, report_next = 0, report_gathering = 0 ;
1433d793cf1SPeter Wemm int clear_report_next = 0;
1443d793cf1SPeter Wemm 
1453d793cf1SPeter Wemm int say_next = 0, hup_next = 0;
1469f65f104SPeter Wemm 
147f1bb2cd2SWarner Losh void *dup_mem(void *b, size_t c);
148f1bb2cd2SWarner Losh void *copy_of(char *s);
149f1bb2cd2SWarner Losh static void usage(void);
1508491f234STim Kientzle void chat_logf(const char *fmt, ...);
151f1bb2cd2SWarner Losh void fatal(int code, const char *fmt, ...);
152f1bb2cd2SWarner Losh SIGTYPE sigalrm(int signo);
153f1bb2cd2SWarner Losh SIGTYPE sigint(int signo);
154f1bb2cd2SWarner Losh SIGTYPE sigterm(int signo);
155f1bb2cd2SWarner Losh SIGTYPE sighup(int signo);
156f1bb2cd2SWarner Losh void init(void);
157f1bb2cd2SWarner Losh void set_tty_parameters(void);
158f1bb2cd2SWarner Losh void echo_stderr(int);
159f1bb2cd2SWarner Losh void break_sequence(void);
160f1bb2cd2SWarner Losh void terminate(int status);
161ee6b974cSMark Murray void do_file(char *chatfile);
162ee6b974cSMark Murray int  get_string(char *string);
163ee6b974cSMark Murray int  put_string(char *s);
164f1bb2cd2SWarner Losh int  write_char(int c);
165f1bb2cd2SWarner Losh int  put_char(int c);
166f1bb2cd2SWarner Losh int  get_char(void);
167ee6b974cSMark Murray void chat_send(char *s);
168f1bb2cd2SWarner Losh char *character(int c);
169ee6b974cSMark Murray void chat_expect(char *s);
170ee6b974cSMark Murray char *clean(char *s, int sending);
171f1bb2cd2SWarner Losh void pack_array(char **array, int end);
172ee6b974cSMark Murray char *expect_strtok(char *, const char *);
173f1bb2cd2SWarner Losh int vfmtmsg(char *, int, const char *, va_list);	/* vsprintf++ */
1743d793cf1SPeter Wemm 
175ee6b974cSMark Murray void *
176ee6b974cSMark Murray dup_mem(void *b, size_t c)
1779b1aec48SLars Fredriksen {
1789b1aec48SLars Fredriksen     void *ans = malloc (c);
1799b1aec48SLars Fredriksen     if (!ans)
18001c855caSPeter Wemm 	fatal(2, "memory error!");
18101c855caSPeter Wemm 
1829b1aec48SLars Fredriksen     memcpy (ans, b, c);
1839b1aec48SLars Fredriksen     return ans;
1849b1aec48SLars Fredriksen }
1859b1aec48SLars Fredriksen 
186ee6b974cSMark Murray void *
187ee6b974cSMark Murray copy_of(char *s)
1889b1aec48SLars Fredriksen {
1899b1aec48SLars Fredriksen     return dup_mem (s, strlen (s) + 1);
1909b1aec48SLars Fredriksen }
1919b1aec48SLars Fredriksen 
1929b1aec48SLars Fredriksen /*
19318ebe779SXin LI  * chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout]
19418ebe779SXin LI  *      [-T phone-number] [-U phone-number2] [chat-script]
19518ebe779SXin LI  * where chat-script has the form:
19618ebe779SXin LI  *	[...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]]
1979b1aec48SLars Fredriksen  *
1989b1aec48SLars Fredriksen  * Perform a UUCP-dialer-like chat script on stdin and stdout.
1999b1aec48SLars Fredriksen  */
2009b1aec48SLars Fredriksen int
201ee6b974cSMark Murray main(int argc, char *argv[])
2029b1aec48SLars Fredriksen {
2039b1aec48SLars Fredriksen     int option;
2049b1aec48SLars Fredriksen 
2059f65f104SPeter Wemm     tzset();
2069b1aec48SLars Fredriksen 
20718ebe779SXin LI     while ((option = getopt(argc, argv, "ef:r:sSt:T:U:vV")) != -1) {
20801c855caSPeter Wemm 	switch (option) {
2093d793cf1SPeter Wemm 	case 'e':
2103d793cf1SPeter Wemm 	    ++echo;
2113d793cf1SPeter Wemm 	    break;
2123d793cf1SPeter Wemm 
21318ebe779SXin LI 	case 'f':
21418ebe779SXin LI 	    if (chat_file != NULL)
21518ebe779SXin LI 		free(chat_file);
21618ebe779SXin LI 	    chat_file = copy_of(optarg);
2179b1aec48SLars Fredriksen 	    break;
2189b1aec48SLars Fredriksen 
21918ebe779SXin LI 	case 'r':
22018ebe779SXin LI 	    if (report_fp != NULL)
22118ebe779SXin LI 		fclose(report_fp);
22218ebe779SXin LI 	    if (report_file != NULL)
22318ebe779SXin LI 		free(report_file);
22418ebe779SXin LI 	    report_file = copy_of(optarg);
22518ebe779SXin LI 	    report_fp = fopen(report_file, "a");
22618ebe779SXin LI 	    if (report_fp != NULL) {
22718ebe779SXin LI 		if (verbose)
22818ebe779SXin LI 		    fprintf(report_fp, "Opening \"%s\"...\n", report_file);
22918ebe779SXin LI 	    } else
23018ebe779SXin LI 		fatal(2, "cannot open \"%s\" for appending", report_file);
2313d793cf1SPeter Wemm 	    break;
2323d793cf1SPeter Wemm 
23301c855caSPeter Wemm 	case 's':
23401c855caSPeter Wemm 	    ++to_stderr;
23501c855caSPeter Wemm 	    break;
23601c855caSPeter Wemm 
23701c855caSPeter Wemm 	case 'S':
23801c855caSPeter Wemm 	    to_log = 0;
23901c855caSPeter Wemm 	    break;
24001c855caSPeter Wemm 
2419b1aec48SLars Fredriksen 	case 't':
24218ebe779SXin LI 	    timeout = atoi(optarg);
2439b1aec48SLars Fredriksen 	    break;
2449b1aec48SLars Fredriksen 
24501c855caSPeter Wemm 	case 'T':
24618ebe779SXin LI 	    if (phone_num != NULL)
24718ebe779SXin LI 		free(phone_num);
24818ebe779SXin LI 	    phone_num = copy_of(optarg);
24901c855caSPeter Wemm 	    break;
25001c855caSPeter Wemm 
25101c855caSPeter Wemm 	case 'U':
25218ebe779SXin LI 	    if (phone_num2 != NULL)
25318ebe779SXin LI 		free(phone_num2);
25418ebe779SXin LI 	    phone_num2 = copy_of(optarg);
25518ebe779SXin LI 	    break;
25618ebe779SXin LI 
25718ebe779SXin LI 	case 'v':
25818ebe779SXin LI 	    ++verbose;
25918ebe779SXin LI 	    break;
26018ebe779SXin LI 
26118ebe779SXin LI 	case 'V':
26218ebe779SXin LI 	    ++Verbose;
26301c855caSPeter Wemm 	    break;
26401c855caSPeter Wemm 
2659b1aec48SLars Fredriksen 	default:
2669b1aec48SLars Fredriksen 	    usage();
2679f65f104SPeter Wemm 	    break;
2689f65f104SPeter Wemm 	}
2699f65f104SPeter Wemm     }
27018ebe779SXin LI 
27118ebe779SXin LI     argc -= optind;
27218ebe779SXin LI     argv += optind;
27318ebe779SXin LI 
2749f65f104SPeter Wemm /*
2759f65f104SPeter Wemm  * Default the report file to the stderr location
2769f65f104SPeter Wemm  */
2779f65f104SPeter Wemm     if (report_fp == NULL)
2789f65f104SPeter Wemm 	report_fp = stderr;
2799b1aec48SLars Fredriksen 
28001c855caSPeter Wemm     if (to_log) {
2819b1aec48SLars Fredriksen 	openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
2829b1aec48SLars Fredriksen 
2839f65f104SPeter Wemm 	if (verbose)
2849b1aec48SLars Fredriksen 	    setlogmask(LOG_UPTO(LOG_INFO));
2859f65f104SPeter Wemm 	else
2869b1aec48SLars Fredriksen 	    setlogmask(LOG_UPTO(LOG_WARNING));
28701c855caSPeter Wemm     }
2889b1aec48SLars Fredriksen 
28901c855caSPeter Wemm     if (chat_file != NULL) {
29018ebe779SXin LI 	if (*argv != NULL)
2919b1aec48SLars Fredriksen 	    usage();
29218ebe779SXin LI 	else {
29318ebe779SXin LI             init();
2949b1aec48SLars Fredriksen 	    do_file(chat_file);
29518ebe779SXin LI 	}
29601c855caSPeter Wemm     } else {
29718ebe779SXin LI 	init();
29818ebe779SXin LI 	while (*argv != NULL && argc > 0) {
29918ebe779SXin LI 	    chat_expect(*argv);
30018ebe779SXin LI 	    argv++;
30118ebe779SXin LI 	    argc--;
3029b1aec48SLars Fredriksen 
30318ebe779SXin LI 	    if (*argv != NULL && argc > 0) {
30418ebe779SXin LI 		chat_send(*argv);
30518ebe779SXin LI 		argv++;
30618ebe779SXin LI 		argc--;
30718ebe779SXin LI 	    }
3089b1aec48SLars Fredriksen 	}
3099b1aec48SLars Fredriksen     }
3109b1aec48SLars Fredriksen 
3119b1aec48SLars Fredriksen     terminate(0);
3123d793cf1SPeter Wemm     return 0;
3139b1aec48SLars Fredriksen }
3149b1aec48SLars Fredriksen 
3159b1aec48SLars Fredriksen /*
3169b1aec48SLars Fredriksen  *  Process a chat script when read from a file.
3179b1aec48SLars Fredriksen  */
3189b1aec48SLars Fredriksen 
319ee6b974cSMark Murray void
320ee6b974cSMark Murray do_file(char *chatfile)
3219b1aec48SLars Fredriksen {
322d7d10053SAlexander Langer     int linect, sendflg;
3239b1aec48SLars Fredriksen     char *sp, *arg, quote;
3249b1aec48SLars Fredriksen     char buf [STR_LEN];
3259b1aec48SLars Fredriksen     FILE *cfp;
3269b1aec48SLars Fredriksen 
327ee6b974cSMark Murray     cfp = fopen (chatfile, "r");
3289f65f104SPeter Wemm     if (cfp == NULL)
329ee6b974cSMark Murray 	fatal(1, "%s -- open failed: %m", chatfile);
3309b1aec48SLars Fredriksen 
3319b1aec48SLars Fredriksen     linect = 0;
3329b1aec48SLars Fredriksen     sendflg = 0;
3339b1aec48SLars Fredriksen 
33401c855caSPeter Wemm     while (fgets(buf, STR_LEN, cfp) != NULL) {
3359b1aec48SLars Fredriksen 	sp = strchr (buf, '\n');
3369b1aec48SLars Fredriksen 	if (sp)
3379b1aec48SLars Fredriksen 	    *sp = '\0';
3389b1aec48SLars Fredriksen 
3399b1aec48SLars Fredriksen 	linect++;
3409b1aec48SLars Fredriksen 	sp = buf;
3413d793cf1SPeter Wemm 
3423d793cf1SPeter Wemm         /* lines starting with '#' are comments. If a real '#'
3433d793cf1SPeter Wemm            is to be expected, it should be quoted .... */
34401c855caSPeter Wemm         if ( *sp == '#' )
34501c855caSPeter Wemm 	    continue;
3463d793cf1SPeter Wemm 
34701c855caSPeter Wemm 	while (*sp != '\0') {
34801c855caSPeter Wemm 	    if (*sp == ' ' || *sp == '\t') {
3499b1aec48SLars Fredriksen 		++sp;
3509b1aec48SLars Fredriksen 		continue;
3519b1aec48SLars Fredriksen 	    }
3529b1aec48SLars Fredriksen 
35301c855caSPeter Wemm 	    if (*sp == '"' || *sp == '\'') {
3549b1aec48SLars Fredriksen 		quote = *sp++;
3559b1aec48SLars Fredriksen 		arg = sp;
35601c855caSPeter Wemm 		while (*sp != quote) {
3579b1aec48SLars Fredriksen 		    if (*sp == '\0')
35801c855caSPeter Wemm 			fatal(1, "unterminated quote (line %d)", linect);
3599b1aec48SLars Fredriksen 
36001c855caSPeter Wemm 		    if (*sp++ == '\\') {
3619b1aec48SLars Fredriksen 			if (*sp != '\0')
3629b1aec48SLars Fredriksen 			    ++sp;
3639b1aec48SLars Fredriksen 		    }
3649b1aec48SLars Fredriksen 		}
3659f65f104SPeter Wemm 	    }
36601c855caSPeter Wemm 	    else {
3679b1aec48SLars Fredriksen 		arg = sp;
3689b1aec48SLars Fredriksen 		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
3699b1aec48SLars Fredriksen 		    ++sp;
3709b1aec48SLars Fredriksen 	    }
3719b1aec48SLars Fredriksen 
3729b1aec48SLars Fredriksen 	    if (*sp != '\0')
3739b1aec48SLars Fredriksen 		*sp++ = '\0';
3749b1aec48SLars Fredriksen 
3759b1aec48SLars Fredriksen 	    if (sendflg)
3769b1aec48SLars Fredriksen 		chat_send (arg);
3779b1aec48SLars Fredriksen 	    else
3789b1aec48SLars Fredriksen 		chat_expect (arg);
3799b1aec48SLars Fredriksen 	    sendflg = !sendflg;
3809b1aec48SLars Fredriksen 	}
3819b1aec48SLars Fredriksen     }
3829b1aec48SLars Fredriksen     fclose (cfp);
3839b1aec48SLars Fredriksen }
3849b1aec48SLars Fredriksen 
3859b1aec48SLars Fredriksen /*
3869b1aec48SLars Fredriksen  *	We got an error parsing the command line.
3879b1aec48SLars Fredriksen  */
388afaeb553SPhilippe Charnier static void
389ee6b974cSMark Murray usage(void)
3909b1aec48SLars Fredriksen {
39118ebe779SXin LI     fprintf(stderr,
39218ebe779SXin LI       "Usage: chat [-esSvV] [-f chat-file] [-r report-file] [-t timeout]\n"
39318ebe779SXin LI       "            [-T phone-number] [-U phone-number2] [chat-script]\n"
39418ebe779SXin LI       "where chat-script has the form:\n"
39518ebe779SXin LI       "            [...[[expect[-send[-expect...]] send expect[-send[-expect]] ...]]]\n");
3969b1aec48SLars Fredriksen     exit(1);
3979b1aec48SLars Fredriksen }
3989b1aec48SLars Fredriksen 
39901c855caSPeter Wemm char line[1024];
4009b1aec48SLars Fredriksen 
4019b1aec48SLars Fredriksen /*
40201c855caSPeter Wemm  * Send a message to syslog and/or stderr.
4039b1aec48SLars Fredriksen  */
404ee6b974cSMark Murray void
4058491f234STim Kientzle chat_logf(const char *fmt, ...)
4069b1aec48SLars Fredriksen {
40701c855caSPeter Wemm     va_list args;
40801c855caSPeter Wemm 
40901c855caSPeter Wemm     va_start(args, fmt);
41001c855caSPeter Wemm     vfmtmsg(line, sizeof(line), fmt, args);
411*544c5e5bSKevin Lo     va_end(args);
41201c855caSPeter Wemm     if (to_log)
41301c855caSPeter Wemm 	syslog(LOG_INFO, "%s", line);
41401c855caSPeter Wemm     if (to_stderr)
41501c855caSPeter Wemm 	fprintf(stderr, "%s\n", line);
4169b1aec48SLars Fredriksen }
4179b1aec48SLars Fredriksen 
4189b1aec48SLars Fredriksen /*
4199b1aec48SLars Fredriksen  *	Print an error message and terminate.
4209b1aec48SLars Fredriksen  */
4219b1aec48SLars Fredriksen 
422ee6b974cSMark Murray void
423ee6b974cSMark Murray fatal(int code, const char *fmt, ...)
4249b1aec48SLars Fredriksen {
42501c855caSPeter Wemm     va_list args;
4269b1aec48SLars Fredriksen 
42701c855caSPeter Wemm     va_start(args, fmt);
42801c855caSPeter Wemm     vfmtmsg(line, sizeof(line), fmt, args);
429*544c5e5bSKevin Lo     va_end(args);
43001c855caSPeter Wemm     if (to_log)
43101c855caSPeter Wemm 	syslog(LOG_ERR, "%s", line);
43201c855caSPeter Wemm     if (to_stderr)
43301c855caSPeter Wemm 	fprintf(stderr, "%s\n", line);
43401c855caSPeter Wemm     terminate(code);
4359b1aec48SLars Fredriksen }
4369b1aec48SLars Fredriksen 
4379b1aec48SLars Fredriksen int alarmed = 0;
4389b1aec48SLars Fredriksen 
439ee6b974cSMark Murray SIGTYPE sigalrm(int signo __unused)
4409b1aec48SLars Fredriksen {
4419b1aec48SLars Fredriksen     int flags;
4429b1aec48SLars Fredriksen 
4439b1aec48SLars Fredriksen     alarm(1);
4449b1aec48SLars Fredriksen     alarmed = 1;		/* Reset alarm to avoid race window */
4459b1aec48SLars Fredriksen     signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
4469b1aec48SLars Fredriksen 
4479b1aec48SLars Fredriksen     if ((flags = fcntl(0, F_GETFL, 0)) == -1)
44801c855caSPeter Wemm 	fatal(2, "Can't get file mode flags on stdin: %m");
44901c855caSPeter Wemm 
4509f65f104SPeter Wemm     if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
45101c855caSPeter Wemm 	fatal(2, "Can't set file mode flags on stdin: %m");
4529b1aec48SLars Fredriksen 
4539b1aec48SLars Fredriksen     if (verbose)
4548491f234STim Kientzle 	chat_logf("alarm");
4559b1aec48SLars Fredriksen }
4569b1aec48SLars Fredriksen 
457ee6b974cSMark Murray SIGTYPE sigint(int signo __unused)
4589b1aec48SLars Fredriksen {
45901c855caSPeter Wemm     fatal(2, "SIGINT");
4609b1aec48SLars Fredriksen }
4619b1aec48SLars Fredriksen 
462ee6b974cSMark Murray SIGTYPE sigterm(int signo __unused)
4639b1aec48SLars Fredriksen {
46401c855caSPeter Wemm     fatal(2, "SIGTERM");
4659b1aec48SLars Fredriksen }
4669b1aec48SLars Fredriksen 
467ee6b974cSMark Murray SIGTYPE sighup(int signo __unused)
4689b1aec48SLars Fredriksen {
46901c855caSPeter Wemm     fatal(2, "SIGHUP");
4709b1aec48SLars Fredriksen }
4719b1aec48SLars Fredriksen 
472ee6b974cSMark Murray void init(void)
4739b1aec48SLars Fredriksen {
4749b1aec48SLars Fredriksen     signal(SIGINT, sigint);
4759b1aec48SLars Fredriksen     signal(SIGTERM, sigterm);
4769b1aec48SLars Fredriksen     signal(SIGHUP, sighup);
4779b1aec48SLars Fredriksen 
4789b1aec48SLars Fredriksen     set_tty_parameters();
4799b1aec48SLars Fredriksen     signal(SIGALRM, sigalrm);
4809b1aec48SLars Fredriksen     alarm(0);
4819b1aec48SLars Fredriksen     alarmed = 0;
4829b1aec48SLars Fredriksen }
4839b1aec48SLars Fredriksen 
484ee6b974cSMark Murray void set_tty_parameters(void)
4859b1aec48SLars Fredriksen {
4869f65f104SPeter Wemm #if defined(get_term_param)
4879f65f104SPeter Wemm     term_parms t;
4889b1aec48SLars Fredriksen 
4899f65f104SPeter Wemm     if (get_term_param (&t) < 0)
49001c855caSPeter Wemm 	fatal(2, "Can't get terminal parameters: %m");
4919b1aec48SLars Fredriksen 
4929b1aec48SLars Fredriksen     saved_tty_parameters = t;
4939b1aec48SLars Fredriksen     have_tty_parameters  = 1;
4949b1aec48SLars Fredriksen 
4959b1aec48SLars Fredriksen     t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
4969b1aec48SLars Fredriksen     t.c_oflag      = 0;
4979b1aec48SLars Fredriksen     t.c_lflag      = 0;
4989f65f104SPeter Wemm     t.c_cc[VERASE] =
4999f65f104SPeter Wemm     t.c_cc[VKILL]  = 0;
5009b1aec48SLars Fredriksen     t.c_cc[VMIN]   = 1;
5019b1aec48SLars Fredriksen     t.c_cc[VTIME]  = 0;
5029b1aec48SLars Fredriksen 
5039f65f104SPeter Wemm     if (set_term_param (&t) < 0)
50401c855caSPeter Wemm 	fatal(2, "Can't set terminal parameters: %m");
5059b1aec48SLars Fredriksen #endif
5069b1aec48SLars Fredriksen }
5079b1aec48SLars Fredriksen 
508ee6b974cSMark Murray void break_sequence(void)
5099b1aec48SLars Fredriksen {
5109b1aec48SLars Fredriksen     tcsendbreak (0, 0);
5119b1aec48SLars Fredriksen }
5129b1aec48SLars Fredriksen 
513ee6b974cSMark Murray void terminate(int status)
5149b1aec48SLars Fredriksen {
5153d793cf1SPeter Wemm     echo_stderr(-1);
51601c855caSPeter Wemm     if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
5173d793cf1SPeter Wemm /*
5183d793cf1SPeter Wemm  * Allow the last of the report string to be gathered before we terminate.
5193d793cf1SPeter Wemm  */
5203d793cf1SPeter Wemm 	if (report_gathering) {
521ee6b974cSMark Murray 	    int c;
522ee6b974cSMark Murray 	    size_t rep_len;
5233d793cf1SPeter Wemm 
5243d793cf1SPeter Wemm 	    rep_len = strlen(report_buffer);
5253d793cf1SPeter Wemm 	    while (rep_len + 1 <= sizeof(report_buffer)) {
5263d793cf1SPeter Wemm 		alarm(1);
5273d793cf1SPeter Wemm 		c = get_char();
5283d793cf1SPeter Wemm 		alarm(0);
5293d793cf1SPeter Wemm 		if (c < 0 || iscntrl(c))
5303d793cf1SPeter Wemm 		    break;
5313d793cf1SPeter Wemm 		report_buffer[rep_len] = c;
5323d793cf1SPeter Wemm 		++rep_len;
5333d793cf1SPeter Wemm 	    }
5343d793cf1SPeter Wemm 	    report_buffer[rep_len] = 0;
5353d793cf1SPeter Wemm 	    fprintf (report_fp, "chat:  %s\n", report_buffer);
5363d793cf1SPeter Wemm 	}
5379f65f104SPeter Wemm 	if (verbose)
5389f65f104SPeter Wemm 	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
5399f65f104SPeter Wemm 	fclose (report_fp);
5409f65f104SPeter Wemm 	report_fp = (FILE *) NULL;
5419f65f104SPeter Wemm     }
5429f65f104SPeter Wemm 
5439f65f104SPeter Wemm #if defined(get_term_param)
54401c855caSPeter Wemm     if (have_tty_parameters) {
5459f65f104SPeter Wemm 	if (set_term_param (&saved_tty_parameters) < 0)
54601c855caSPeter Wemm 	    fatal(2, "Can't restore terminal parameters: %m");
5479f65f104SPeter Wemm     }
5489f65f104SPeter Wemm #endif
5499f65f104SPeter Wemm 
5509b1aec48SLars Fredriksen     exit(status);
5519b1aec48SLars Fredriksen }
5529b1aec48SLars Fredriksen 
5539b1aec48SLars Fredriksen /*
5549b1aec48SLars Fredriksen  *	'Clean up' this string.
5559b1aec48SLars Fredriksen  */
556ee6b974cSMark Murray char *
557ee6b974cSMark Murray clean(char *s, int sending)
5589b1aec48SLars Fredriksen {
5599b1aec48SLars Fredriksen     char temp[STR_LEN], cur_chr;
560ee6b974cSMark Murray     char *s1, *phchar;
5619b1aec48SLars Fredriksen     int add_return = sending;
5629b1aec48SLars Fredriksen #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
5639b1aec48SLars Fredriksen 
5649b1aec48SLars Fredriksen     s1 = temp;
5654e83a8feSKris Kennaway     /* Don't overflow buffer, leave room for chars we append later */
566ee6b974cSMark Murray     while (*s && s1 - temp < (off_t)(sizeof(temp) - 2 - add_return)) {
5679b1aec48SLars Fredriksen 	cur_chr = *s++;
56801c855caSPeter Wemm 	if (cur_chr == '^') {
5699b1aec48SLars Fredriksen 	    cur_chr = *s++;
57001c855caSPeter Wemm 	    if (cur_chr == '\0') {
5719b1aec48SLars Fredriksen 		*s1++ = '^';
5729b1aec48SLars Fredriksen 		break;
5739b1aec48SLars Fredriksen 	    }
5749b1aec48SLars Fredriksen 	    cur_chr &= 0x1F;
57501c855caSPeter Wemm 	    if (cur_chr != 0) {
5769b1aec48SLars Fredriksen 		*s1++ = cur_chr;
5779f65f104SPeter Wemm 	    }
5789b1aec48SLars Fredriksen 	    continue;
5799b1aec48SLars Fredriksen 	}
5809b1aec48SLars Fredriksen 
58101c855caSPeter Wemm 	if (cur_chr != '\\') {
5829b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
5839b1aec48SLars Fredriksen 	    continue;
5849b1aec48SLars Fredriksen 	}
5859b1aec48SLars Fredriksen 
5869b1aec48SLars Fredriksen 	cur_chr = *s++;
58701c855caSPeter Wemm 	if (cur_chr == '\0') {
58801c855caSPeter Wemm 	    if (sending) {
5899b1aec48SLars Fredriksen 		*s1++ = '\\';
5909b1aec48SLars Fredriksen 		*s1++ = '\\';
5919b1aec48SLars Fredriksen 	    }
5929b1aec48SLars Fredriksen 	    break;
5939b1aec48SLars Fredriksen 	}
5949b1aec48SLars Fredriksen 
59501c855caSPeter Wemm 	switch (cur_chr) {
5969b1aec48SLars Fredriksen 	case 'b':
5979b1aec48SLars Fredriksen 	    *s1++ = '\b';
5989b1aec48SLars Fredriksen 	    break;
5999b1aec48SLars Fredriksen 
6009b1aec48SLars Fredriksen 	case 'c':
6019b1aec48SLars Fredriksen 	    if (sending && *s == '\0')
6029b1aec48SLars Fredriksen 		add_return = 0;
6039b1aec48SLars Fredriksen 	    else
6049b1aec48SLars Fredriksen 		*s1++ = cur_chr;
6059b1aec48SLars Fredriksen 	    break;
6069b1aec48SLars Fredriksen 
6079b1aec48SLars Fredriksen 	case '\\':
6089b1aec48SLars Fredriksen 	case 'K':
6099b1aec48SLars Fredriksen 	case 'p':
6109b1aec48SLars Fredriksen 	case 'd':
6119b1aec48SLars Fredriksen 	    if (sending)
6129b1aec48SLars Fredriksen 		*s1++ = '\\';
6139b1aec48SLars Fredriksen 
6149b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
6159b1aec48SLars Fredriksen 	    break;
6169b1aec48SLars Fredriksen 
61701c855caSPeter Wemm 	case 'T':
61801c855caSPeter Wemm 	    if (sending && phone_num) {
61901c855caSPeter Wemm 		for ( phchar = phone_num; *phchar != '\0'; phchar++)
62001c855caSPeter Wemm 		    *s1++ = *phchar;
62101c855caSPeter Wemm 	    }
62201c855caSPeter Wemm 	    else {
62301c855caSPeter Wemm 		*s1++ = '\\';
62401c855caSPeter Wemm 		*s1++ = 'T';
62501c855caSPeter Wemm 	    }
62601c855caSPeter Wemm 	    break;
62701c855caSPeter Wemm 
62801c855caSPeter Wemm 	case 'U':
62901c855caSPeter Wemm 	    if (sending && phone_num2) {
63001c855caSPeter Wemm 		for ( phchar = phone_num2; *phchar != '\0'; phchar++)
63101c855caSPeter Wemm 		    *s1++ = *phchar;
63201c855caSPeter Wemm 	    }
63301c855caSPeter Wemm 	    else {
63401c855caSPeter Wemm 		*s1++ = '\\';
63501c855caSPeter Wemm 		*s1++ = 'U';
63601c855caSPeter Wemm 	    }
63701c855caSPeter Wemm 	    break;
63801c855caSPeter Wemm 
6399b1aec48SLars Fredriksen 	case 'q':
6403d793cf1SPeter Wemm 	    quiet = 1;
6419b1aec48SLars Fredriksen 	    break;
6429b1aec48SLars Fredriksen 
6439b1aec48SLars Fredriksen 	case 'r':
6449b1aec48SLars Fredriksen 	    *s1++ = '\r';
6459b1aec48SLars Fredriksen 	    break;
6469b1aec48SLars Fredriksen 
6479b1aec48SLars Fredriksen 	case 'n':
6489b1aec48SLars Fredriksen 	    *s1++ = '\n';
6499b1aec48SLars Fredriksen 	    break;
6509b1aec48SLars Fredriksen 
6519b1aec48SLars Fredriksen 	case 's':
6529b1aec48SLars Fredriksen 	    *s1++ = ' ';
6539b1aec48SLars Fredriksen 	    break;
6549b1aec48SLars Fredriksen 
6559b1aec48SLars Fredriksen 	case 't':
6569b1aec48SLars Fredriksen 	    *s1++ = '\t';
6579b1aec48SLars Fredriksen 	    break;
6589b1aec48SLars Fredriksen 
6599b1aec48SLars Fredriksen 	case 'N':
66001c855caSPeter Wemm 	    if (sending) {
6619b1aec48SLars Fredriksen 		*s1++ = '\\';
6629b1aec48SLars Fredriksen 		*s1++ = '\0';
6639b1aec48SLars Fredriksen 	    }
6649b1aec48SLars Fredriksen 	    else
6659b1aec48SLars Fredriksen 		*s1++ = 'N';
6669b1aec48SLars Fredriksen 	    break;
6679b1aec48SLars Fredriksen 
6689b1aec48SLars Fredriksen 	default:
66901c855caSPeter Wemm 	    if (isoctal (cur_chr)) {
6709b1aec48SLars Fredriksen 		cur_chr &= 0x07;
67101c855caSPeter Wemm 		if (isoctal (*s)) {
6729b1aec48SLars Fredriksen 		    cur_chr <<= 3;
6739b1aec48SLars Fredriksen 		    cur_chr |= *s++ - '0';
67401c855caSPeter Wemm 		    if (isoctal (*s)) {
6759b1aec48SLars Fredriksen 			cur_chr <<= 3;
6769b1aec48SLars Fredriksen 			cur_chr |= *s++ - '0';
6779b1aec48SLars Fredriksen 		    }
6789b1aec48SLars Fredriksen 		}
6799b1aec48SLars Fredriksen 
68001c855caSPeter Wemm 		if (cur_chr != 0 || sending) {
6819b1aec48SLars Fredriksen 		    if (sending && (cur_chr == '\\' || cur_chr == 0))
6829b1aec48SLars Fredriksen 			*s1++ = '\\';
6839b1aec48SLars Fredriksen 		    *s1++ = cur_chr;
6849b1aec48SLars Fredriksen 		}
6859b1aec48SLars Fredriksen 		break;
6869b1aec48SLars Fredriksen 	    }
6879b1aec48SLars Fredriksen 
6889b1aec48SLars Fredriksen 	    if (sending)
6899b1aec48SLars Fredriksen 		*s1++ = '\\';
6909b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
6919b1aec48SLars Fredriksen 	    break;
6929b1aec48SLars Fredriksen 	}
6939b1aec48SLars Fredriksen     }
6949b1aec48SLars Fredriksen 
6959b1aec48SLars Fredriksen     if (add_return)
6969b1aec48SLars Fredriksen 	*s1++ = '\r';
6979b1aec48SLars Fredriksen 
6989b1aec48SLars Fredriksen     *s1++ = '\0'; /* guarantee closure */
6999b1aec48SLars Fredriksen     *s1++ = '\0'; /* terminate the string */
7009b1aec48SLars Fredriksen     return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
7019b1aec48SLars Fredriksen }
7029b1aec48SLars Fredriksen 
7039b1aec48SLars Fredriksen /*
7043d793cf1SPeter Wemm  * A modified version of 'strtok'. This version skips \ sequences.
7053d793cf1SPeter Wemm  */
7063d793cf1SPeter Wemm 
707ee6b974cSMark Murray char *
708ee6b974cSMark Murray expect_strtok (char *s, const char *term)
7093d793cf1SPeter Wemm {
710ee6b974cSMark Murray     static  char *str   = blank;
7113d793cf1SPeter Wemm     int	    escape_flag = 0;
7123d793cf1SPeter Wemm     char   *result;
71301c855caSPeter Wemm 
7143d793cf1SPeter Wemm /*
7153d793cf1SPeter Wemm  * If a string was specified then do initial processing.
7163d793cf1SPeter Wemm  */
7173d793cf1SPeter Wemm     if (s)
7183d793cf1SPeter Wemm 	str = s;
71901c855caSPeter Wemm 
7203d793cf1SPeter Wemm /*
7213d793cf1SPeter Wemm  * If this is the escape flag then reset it and ignore the character.
7223d793cf1SPeter Wemm  */
7233d793cf1SPeter Wemm     if (*str)
7243d793cf1SPeter Wemm 	result = str;
7253d793cf1SPeter Wemm     else
7263d793cf1SPeter Wemm 	result = (char *) 0;
7273d793cf1SPeter Wemm 
72801c855caSPeter Wemm     while (*str) {
72901c855caSPeter Wemm 	if (escape_flag) {
7303d793cf1SPeter Wemm 	    escape_flag = 0;
7313d793cf1SPeter Wemm 	    ++str;
7323d793cf1SPeter Wemm 	    continue;
7333d793cf1SPeter Wemm 	}
7343d793cf1SPeter Wemm 
73501c855caSPeter Wemm 	if (*str == '\\') {
7363d793cf1SPeter Wemm 	    ++str;
7373d793cf1SPeter Wemm 	    escape_flag = 1;
7383d793cf1SPeter Wemm 	    continue;
7393d793cf1SPeter Wemm 	}
74001c855caSPeter Wemm 
7413d793cf1SPeter Wemm /*
7423d793cf1SPeter Wemm  * If this is not in the termination string, continue.
7433d793cf1SPeter Wemm  */
74401c855caSPeter Wemm 	if (strchr (term, *str) == (char *) 0) {
7453d793cf1SPeter Wemm 	    ++str;
7463d793cf1SPeter Wemm 	    continue;
7473d793cf1SPeter Wemm 	}
74801c855caSPeter Wemm 
7493d793cf1SPeter Wemm /*
7503d793cf1SPeter Wemm  * This is the terminator. Mark the end of the string and stop.
7513d793cf1SPeter Wemm  */
7523d793cf1SPeter Wemm 	*str++ = '\0';
7533d793cf1SPeter Wemm 	break;
7543d793cf1SPeter Wemm     }
7553d793cf1SPeter Wemm     return (result);
7563d793cf1SPeter Wemm }
7573d793cf1SPeter Wemm 
7583d793cf1SPeter Wemm /*
7599b1aec48SLars Fredriksen  * Process the expect string
7609b1aec48SLars Fredriksen  */
7613d793cf1SPeter Wemm 
762ee6b974cSMark Murray void
763ee6b974cSMark Murray chat_expect(char *s)
7649b1aec48SLars Fredriksen {
7653d793cf1SPeter Wemm     char *expect;
7663d793cf1SPeter Wemm     char *reply;
7673d793cf1SPeter Wemm 
76801c855caSPeter Wemm     if (strcmp(s, "HANGUP") == 0) {
7693d793cf1SPeter Wemm 	++hup_next;
7703d793cf1SPeter Wemm         return;
7713d793cf1SPeter Wemm     }
7723d793cf1SPeter Wemm 
77301c855caSPeter Wemm     if (strcmp(s, "ABORT") == 0) {
7749b1aec48SLars Fredriksen 	++abort_next;
7759b1aec48SLars Fredriksen 	return;
7769b1aec48SLars Fredriksen     }
7779b1aec48SLars Fredriksen 
77801c855caSPeter Wemm     if (strcmp(s, "CLR_ABORT") == 0) {
7793d793cf1SPeter Wemm 	++clear_abort_next;
7803d793cf1SPeter Wemm 	return;
7813d793cf1SPeter Wemm     }
7823d793cf1SPeter Wemm 
78301c855caSPeter Wemm     if (strcmp(s, "REPORT") == 0) {
7849f65f104SPeter Wemm 	++report_next;
7859f65f104SPeter Wemm 	return;
7869f65f104SPeter Wemm     }
7879f65f104SPeter Wemm 
78801c855caSPeter Wemm     if (strcmp(s, "CLR_REPORT") == 0) {
7893d793cf1SPeter Wemm 	++clear_report_next;
7903d793cf1SPeter Wemm 	return;
7913d793cf1SPeter Wemm     }
7923d793cf1SPeter Wemm 
79301c855caSPeter Wemm     if (strcmp(s, "TIMEOUT") == 0) {
7949b1aec48SLars Fredriksen 	++timeout_next;
7959b1aec48SLars Fredriksen 	return;
7969b1aec48SLars Fredriksen     }
7979b1aec48SLars Fredriksen 
79801c855caSPeter Wemm     if (strcmp(s, "ECHO") == 0) {
7993d793cf1SPeter Wemm 	++echo_next;
8003d793cf1SPeter Wemm 	return;
8013d793cf1SPeter Wemm     }
80201c855caSPeter Wemm 
80301c855caSPeter Wemm     if (strcmp(s, "SAY") == 0) {
8043d793cf1SPeter Wemm 	++say_next;
8053d793cf1SPeter Wemm 	return;
8063d793cf1SPeter Wemm     }
80701c855caSPeter Wemm 
8083d793cf1SPeter Wemm /*
8093d793cf1SPeter Wemm  * Fetch the expect and reply string.
8103d793cf1SPeter Wemm  */
81101c855caSPeter Wemm     for (;;) {
8123d793cf1SPeter Wemm 	expect = expect_strtok (s, "-");
8133d793cf1SPeter Wemm 	s      = (char *) 0;
8149b1aec48SLars Fredriksen 
8153d793cf1SPeter Wemm 	if (expect == (char *) 0)
8163d793cf1SPeter Wemm 	    return;
8173d793cf1SPeter Wemm 
8183d793cf1SPeter Wemm 	reply = expect_strtok (s, "-");
81901c855caSPeter Wemm 
8203d793cf1SPeter Wemm /*
8213d793cf1SPeter Wemm  * Handle the expect string. If successful then exit.
8223d793cf1SPeter Wemm  */
8233d793cf1SPeter Wemm 	if (get_string (expect))
8243d793cf1SPeter Wemm 	    return;
82501c855caSPeter Wemm 
8263d793cf1SPeter Wemm /*
8273d793cf1SPeter Wemm  * If there is a sub-reply string then send it. Otherwise any condition
8283d793cf1SPeter Wemm  * is terminal.
8293d793cf1SPeter Wemm  */
8303d793cf1SPeter Wemm 	if (reply == (char *) 0 || exit_code != 3)
8319b1aec48SLars Fredriksen 	    break;
8329b1aec48SLars Fredriksen 
8333d793cf1SPeter Wemm 	chat_send (reply);
8349f65f104SPeter Wemm     }
83501c855caSPeter Wemm 
8363d793cf1SPeter Wemm /*
8373d793cf1SPeter Wemm  * The expectation did not occur. This is terminal.
8383d793cf1SPeter Wemm  */
8399b1aec48SLars Fredriksen     if (fail_reason)
8408491f234STim Kientzle 	chat_logf("Failed (%s)", fail_reason);
8419b1aec48SLars Fredriksen     else
8428491f234STim Kientzle 	chat_logf("Failed");
8439f65f104SPeter Wemm     terminate(exit_code);
8449f65f104SPeter Wemm }
8453d793cf1SPeter Wemm 
8463d793cf1SPeter Wemm /*
8473d793cf1SPeter Wemm  * Translate the input character to the appropriate string for printing
8483d793cf1SPeter Wemm  * the data.
8493d793cf1SPeter Wemm  */
8509b1aec48SLars Fredriksen 
851ee6b974cSMark Murray char *
852ee6b974cSMark Murray character(int c)
8539b1aec48SLars Fredriksen {
8549b1aec48SLars Fredriksen     static char string[10];
855ee6b974cSMark Murray     const char *meta;
8569b1aec48SLars Fredriksen 
8579b1aec48SLars Fredriksen     meta = (c & 0x80) ? "M-" : "";
8589b1aec48SLars Fredriksen     c &= 0x7F;
8599b1aec48SLars Fredriksen 
8609b1aec48SLars Fredriksen     if (c < 32)
8619b1aec48SLars Fredriksen 	sprintf(string, "%s^%c", meta, (int)c + '@');
86201c855caSPeter Wemm     else if (c == 127)
8639b1aec48SLars Fredriksen 	sprintf(string, "%s^?", meta);
8649b1aec48SLars Fredriksen     else
8659b1aec48SLars Fredriksen 	sprintf(string, "%s%c", meta, c);
8669b1aec48SLars Fredriksen 
8679b1aec48SLars Fredriksen     return (string);
8689b1aec48SLars Fredriksen }
8699b1aec48SLars Fredriksen 
8709b1aec48SLars Fredriksen /*
8719b1aec48SLars Fredriksen  *  process the reply string
8729b1aec48SLars Fredriksen  */
873ee6b974cSMark Murray void
874ee6b974cSMark Murray chat_send(char *s)
8759b1aec48SLars Fredriksen {
87601c855caSPeter Wemm     if (say_next) {
8773d793cf1SPeter Wemm 	say_next = 0;
8783d793cf1SPeter Wemm 	s = clean(s,0);
879e1b4d8d0SSheldon Hearn 	write(STDERR_FILENO, s, strlen(s));
8803d793cf1SPeter Wemm         free(s);
8813d793cf1SPeter Wemm 	return;
8823d793cf1SPeter Wemm     }
88301c855caSPeter Wemm 
88401c855caSPeter Wemm     if (hup_next) {
8853d793cf1SPeter Wemm         hup_next = 0;
8863d793cf1SPeter Wemm 	if (strcmp(s, "OFF") == 0)
8873d793cf1SPeter Wemm            signal(SIGHUP, SIG_IGN);
8883d793cf1SPeter Wemm         else
8893d793cf1SPeter Wemm            signal(SIGHUP, sighup);
8903d793cf1SPeter Wemm         return;
8913d793cf1SPeter Wemm     }
89201c855caSPeter Wemm 
89301c855caSPeter Wemm     if (echo_next) {
8943d793cf1SPeter Wemm 	echo_next = 0;
8953d793cf1SPeter Wemm 	echo = (strcmp(s, "ON") == 0);
8963d793cf1SPeter Wemm 	return;
8973d793cf1SPeter Wemm     }
89801c855caSPeter Wemm 
89901c855caSPeter Wemm     if (abort_next) {
9009b1aec48SLars Fredriksen 	char *s1;
9019b1aec48SLars Fredriksen 
9029b1aec48SLars Fredriksen 	abort_next = 0;
9039b1aec48SLars Fredriksen 
9049b1aec48SLars Fredriksen 	if (n_aborts >= MAX_ABORTS)
90501c855caSPeter Wemm 	    fatal(2, "Too many ABORT strings");
9069b1aec48SLars Fredriksen 
9079b1aec48SLars Fredriksen 	s1 = clean(s, 0);
9089b1aec48SLars Fredriksen 
9099f65f104SPeter Wemm 	if (strlen(s1) > strlen(s)
9109f65f104SPeter Wemm 	    || strlen(s1) + 1 > sizeof(fail_buffer))
91101c855caSPeter Wemm 	    fatal(1, "Illegal or too-long ABORT string ('%v')", s);
9129b1aec48SLars Fredriksen 
9139b1aec48SLars Fredriksen 	abort_string[n_aborts++] = s1;
9149b1aec48SLars Fredriksen 
9159b1aec48SLars Fredriksen 	if (verbose)
9168491f234STim Kientzle 	    chat_logf("abort on (%v)", s);
9179f65f104SPeter Wemm 	return;
9189b1aec48SLars Fredriksen     }
9199f65f104SPeter Wemm 
92001c855caSPeter Wemm     if (clear_abort_next) {
9213d793cf1SPeter Wemm 	char *s1;
9223d793cf1SPeter Wemm 	int   i;
9233d793cf1SPeter Wemm         int   old_max;
9243d793cf1SPeter Wemm 	int   pack = 0;
9253d793cf1SPeter Wemm 
9263d793cf1SPeter Wemm 	clear_abort_next = 0;
9273d793cf1SPeter Wemm 
9283d793cf1SPeter Wemm 	s1 = clean(s, 0);
9293d793cf1SPeter Wemm 
9303d793cf1SPeter Wemm 	if (strlen(s1) > strlen(s)
9313d793cf1SPeter Wemm 	    || strlen(s1) + 1 > sizeof(fail_buffer))
93201c855caSPeter Wemm 	    fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
9333d793cf1SPeter Wemm 
9343d793cf1SPeter Wemm         old_max = n_aborts;
93501c855caSPeter Wemm 	for (i=0; i < n_aborts; i++) {
93601c855caSPeter Wemm 	    if ( strcmp(s1,abort_string[i]) == 0 ) {
9373d793cf1SPeter Wemm 		free(abort_string[i]);
9383d793cf1SPeter Wemm 		abort_string[i] = NULL;
9393d793cf1SPeter Wemm 		pack++;
9403d793cf1SPeter Wemm 		n_aborts--;
9413d793cf1SPeter Wemm 		if (verbose)
9428491f234STim Kientzle 		    chat_logf("clear abort on (%v)", s);
9433d793cf1SPeter Wemm 	    }
9443d793cf1SPeter Wemm 	}
9453d793cf1SPeter Wemm         free(s1);
94601c855caSPeter Wemm 	if (pack)
94701c855caSPeter Wemm 	    pack_array(abort_string,old_max);
9483d793cf1SPeter Wemm 	return;
9493d793cf1SPeter Wemm     }
9503d793cf1SPeter Wemm 
95101c855caSPeter Wemm     if (report_next) {
9529f65f104SPeter Wemm 	char *s1;
9539f65f104SPeter Wemm 
9549f65f104SPeter Wemm 	report_next = 0;
9559f65f104SPeter Wemm 	if (n_reports >= MAX_REPORTS)
95601c855caSPeter Wemm 	    fatal(2, "Too many REPORT strings");
9579f65f104SPeter Wemm 
9589f65f104SPeter Wemm 	s1 = clean(s, 0);
9599f65f104SPeter Wemm 
9609f65f104SPeter Wemm 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
96101c855caSPeter Wemm 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
9629f65f104SPeter Wemm 
9639f65f104SPeter Wemm 	report_string[n_reports++] = s1;
9649f65f104SPeter Wemm 
9659f65f104SPeter Wemm 	if (verbose)
9668491f234STim Kientzle 	    chat_logf("report (%v)", s);
9679f65f104SPeter Wemm 	return;
9689f65f104SPeter Wemm     }
9699f65f104SPeter Wemm 
97001c855caSPeter Wemm     if (clear_report_next) {
9713d793cf1SPeter Wemm 	char *s1;
9723d793cf1SPeter Wemm 	int   i;
9733d793cf1SPeter Wemm 	int   old_max;
9743d793cf1SPeter Wemm 	int   pack = 0;
9753d793cf1SPeter Wemm 
9763d793cf1SPeter Wemm 	clear_report_next = 0;
9773d793cf1SPeter Wemm 
9783d793cf1SPeter Wemm 	s1 = clean(s, 0);
9793d793cf1SPeter Wemm 
9803d793cf1SPeter Wemm 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
98101c855caSPeter Wemm 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
9823d793cf1SPeter Wemm 
9833d793cf1SPeter Wemm 	old_max = n_reports;
98401c855caSPeter Wemm 	for (i=0; i < n_reports; i++) {
98501c855caSPeter Wemm 	    if ( strcmp(s1,report_string[i]) == 0 ) {
9863d793cf1SPeter Wemm 		free(report_string[i]);
9873d793cf1SPeter Wemm 		report_string[i] = NULL;
9883d793cf1SPeter Wemm 		pack++;
9893d793cf1SPeter Wemm 		n_reports--;
9903d793cf1SPeter Wemm 		if (verbose)
9918491f234STim Kientzle 		    chat_logf("clear report (%v)", s);
9923d793cf1SPeter Wemm 	    }
9933d793cf1SPeter Wemm 	}
9943d793cf1SPeter Wemm         free(s1);
99501c855caSPeter Wemm         if (pack)
99601c855caSPeter Wemm 	    pack_array(report_string,old_max);
9973d793cf1SPeter Wemm 
9983d793cf1SPeter Wemm 	return;
9993d793cf1SPeter Wemm     }
10003d793cf1SPeter Wemm 
100101c855caSPeter Wemm     if (timeout_next) {
10029b1aec48SLars Fredriksen 	timeout_next = 0;
10039b1aec48SLars Fredriksen 	timeout = atoi(s);
10049b1aec48SLars Fredriksen 
10059b1aec48SLars Fredriksen 	if (timeout <= 0)
10069b1aec48SLars Fredriksen 	    timeout = DEFAULT_CHAT_TIMEOUT;
10079b1aec48SLars Fredriksen 
10089b1aec48SLars Fredriksen 	if (verbose)
10098491f234STim Kientzle 	    chat_logf("timeout set to %d seconds", timeout);
101001c855caSPeter Wemm 
10119f65f104SPeter Wemm 	return;
10129f65f104SPeter Wemm     }
10139f65f104SPeter Wemm 
10149f65f104SPeter Wemm     if (strcmp(s, "EOT") == 0)
1015ee6b974cSMark Murray 	s = strdup("^D\\c");
101601c855caSPeter Wemm     else if (strcmp(s, "BREAK") == 0)
1017ee6b974cSMark Murray 	s = strdup("\\K\\c");
10189f65f104SPeter Wemm 
10199b1aec48SLars Fredriksen     if (!put_string(s))
102001c855caSPeter Wemm 	fatal(1, "Failed");
10219b1aec48SLars Fredriksen }
10229b1aec48SLars Fredriksen 
1023ee6b974cSMark Murray int
1024ee6b974cSMark Murray get_char(void)
10259b1aec48SLars Fredriksen {
10269b1aec48SLars Fredriksen     int status;
10279b1aec48SLars Fredriksen     char c;
10289b1aec48SLars Fredriksen 
1029e1b4d8d0SSheldon Hearn     status = read(STDIN_FILENO, &c, 1);
10309b1aec48SLars Fredriksen 
103101c855caSPeter Wemm     switch (status) {
10329b1aec48SLars Fredriksen     case 1:
10339b1aec48SLars Fredriksen 	return ((int)c & 0x7F);
10349b1aec48SLars Fredriksen 
10359b1aec48SLars Fredriksen     default:
10368491f234STim Kientzle 	chat_logf("warning: read() on stdin returned %d", status);
10379b1aec48SLars Fredriksen 
10389b1aec48SLars Fredriksen     case -1:
10399b1aec48SLars Fredriksen 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
104001c855caSPeter Wemm 	    fatal(2, "Can't get file mode flags on stdin: %m");
104101c855caSPeter Wemm 
10429f65f104SPeter Wemm 	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
104301c855caSPeter Wemm 	    fatal(2, "Can't set file mode flags on stdin: %m");
10449b1aec48SLars Fredriksen 
10459b1aec48SLars Fredriksen 	return (-1);
10469b1aec48SLars Fredriksen     }
10479b1aec48SLars Fredriksen }
10489b1aec48SLars Fredriksen 
1049ee6b974cSMark Murray int put_char(int c)
10509b1aec48SLars Fredriksen {
10519b1aec48SLars Fredriksen     int status;
10529f65f104SPeter Wemm     char ch = c;
10539b1aec48SLars Fredriksen 
10549f65f104SPeter Wemm     usleep(10000);		/* inter-character typing delay (?) */
10559b1aec48SLars Fredriksen 
1056e1b4d8d0SSheldon Hearn     status = write(STDOUT_FILENO, &ch, 1);
10579b1aec48SLars Fredriksen 
105801c855caSPeter Wemm     switch (status) {
10599b1aec48SLars Fredriksen     case 1:
10609b1aec48SLars Fredriksen 	return (0);
10619b1aec48SLars Fredriksen 
10629b1aec48SLars Fredriksen     default:
10638491f234STim Kientzle 	chat_logf("warning: write() on stdout returned %d", status);
10649b1aec48SLars Fredriksen 
10659b1aec48SLars Fredriksen     case -1:
10669b1aec48SLars Fredriksen 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
106701c855caSPeter Wemm 	    fatal(2, "Can't get file mode flags on stdin, %m");
106801c855caSPeter Wemm 
10699f65f104SPeter Wemm 	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
107001c855caSPeter Wemm 	    fatal(2, "Can't set file mode flags on stdin: %m");
10719b1aec48SLars Fredriksen 
10729b1aec48SLars Fredriksen 	return (-1);
10739b1aec48SLars Fredriksen     }
10749b1aec48SLars Fredriksen }
10759b1aec48SLars Fredriksen 
1076ee6b974cSMark Murray int
1077ee6b974cSMark Murray write_char(int c)
10789b1aec48SLars Fredriksen {
107901c855caSPeter Wemm     if (alarmed || put_char(c) < 0) {
10809f65f104SPeter Wemm 	alarm(0);
10819f65f104SPeter Wemm 	alarmed = 0;
10829b1aec48SLars Fredriksen 
108301c855caSPeter Wemm 	if (verbose) {
10849b1aec48SLars Fredriksen 	    if (errno == EINTR || errno == EWOULDBLOCK)
10858491f234STim Kientzle 		chat_logf(" -- write timed out");
10869b1aec48SLars Fredriksen 	    else
10878491f234STim Kientzle 		chat_logf(" -- write failed: %m");
10889f65f104SPeter Wemm 	}
10899b1aec48SLars Fredriksen 	return (0);
10909b1aec48SLars Fredriksen     }
10919b1aec48SLars Fredriksen     return (1);
10929b1aec48SLars Fredriksen }
10939b1aec48SLars Fredriksen 
1094ee6b974cSMark Murray int
1095ee6b974cSMark Murray put_string(char *s)
10969b1aec48SLars Fredriksen {
10973d793cf1SPeter Wemm     quiet = 0;
10989b1aec48SLars Fredriksen     s = clean(s, 1);
10999b1aec48SLars Fredriksen 
1100ee6b974cSMark Murray     if (verbose)
11018491f234STim Kientzle         chat_logf("send (%v)", quiet ? "??????" : s);
11029b1aec48SLars Fredriksen 
11039b1aec48SLars Fredriksen     alarm(timeout); alarmed = 0;
11049b1aec48SLars Fredriksen 
110501c855caSPeter Wemm     while (*s) {
1106ee6b974cSMark Murray 	char c = *s++;
11079b1aec48SLars Fredriksen 
110801c855caSPeter Wemm 	if (c != '\\') {
11099b1aec48SLars Fredriksen 	    if (!write_char (c))
11109b1aec48SLars Fredriksen 		return 0;
11119b1aec48SLars Fredriksen 	    continue;
11129b1aec48SLars Fredriksen 	}
11139b1aec48SLars Fredriksen 
11149b1aec48SLars Fredriksen 	c = *s++;
111501c855caSPeter Wemm 	switch (c) {
11169b1aec48SLars Fredriksen 	case 'd':
11179b1aec48SLars Fredriksen 	    sleep(1);
11189b1aec48SLars Fredriksen 	    break;
11199b1aec48SLars Fredriksen 
11209b1aec48SLars Fredriksen 	case 'K':
11219b1aec48SLars Fredriksen 	    break_sequence();
11229b1aec48SLars Fredriksen 	    break;
11239b1aec48SLars Fredriksen 
11249b1aec48SLars Fredriksen 	case 'p':
11259f65f104SPeter Wemm 	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
11269b1aec48SLars Fredriksen 	    break;
11279b1aec48SLars Fredriksen 
11289b1aec48SLars Fredriksen 	default:
11299b1aec48SLars Fredriksen 	    if (!write_char (c))
11309b1aec48SLars Fredriksen 		return 0;
11319b1aec48SLars Fredriksen 	    break;
11329b1aec48SLars Fredriksen 	}
11339b1aec48SLars Fredriksen     }
11349b1aec48SLars Fredriksen 
11359b1aec48SLars Fredriksen     alarm(0);
11369b1aec48SLars Fredriksen     alarmed = 0;
11379b1aec48SLars Fredriksen     return (1);
11389b1aec48SLars Fredriksen }
11399b1aec48SLars Fredriksen 
11409b1aec48SLars Fredriksen /*
11413d793cf1SPeter Wemm  *	Echo a character to stderr.
11423d793cf1SPeter Wemm  *	When called with -1, a '\n' character is generated when
11433d793cf1SPeter Wemm  *	the cursor is not at the beginning of a line.
11443d793cf1SPeter Wemm  */
1145ee6b974cSMark Murray void
1146ee6b974cSMark Murray echo_stderr(int n)
11473d793cf1SPeter Wemm {
11483d793cf1SPeter Wemm     static int need_lf;
11493d793cf1SPeter Wemm     char *s;
11503d793cf1SPeter Wemm 
115101c855caSPeter Wemm     switch (n) {
11523d793cf1SPeter Wemm     case '\r':		/* ignore '\r' */
11533d793cf1SPeter Wemm 	break;
11543d793cf1SPeter Wemm     case -1:
11553d793cf1SPeter Wemm 	if (need_lf == 0)
11563d793cf1SPeter Wemm 	    break;
115793b0017fSPhilippe Charnier 	/* FALLTHROUGH */
11583d793cf1SPeter Wemm     case '\n':
1159e1b4d8d0SSheldon Hearn 	write(STDERR_FILENO, "\n", 1);
11603d793cf1SPeter Wemm 	need_lf = 0;
11613d793cf1SPeter Wemm 	break;
11623d793cf1SPeter Wemm     default:
11633d793cf1SPeter Wemm 	s = character(n);
1164e1b4d8d0SSheldon Hearn 	write(STDERR_FILENO, s, strlen(s));
11653d793cf1SPeter Wemm 	need_lf = 1;
11663d793cf1SPeter Wemm 	break;
11673d793cf1SPeter Wemm     }
11683d793cf1SPeter Wemm }
11693d793cf1SPeter Wemm 
11703d793cf1SPeter Wemm /*
11719b1aec48SLars Fredriksen  *	'Wait for' this string to appear on this file descriptor.
11729b1aec48SLars Fredriksen  */
1173ee6b974cSMark Murray int
1174ee6b974cSMark Murray get_string(char *string)
11759b1aec48SLars Fredriksen {
11769b1aec48SLars Fredriksen     char temp[STR_LEN];
1177ee6b974cSMark Murray     int c, printed = 0;
1178ee6b974cSMark Murray     size_t len, minlen;
1179ee6b974cSMark Murray     char *s = temp, *end = s + STR_LEN;
118001c855caSPeter Wemm     char *logged = temp;
11819b1aec48SLars Fredriksen 
11829b1aec48SLars Fredriksen     fail_reason = (char *)0;
11834e83a8feSKris Kennaway 
11844e83a8feSKris Kennaway     if (strlen(string) > STR_LEN) {
11858491f234STim Kientzle 	chat_logf("expect string is too long");
11864e83a8feSKris Kennaway 	exit_code = 1;
11874e83a8feSKris Kennaway 	return 0;
11884e83a8feSKris Kennaway     }
11894e83a8feSKris Kennaway 
11909b1aec48SLars Fredriksen     string = clean(string, 0);
11919b1aec48SLars Fredriksen     len = strlen(string);
11929b1aec48SLars Fredriksen     minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
11939b1aec48SLars Fredriksen 
11949b1aec48SLars Fredriksen     if (verbose)
11958491f234STim Kientzle 	chat_logf("expect (%v)", string);
11969b1aec48SLars Fredriksen 
119701c855caSPeter Wemm     if (len == 0) {
11989b1aec48SLars Fredriksen 	if (verbose)
11998491f234STim Kientzle 	    chat_logf("got it");
12009b1aec48SLars Fredriksen 	return (1);
12019b1aec48SLars Fredriksen     }
12029b1aec48SLars Fredriksen 
12039f65f104SPeter Wemm     alarm(timeout);
12049f65f104SPeter Wemm     alarmed = 0;
12059b1aec48SLars Fredriksen 
120601c855caSPeter Wemm     while ( ! alarmed && (c = get_char()) >= 0) {
12079f65f104SPeter Wemm 	int n, abort_len, report_len;
12089b1aec48SLars Fredriksen 
12093d793cf1SPeter Wemm 	if (echo)
12103d793cf1SPeter Wemm 	    echo_stderr(c);
121101c855caSPeter Wemm 	if (verbose && c == '\n') {
121201c855caSPeter Wemm 	    if (s == logged)
12138491f234STim Kientzle 		chat_logf("");	/* blank line */
12149b1aec48SLars Fredriksen 	    else
12158491f234STim Kientzle 		chat_logf("%0.*v", s - logged, logged);
121601c855caSPeter Wemm 	    logged = s + 1;
12173d793cf1SPeter Wemm 	}
12183d793cf1SPeter Wemm 
12199b1aec48SLars Fredriksen 	*s++ = c;
12209b1aec48SLars Fredriksen 
122101c855caSPeter Wemm 	if (verbose && s >= logged + 80) {
12228491f234STim Kientzle 	    chat_logf("%0.*v", s - logged, logged);
122301c855caSPeter Wemm 	    logged = s;
122401c855caSPeter Wemm 	}
122501c855caSPeter Wemm 
122601c855caSPeter Wemm 	if (Verbose) {
122701c855caSPeter Wemm 	   if (c == '\n')
122801c855caSPeter Wemm 	       fputc( '\n', stderr );
122901c855caSPeter Wemm 	   else if (c != '\r')
123001c855caSPeter Wemm 	       fprintf( stderr, "%s", character(c) );
123101c855caSPeter Wemm 	}
123201c855caSPeter Wemm 
123301c855caSPeter Wemm 	if (!report_gathering) {
123401c855caSPeter Wemm 	    for (n = 0; n < n_reports; ++n) {
12359f65f104SPeter Wemm 		if ((report_string[n] != (char*) NULL) &&
12369f65f104SPeter Wemm 		    s - temp >= (report_len = strlen(report_string[n])) &&
123701c855caSPeter Wemm 		    strncmp(s - report_len, report_string[n], report_len) == 0) {
12389f65f104SPeter Wemm 		    time_t time_now   = time ((time_t*) NULL);
12399f65f104SPeter Wemm 		    struct tm* tm_now = localtime (&time_now);
12409f65f104SPeter Wemm 
12419f65f104SPeter Wemm 		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
12429f65f104SPeter Wemm 		    strcat (report_buffer, report_string[n]);
12439f65f104SPeter Wemm 
12449f65f104SPeter Wemm 		    report_string[n] = (char *) NULL;
12459f65f104SPeter Wemm 		    report_gathering = 1;
12469f65f104SPeter Wemm 		    break;
12479f65f104SPeter Wemm 		}
12489f65f104SPeter Wemm 	    }
12499f65f104SPeter Wemm 	}
125001c855caSPeter Wemm 	else {
125101c855caSPeter Wemm 	    if (!iscntrl (c)) {
12529f65f104SPeter Wemm 		int rep_len = strlen (report_buffer);
12539f65f104SPeter Wemm 		report_buffer[rep_len]     = c;
12549f65f104SPeter Wemm 		report_buffer[rep_len + 1] = '\0';
12559f65f104SPeter Wemm 	    }
125601c855caSPeter Wemm 	    else {
12579f65f104SPeter Wemm 		report_gathering = 0;
12589f65f104SPeter Wemm 		fprintf (report_fp, "chat:  %s\n", report_buffer);
12599f65f104SPeter Wemm 	    }
12609f65f104SPeter Wemm 	}
12619b1aec48SLars Fredriksen 
1262ee6b974cSMark Murray 	if ((size_t)(s - temp) >= len &&
12633d793cf1SPeter Wemm 	    c == string[len - 1] &&
126401c855caSPeter Wemm 	    strncmp(s - len, string, len) == 0) {
126501c855caSPeter Wemm 	    if (verbose) {
126601c855caSPeter Wemm 		if (s > logged)
12678491f234STim Kientzle 		    chat_logf("%0.*v", s - logged, logged);
12688491f234STim Kientzle 		chat_logf(" -- got it\n");
12693d793cf1SPeter Wemm 	    }
12703d793cf1SPeter Wemm 
12713d793cf1SPeter Wemm 	    alarm(0);
12723d793cf1SPeter Wemm 	    alarmed = 0;
12733d793cf1SPeter Wemm 	    return (1);
12743d793cf1SPeter Wemm 	}
12753d793cf1SPeter Wemm 
127601c855caSPeter Wemm 	for (n = 0; n < n_aborts; ++n) {
12773d793cf1SPeter Wemm 	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
127801c855caSPeter Wemm 		strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
127901c855caSPeter Wemm 		if (verbose) {
128001c855caSPeter Wemm 		    if (s > logged)
12818491f234STim Kientzle 			chat_logf("%0.*v", s - logged, logged);
12828491f234STim Kientzle 		    chat_logf(" -- failed");
12833d793cf1SPeter Wemm 		}
12843d793cf1SPeter Wemm 
12853d793cf1SPeter Wemm 		alarm(0);
12863d793cf1SPeter Wemm 		alarmed = 0;
12873d793cf1SPeter Wemm 		exit_code = n + 4;
12883d793cf1SPeter Wemm 		strcpy(fail_reason = fail_buffer, abort_string[n]);
12893d793cf1SPeter Wemm 		return (0);
12903d793cf1SPeter Wemm 	    }
12913d793cf1SPeter Wemm 	}
12923d793cf1SPeter Wemm 
129301c855caSPeter Wemm 	if (s >= end) {
129401c855caSPeter Wemm 	    if (logged < s - minlen) {
12958491f234STim Kientzle 		chat_logf("%0.*v", s - logged, logged);
129601c855caSPeter Wemm 		logged = s;
129701c855caSPeter Wemm 	    }
129801c855caSPeter Wemm 	    s -= minlen;
129901c855caSPeter Wemm 	    memmove(temp, s, minlen);
130001c855caSPeter Wemm 	    logged = temp + (logged - s);
13019b1aec48SLars Fredriksen 	    s = temp + minlen;
13029b1aec48SLars Fredriksen 	}
13039b1aec48SLars Fredriksen 
13049b1aec48SLars Fredriksen 	if (alarmed && verbose)
13058491f234STim Kientzle 	    chat_logf("warning: alarm synchronization problem");
13069f65f104SPeter Wemm     }
13079b1aec48SLars Fredriksen 
13089b1aec48SLars Fredriksen     alarm(0);
13099b1aec48SLars Fredriksen 
131001c855caSPeter Wemm     if (verbose && printed) {
13119b1aec48SLars Fredriksen 	if (alarmed)
13128491f234STim Kientzle 	    chat_logf(" -- read timed out");
13139b1aec48SLars Fredriksen 	else
13148491f234STim Kientzle 	    chat_logf(" -- read failed: %m");
13159b1aec48SLars Fredriksen     }
13169b1aec48SLars Fredriksen 
13179f65f104SPeter Wemm     exit_code = 3;
13189b1aec48SLars Fredriksen     alarmed   = 0;
13199b1aec48SLars Fredriksen     return (0);
13209b1aec48SLars Fredriksen }
13219b1aec48SLars Fredriksen 
13223d793cf1SPeter Wemm void
1323ee6b974cSMark Murray pack_array(char **array, int end)
13243d793cf1SPeter Wemm {
13253d793cf1SPeter Wemm     int i, j;
13263d793cf1SPeter Wemm 
13273d793cf1SPeter Wemm     for (i = 0; i < end; i++) {
13283d793cf1SPeter Wemm 	if (array[i] == NULL) {
13293d793cf1SPeter Wemm 	    for (j = i+1; j < end; ++j)
13303d793cf1SPeter Wemm 		if (array[j] != NULL)
13313d793cf1SPeter Wemm 		    array[i++] = array[j];
13323d793cf1SPeter Wemm 	    for (; i < end; ++i)
13333d793cf1SPeter Wemm 		array[i] = NULL;
13343d793cf1SPeter Wemm 	    break;
13353d793cf1SPeter Wemm 	}
13363d793cf1SPeter Wemm     }
13373d793cf1SPeter Wemm }
133801c855caSPeter Wemm 
133901c855caSPeter Wemm /*
134001c855caSPeter Wemm  * vfmtmsg - format a message into a buffer.  Like vsprintf except we
134101c855caSPeter Wemm  * also specify the length of the output buffer, and we handle the
134201c855caSPeter Wemm  * %m (error message) format.
134301c855caSPeter Wemm  * Doesn't do floating-point formats.
134401c855caSPeter Wemm  * Returns the number of chars put into buf.
134501c855caSPeter Wemm  */
134601c855caSPeter Wemm #define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)
134701c855caSPeter Wemm 
134801c855caSPeter Wemm int
1349ee6b974cSMark Murray vfmtmsg(char *buf, int buflen, const char *fmt, va_list args)
135001c855caSPeter Wemm {
135101c855caSPeter Wemm     int c, i, n;
135201c855caSPeter Wemm     int width, prec, fillch;
135301c855caSPeter Wemm     int base, len, neg, quoted;
135401c855caSPeter Wemm     unsigned long val = 0;
135501c855caSPeter Wemm     char *str, *buf0;
135601c855caSPeter Wemm     const char *f;
135701c855caSPeter Wemm     unsigned char *p;
135801c855caSPeter Wemm     char num[32];
135901c855caSPeter Wemm     static char hexchars[] = "0123456789abcdef";
136001c855caSPeter Wemm 
136101c855caSPeter Wemm     buf0 = buf;
136201c855caSPeter Wemm     --buflen;
136301c855caSPeter Wemm     while (buflen > 0) {
136401c855caSPeter Wemm 	for (f = fmt; *f != '%' && *f != 0; ++f)
136501c855caSPeter Wemm 	    ;
136601c855caSPeter Wemm 	if (f > fmt) {
136701c855caSPeter Wemm 	    len = f - fmt;
136801c855caSPeter Wemm 	    if (len > buflen)
136901c855caSPeter Wemm 		len = buflen;
137001c855caSPeter Wemm 	    memcpy(buf, fmt, len);
137101c855caSPeter Wemm 	    buf += len;
137201c855caSPeter Wemm 	    buflen -= len;
137301c855caSPeter Wemm 	    fmt = f;
137401c855caSPeter Wemm 	}
137501c855caSPeter Wemm 	if (*fmt == 0)
137601c855caSPeter Wemm 	    break;
137701c855caSPeter Wemm 	c = *++fmt;
137801c855caSPeter Wemm 	width = prec = 0;
137901c855caSPeter Wemm 	fillch = ' ';
138001c855caSPeter Wemm 	if (c == '0') {
138101c855caSPeter Wemm 	    fillch = '0';
138201c855caSPeter Wemm 	    c = *++fmt;
138301c855caSPeter Wemm 	}
138401c855caSPeter Wemm 	if (c == '*') {
138501c855caSPeter Wemm 	    width = va_arg(args, int);
138601c855caSPeter Wemm 	    c = *++fmt;
138701c855caSPeter Wemm 	} else {
138801c855caSPeter Wemm 	    while (isdigit(c)) {
138901c855caSPeter Wemm 		width = width * 10 + c - '0';
139001c855caSPeter Wemm 		c = *++fmt;
139101c855caSPeter Wemm 	    }
139201c855caSPeter Wemm 	}
139301c855caSPeter Wemm 	if (c == '.') {
139401c855caSPeter Wemm 	    c = *++fmt;
139501c855caSPeter Wemm 	    if (c == '*') {
139601c855caSPeter Wemm 		prec = va_arg(args, int);
139701c855caSPeter Wemm 		c = *++fmt;
139801c855caSPeter Wemm 	    } else {
139901c855caSPeter Wemm 		while (isdigit(c)) {
140001c855caSPeter Wemm 		    prec = prec * 10 + c - '0';
140101c855caSPeter Wemm 		    c = *++fmt;
140201c855caSPeter Wemm 		}
140301c855caSPeter Wemm 	    }
140401c855caSPeter Wemm 	}
140501c855caSPeter Wemm 	str = 0;
140601c855caSPeter Wemm 	base = 0;
140701c855caSPeter Wemm 	neg = 0;
140801c855caSPeter Wemm 	++fmt;
140901c855caSPeter Wemm 	switch (c) {
141001c855caSPeter Wemm 	case 'd':
141101c855caSPeter Wemm 	    i = va_arg(args, int);
141201c855caSPeter Wemm 	    if (i < 0) {
141301c855caSPeter Wemm 		neg = 1;
141401c855caSPeter Wemm 		val = -i;
141501c855caSPeter Wemm 	    } else
141601c855caSPeter Wemm 		val = i;
141701c855caSPeter Wemm 	    base = 10;
141801c855caSPeter Wemm 	    break;
141901c855caSPeter Wemm 	case 'o':
142001c855caSPeter Wemm 	    val = va_arg(args, unsigned int);
142101c855caSPeter Wemm 	    base = 8;
142201c855caSPeter Wemm 	    break;
142301c855caSPeter Wemm 	case 'x':
142401c855caSPeter Wemm 	    val = va_arg(args, unsigned int);
142501c855caSPeter Wemm 	    base = 16;
142601c855caSPeter Wemm 	    break;
142701c855caSPeter Wemm 	case 'p':
142801c855caSPeter Wemm 	    val = (unsigned long) va_arg(args, void *);
142901c855caSPeter Wemm 	    base = 16;
143001c855caSPeter Wemm 	    neg = 2;
143101c855caSPeter Wemm 	    break;
143201c855caSPeter Wemm 	case 's':
143301c855caSPeter Wemm 	    str = va_arg(args, char *);
143401c855caSPeter Wemm 	    break;
143501c855caSPeter Wemm 	case 'c':
143601c855caSPeter Wemm 	    num[0] = va_arg(args, int);
143701c855caSPeter Wemm 	    num[1] = 0;
143801c855caSPeter Wemm 	    str = num;
143901c855caSPeter Wemm 	    break;
144001c855caSPeter Wemm 	case 'm':
144101c855caSPeter Wemm 	    str = strerror(errno);
144201c855caSPeter Wemm 	    break;
144301c855caSPeter Wemm 	case 'v':		/* "visible" string */
144401c855caSPeter Wemm 	case 'q':		/* quoted string */
144501c855caSPeter Wemm 	    quoted = c == 'q';
144601c855caSPeter Wemm 	    p = va_arg(args, unsigned char *);
144701c855caSPeter Wemm 	    if (fillch == '0' && prec > 0) {
144801c855caSPeter Wemm 		n = prec;
144901c855caSPeter Wemm 	    } else {
145001c855caSPeter Wemm 		n = strlen((char *)p);
145101c855caSPeter Wemm 		if (prec > 0 && prec < n)
145201c855caSPeter Wemm 		    n = prec;
145301c855caSPeter Wemm 	    }
145401c855caSPeter Wemm 	    while (n > 0 && buflen > 0) {
145501c855caSPeter Wemm 		c = *p++;
145601c855caSPeter Wemm 		--n;
145701c855caSPeter Wemm 		if (!quoted && c >= 0x80) {
145801c855caSPeter Wemm 		    OUTCHAR('M');
145901c855caSPeter Wemm 		    OUTCHAR('-');
146001c855caSPeter Wemm 		    c -= 0x80;
146101c855caSPeter Wemm 		}
146201c855caSPeter Wemm 		if (quoted && (c == '"' || c == '\\'))
146301c855caSPeter Wemm 		    OUTCHAR('\\');
146401c855caSPeter Wemm 		if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
146501c855caSPeter Wemm 		    if (quoted) {
146601c855caSPeter Wemm 			OUTCHAR('\\');
146701c855caSPeter Wemm 			switch (c) {
146801c855caSPeter Wemm 			case '\t':	OUTCHAR('t');	break;
146901c855caSPeter Wemm 			case '\n':	OUTCHAR('n');	break;
147001c855caSPeter Wemm 			case '\b':	OUTCHAR('b');	break;
147101c855caSPeter Wemm 			case '\f':	OUTCHAR('f');	break;
147201c855caSPeter Wemm 			default:
147301c855caSPeter Wemm 			    OUTCHAR('x');
147401c855caSPeter Wemm 			    OUTCHAR(hexchars[c >> 4]);
147501c855caSPeter Wemm 			    OUTCHAR(hexchars[c & 0xf]);
147601c855caSPeter Wemm 			}
147701c855caSPeter Wemm 		    } else {
147801c855caSPeter Wemm 			if (c == '\t')
147901c855caSPeter Wemm 			    OUTCHAR(c);
148001c855caSPeter Wemm 			else {
148101c855caSPeter Wemm 			    OUTCHAR('^');
148201c855caSPeter Wemm 			    OUTCHAR(c ^ 0x40);
148301c855caSPeter Wemm 			}
148401c855caSPeter Wemm 		    }
148501c855caSPeter Wemm 		} else
148601c855caSPeter Wemm 		    OUTCHAR(c);
148701c855caSPeter Wemm 	    }
148801c855caSPeter Wemm 	    continue;
148901c855caSPeter Wemm 	default:
149001c855caSPeter Wemm 	    *buf++ = '%';
149101c855caSPeter Wemm 	    if (c != '%')
149201c855caSPeter Wemm 		--fmt;		/* so %z outputs %z etc. */
149301c855caSPeter Wemm 	    --buflen;
149401c855caSPeter Wemm 	    continue;
149501c855caSPeter Wemm 	}
149601c855caSPeter Wemm 	if (base != 0) {
149701c855caSPeter Wemm 	    str = num + sizeof(num);
149801c855caSPeter Wemm 	    *--str = 0;
149901c855caSPeter Wemm 	    while (str > num + neg) {
150001c855caSPeter Wemm 		*--str = hexchars[val % base];
150101c855caSPeter Wemm 		val = val / base;
150201c855caSPeter Wemm 		if (--prec <= 0 && val == 0)
150301c855caSPeter Wemm 		    break;
150401c855caSPeter Wemm 	    }
150501c855caSPeter Wemm 	    switch (neg) {
150601c855caSPeter Wemm 	    case 1:
150701c855caSPeter Wemm 		*--str = '-';
150801c855caSPeter Wemm 		break;
150901c855caSPeter Wemm 	    case 2:
151001c855caSPeter Wemm 		*--str = 'x';
151101c855caSPeter Wemm 		*--str = '0';
151201c855caSPeter Wemm 		break;
151301c855caSPeter Wemm 	    }
151401c855caSPeter Wemm 	    len = num + sizeof(num) - 1 - str;
151501c855caSPeter Wemm 	} else {
151601c855caSPeter Wemm 	    len = strlen(str);
151701c855caSPeter Wemm 	    if (prec > 0 && len > prec)
151801c855caSPeter Wemm 		len = prec;
151901c855caSPeter Wemm 	}
152001c855caSPeter Wemm 	if (width > 0) {
152101c855caSPeter Wemm 	    if (width > buflen)
152201c855caSPeter Wemm 		width = buflen;
152301c855caSPeter Wemm 	    if ((n = width - len) > 0) {
152401c855caSPeter Wemm 		buflen -= n;
152501c855caSPeter Wemm 		for (; n > 0; --n)
152601c855caSPeter Wemm 		    *buf++ = fillch;
152701c855caSPeter Wemm 	    }
152801c855caSPeter Wemm 	}
152901c855caSPeter Wemm 	if (len > buflen)
153001c855caSPeter Wemm 	    len = buflen;
153101c855caSPeter Wemm 	memcpy(buf, str, len);
153201c855caSPeter Wemm 	buf += len;
153301c855caSPeter Wemm 	buflen -= len;
153401c855caSPeter Wemm     }
153501c855caSPeter Wemm     *buf = 0;
153601c855caSPeter Wemm     return buf - buf0;
153701c855caSPeter Wemm }
1538