xref: /freebsd/usr.bin/chat/chat.c (revision 4e83a8fef438ccb47b4424ee88fab459778c45b0)
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 
803d793cf1SPeter Wemm #ifndef lint
81fa146c53SArchie Cobbs static const char rcsid[] =
82c3aac50fSPeter Wemm   "$FreeBSD$";
833d793cf1SPeter Wemm #endif
849b1aec48SLars Fredriksen 
859b1aec48SLars Fredriksen #include <stdio.h>
863d793cf1SPeter Wemm #include <ctype.h>
879f65f104SPeter Wemm #include <time.h>
889b1aec48SLars Fredriksen #include <fcntl.h>
899b1aec48SLars Fredriksen #include <signal.h>
909b1aec48SLars Fredriksen #include <errno.h>
919b1aec48SLars Fredriksen #include <string.h>
929b1aec48SLars Fredriksen #include <stdlib.h>
933d793cf1SPeter Wemm #include <unistd.h>
949b1aec48SLars Fredriksen #include <sys/types.h>
959b1aec48SLars Fredriksen #include <sys/stat.h>
969b1aec48SLars Fredriksen #include <syslog.h>
979b1aec48SLars Fredriksen 
989b1aec48SLars Fredriksen #ifndef TERMIO
999b1aec48SLars Fredriksen #undef	TERMIOS
1009b1aec48SLars Fredriksen #define TERMIOS
1019b1aec48SLars Fredriksen #endif
1029b1aec48SLars Fredriksen 
1039b1aec48SLars Fredriksen #ifdef TERMIO
1049b1aec48SLars Fredriksen #include <termio.h>
1059b1aec48SLars Fredriksen #endif
1069b1aec48SLars Fredriksen #ifdef TERMIOS
1079b1aec48SLars Fredriksen #include <termios.h>
1089b1aec48SLars Fredriksen #endif
1099b1aec48SLars Fredriksen 
1109b1aec48SLars Fredriksen #define	STR_LEN	1024
1119b1aec48SLars Fredriksen 
1129b1aec48SLars Fredriksen #ifndef SIGTYPE
1139b1aec48SLars Fredriksen #define SIGTYPE void
1149b1aec48SLars Fredriksen #endif
1159b1aec48SLars Fredriksen 
1169b1aec48SLars Fredriksen #undef __P
11701c855caSPeter Wemm #undef __V
11801c855caSPeter Wemm 
11901c855caSPeter Wemm #ifdef __STDC__
12001c855caSPeter Wemm #include <stdarg.h>
12101c855caSPeter Wemm #define __V(x)	x
1229b1aec48SLars Fredriksen #define __P(x)	x
1239b1aec48SLars Fredriksen #else
12401c855caSPeter Wemm #include <varargs.h>
12501c855caSPeter Wemm #define __V(x)	(va_alist) va_dcl
1269b1aec48SLars Fredriksen #define __P(x)	()
1279b1aec48SLars Fredriksen #define const
1289b1aec48SLars Fredriksen #endif
1299b1aec48SLars Fredriksen 
1309f65f104SPeter Wemm #ifndef O_NONBLOCK
1319f65f104SPeter Wemm #define O_NONBLOCK	O_NDELAY
1329f65f104SPeter Wemm #endif
1339f65f104SPeter Wemm 
13401c855caSPeter Wemm #ifdef SUNOS
13501c855caSPeter Wemm extern int sys_nerr;
13601c855caSPeter Wemm extern char *sys_errlist[];
13701c855caSPeter Wemm #define memmove(to, from, n)	bcopy(from, to, n)
13801c855caSPeter Wemm #define strerror(n)		((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
13901c855caSPeter Wemm 				 "unknown error")
14001c855caSPeter Wemm #endif
14101c855caSPeter Wemm 
1429b1aec48SLars Fredriksen /*************** Micro getopt() *********************************************/
1439b1aec48SLars Fredriksen #define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
1449b1aec48SLars Fredriksen 				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
1459b1aec48SLars Fredriksen 				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
1469b1aec48SLars Fredriksen #define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
1479b1aec48SLars Fredriksen 				(_O=4,(char*)0):(char*)0)
1489b1aec48SLars Fredriksen #define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
1499b1aec48SLars Fredriksen #define	ARG(c,v)	(c?(--c,*v++):(char*)0)
1509b1aec48SLars Fredriksen 
1519b1aec48SLars Fredriksen static int _O = 0;		/* Internal state */
1529b1aec48SLars Fredriksen /*************** Micro getopt() *********************************************/
1539b1aec48SLars Fredriksen 
1549b1aec48SLars Fredriksen #define	MAX_ABORTS		50
1559f65f104SPeter Wemm #define	MAX_REPORTS		50
1569b1aec48SLars Fredriksen #define	DEFAULT_CHAT_TIMEOUT	45
1579b1aec48SLars Fredriksen 
1583d793cf1SPeter Wemm int echo          = 0;
1599b1aec48SLars Fredriksen int verbose       = 0;
16001c855caSPeter Wemm int to_log        = 1;
16101c855caSPeter Wemm int to_stderr     = 0;
1623d793cf1SPeter Wemm int Verbose       = 0;
1639b1aec48SLars Fredriksen int quiet         = 0;
1649f65f104SPeter Wemm int report        = 0;
1659f65f104SPeter Wemm int exit_code     = 0;
1669f65f104SPeter Wemm FILE* report_fp   = (FILE *) 0;
1679f65f104SPeter Wemm char *report_file = (char *) 0;
1689b1aec48SLars Fredriksen char *chat_file   = (char *) 0;
16901c855caSPeter Wemm char *phone_num   = (char *) 0;
17001c855caSPeter Wemm char *phone_num2  = (char *) 0;
1719b1aec48SLars Fredriksen int timeout       = DEFAULT_CHAT_TIMEOUT;
1729b1aec48SLars Fredriksen 
1739b1aec48SLars Fredriksen int have_tty_parameters = 0;
1749f65f104SPeter Wemm 
1759b1aec48SLars Fredriksen #ifdef TERMIO
1769f65f104SPeter Wemm #define term_parms struct termio
1779f65f104SPeter Wemm #define get_term_param(param) ioctl(0, TCGETA, param)
1789f65f104SPeter Wemm #define set_term_param(param) ioctl(0, TCSETA, param)
1799b1aec48SLars Fredriksen struct termio saved_tty_parameters;
1809b1aec48SLars Fredriksen #endif
1819f65f104SPeter Wemm 
1829b1aec48SLars Fredriksen #ifdef TERMIOS
1839f65f104SPeter Wemm #define term_parms struct termios
1849f65f104SPeter Wemm #define get_term_param(param) tcgetattr(0, param)
1859f65f104SPeter Wemm #define set_term_param(param) tcsetattr(0, TCSANOW, param)
1869b1aec48SLars Fredriksen struct termios saved_tty_parameters;
1879b1aec48SLars Fredriksen #endif
1889b1aec48SLars Fredriksen 
1899b1aec48SLars Fredriksen char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
1909b1aec48SLars Fredriksen 	fail_buffer[50];
1913d793cf1SPeter Wemm int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
1923d793cf1SPeter Wemm int clear_abort_next = 0;
1939b1aec48SLars Fredriksen 
1949f65f104SPeter Wemm char *report_string[MAX_REPORTS] ;
1959f65f104SPeter Wemm char  report_buffer[50] ;
1969f65f104SPeter Wemm int n_reports = 0, report_next = 0, report_gathering = 0 ;
1973d793cf1SPeter Wemm int clear_report_next = 0;
1983d793cf1SPeter Wemm 
1993d793cf1SPeter Wemm int say_next = 0, hup_next = 0;
2009f65f104SPeter Wemm 
2019b1aec48SLars Fredriksen void *dup_mem __P((void *b, size_t c));
2029b1aec48SLars Fredriksen void *copy_of __P((char *s));
203afaeb553SPhilippe Charnier static void usage __P((void));
20401c855caSPeter Wemm void logf __P((const char *fmt, ...));
20501c855caSPeter Wemm void fatal __P((int code, const char *fmt, ...));
2069b1aec48SLars Fredriksen SIGTYPE sigalrm __P((int signo));
2079b1aec48SLars Fredriksen SIGTYPE sigint __P((int signo));
2089b1aec48SLars Fredriksen SIGTYPE sigterm __P((int signo));
2099b1aec48SLars Fredriksen SIGTYPE sighup __P((int signo));
2109b1aec48SLars Fredriksen void unalarm __P((void));
2119b1aec48SLars Fredriksen void init __P((void));
2129b1aec48SLars Fredriksen void set_tty_parameters __P((void));
2133d793cf1SPeter Wemm void echo_stderr __P((int));
2149b1aec48SLars Fredriksen void break_sequence __P((void));
2159b1aec48SLars Fredriksen void terminate __P((int status));
2169b1aec48SLars Fredriksen void do_file __P((char *chat_file));
2179b1aec48SLars Fredriksen int  get_string __P((register char *string));
2189b1aec48SLars Fredriksen int  put_string __P((register char *s));
2199b1aec48SLars Fredriksen int  write_char __P((int c));
2209f65f104SPeter Wemm int  put_char __P((int c));
2219b1aec48SLars Fredriksen int  get_char __P((void));
2229b1aec48SLars Fredriksen void chat_send __P((register char *s));
2239f65f104SPeter Wemm char *character __P((int c));
2249b1aec48SLars Fredriksen void chat_expect __P((register char *s));
2259b1aec48SLars Fredriksen char *clean __P((register char *s, int sending));
2269b1aec48SLars Fredriksen void break_sequence __P((void));
2279b1aec48SLars Fredriksen void terminate __P((int status));
2283d793cf1SPeter Wemm void pack_array __P((char **array, int end));
2293d793cf1SPeter Wemm char *expect_strtok __P((char *, char *));
23001c855caSPeter Wemm int vfmtmsg __P((char *, int, const char *, va_list));	/* vsprintf++ */
2313d793cf1SPeter Wemm 
2323d793cf1SPeter Wemm int main __P((int, char *[]));
2339b1aec48SLars Fredriksen 
2349b1aec48SLars Fredriksen void *dup_mem(b, c)
2359b1aec48SLars Fredriksen void *b;
2369b1aec48SLars Fredriksen size_t c;
2379b1aec48SLars Fredriksen {
2389b1aec48SLars Fredriksen     void *ans = malloc (c);
2399b1aec48SLars Fredriksen     if (!ans)
24001c855caSPeter Wemm 	fatal(2, "memory error!");
24101c855caSPeter Wemm 
2429b1aec48SLars Fredriksen     memcpy (ans, b, c);
2439b1aec48SLars Fredriksen     return ans;
2449b1aec48SLars Fredriksen }
2459b1aec48SLars Fredriksen 
2469b1aec48SLars Fredriksen void *copy_of (s)
2479b1aec48SLars Fredriksen char *s;
2489b1aec48SLars Fredriksen {
2499b1aec48SLars Fredriksen     return dup_mem (s, strlen (s) + 1);
2509b1aec48SLars Fredriksen }
2519b1aec48SLars Fredriksen 
2529b1aec48SLars Fredriksen /*
25301c855caSPeter Wemm  * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \
25401c855caSPeter Wemm  * [ -r report-file ] \
2559b1aec48SLars Fredriksen  *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
2569b1aec48SLars Fredriksen  *
2579b1aec48SLars Fredriksen  *	Perform a UUCP-dialer-like chat script on stdin and stdout.
2589b1aec48SLars Fredriksen  */
2599b1aec48SLars Fredriksen int
2609b1aec48SLars Fredriksen main(argc, argv)
2619b1aec48SLars Fredriksen      int argc;
2629b1aec48SLars Fredriksen      char **argv;
2639b1aec48SLars Fredriksen {
2649b1aec48SLars Fredriksen     int option;
2659b1aec48SLars Fredriksen     char *arg;
2669b1aec48SLars Fredriksen 
2679f65f104SPeter Wemm     tzset();
2689b1aec48SLars Fredriksen 
26901c855caSPeter Wemm     while ((option = OPTION(argc, argv)) != 0) {
27001c855caSPeter Wemm 	switch (option) {
2713d793cf1SPeter Wemm 	case 'e':
2723d793cf1SPeter Wemm 	    ++echo;
2733d793cf1SPeter Wemm 	    break;
2743d793cf1SPeter Wemm 
2759b1aec48SLars Fredriksen 	case 'v':
2769b1aec48SLars Fredriksen 	    ++verbose;
2779b1aec48SLars Fredriksen 	    break;
2789b1aec48SLars Fredriksen 
2793d793cf1SPeter Wemm 	case 'V':
2803d793cf1SPeter Wemm 	    ++Verbose;
2813d793cf1SPeter Wemm 	    break;
2823d793cf1SPeter Wemm 
28301c855caSPeter Wemm 	case 's':
28401c855caSPeter Wemm 	    ++to_stderr;
28501c855caSPeter Wemm 	    break;
28601c855caSPeter Wemm 
28701c855caSPeter Wemm 	case 'S':
28801c855caSPeter Wemm 	    to_log = 0;
28901c855caSPeter Wemm 	    break;
29001c855caSPeter Wemm 
2919b1aec48SLars Fredriksen 	case 'f':
2923d793cf1SPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
2939b1aec48SLars Fredriksen 		    chat_file = copy_of(arg);
2949b1aec48SLars Fredriksen 	    else
2959b1aec48SLars Fredriksen 		usage();
2969b1aec48SLars Fredriksen 	    break;
2979b1aec48SLars Fredriksen 
2989b1aec48SLars Fredriksen 	case 't':
2993d793cf1SPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
3009b1aec48SLars Fredriksen 		timeout = atoi(arg);
3019b1aec48SLars Fredriksen 	    else
3029b1aec48SLars Fredriksen 		usage();
3039f65f104SPeter Wemm 	    break;
3049b1aec48SLars Fredriksen 
3059f65f104SPeter Wemm 	case 'r':
3069f65f104SPeter Wemm 	    arg = OPTARG (argc, argv);
30701c855caSPeter Wemm 	    if (arg) {
3089f65f104SPeter Wemm 		if (report_fp != NULL)
3099f65f104SPeter Wemm 		    fclose (report_fp);
3109f65f104SPeter Wemm 		report_file = copy_of (arg);
3119f65f104SPeter Wemm 		report_fp   = fopen (report_file, "a");
31201c855caSPeter Wemm 		if (report_fp != NULL) {
3139f65f104SPeter Wemm 		    if (verbose)
3149f65f104SPeter Wemm 			fprintf (report_fp, "Opening \"%s\"...\n",
3159f65f104SPeter Wemm 				 report_file);
3169f65f104SPeter Wemm 		    report = 1;
3179f65f104SPeter Wemm 		}
3189f65f104SPeter Wemm 	    }
3199b1aec48SLars Fredriksen 	    break;
3209b1aec48SLars Fredriksen 
32101c855caSPeter Wemm 	case 'T':
32201c855caSPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
32301c855caSPeter Wemm 		phone_num = copy_of(arg);
32401c855caSPeter Wemm 	    else
32501c855caSPeter Wemm 		usage();
32601c855caSPeter Wemm 	    break;
32701c855caSPeter Wemm 
32801c855caSPeter Wemm 	case 'U':
32901c855caSPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
33001c855caSPeter Wemm 		phone_num2 = copy_of(arg);
33101c855caSPeter Wemm 	    else
33201c855caSPeter Wemm 		usage();
33301c855caSPeter Wemm 	    break;
33401c855caSPeter Wemm 
3359b1aec48SLars Fredriksen 	default:
3369b1aec48SLars Fredriksen 	    usage();
3379f65f104SPeter Wemm 	    break;
3389f65f104SPeter Wemm 	}
3399f65f104SPeter Wemm     }
3409f65f104SPeter Wemm /*
3419f65f104SPeter Wemm  * Default the report file to the stderr location
3429f65f104SPeter Wemm  */
3439f65f104SPeter Wemm     if (report_fp == NULL)
3449f65f104SPeter Wemm 	report_fp = stderr;
3459b1aec48SLars Fredriksen 
34601c855caSPeter Wemm     if (to_log) {
3479b1aec48SLars Fredriksen #ifdef ultrix
3489b1aec48SLars Fredriksen 	openlog("chat", LOG_PID);
3499b1aec48SLars Fredriksen #else
3509b1aec48SLars Fredriksen 	openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
3519b1aec48SLars Fredriksen 
3529f65f104SPeter Wemm 	if (verbose)
3539b1aec48SLars Fredriksen 	    setlogmask(LOG_UPTO(LOG_INFO));
3549f65f104SPeter Wemm 	else
3559b1aec48SLars Fredriksen 	    setlogmask(LOG_UPTO(LOG_WARNING));
3569b1aec48SLars Fredriksen #endif
35701c855caSPeter Wemm     }
3589b1aec48SLars Fredriksen 
3599b1aec48SLars Fredriksen     init();
3609b1aec48SLars Fredriksen 
36101c855caSPeter Wemm     if (chat_file != NULL) {
3629b1aec48SLars Fredriksen 	arg = ARG(argc, argv);
3639b1aec48SLars Fredriksen 	if (arg != NULL)
3649b1aec48SLars Fredriksen 	    usage();
3659b1aec48SLars Fredriksen 	else
3669b1aec48SLars Fredriksen 	    do_file (chat_file);
36701c855caSPeter Wemm     } else {
36801c855caSPeter Wemm 	while ((arg = ARG(argc, argv)) != NULL) {
3699b1aec48SLars Fredriksen 	    chat_expect(arg);
3709b1aec48SLars Fredriksen 
3713d793cf1SPeter Wemm 	    if ((arg = ARG(argc, argv)) != NULL)
3729b1aec48SLars Fredriksen 		chat_send(arg);
3739b1aec48SLars Fredriksen 	}
3749b1aec48SLars Fredriksen     }
3759b1aec48SLars Fredriksen 
3769b1aec48SLars Fredriksen     terminate(0);
3773d793cf1SPeter Wemm     return 0;
3789b1aec48SLars Fredriksen }
3799b1aec48SLars Fredriksen 
3809b1aec48SLars Fredriksen /*
3819b1aec48SLars Fredriksen  *  Process a chat script when read from a file.
3829b1aec48SLars Fredriksen  */
3839b1aec48SLars Fredriksen 
3849b1aec48SLars Fredriksen void do_file (chat_file)
3859b1aec48SLars Fredriksen char *chat_file;
3869b1aec48SLars Fredriksen {
387d7d10053SAlexander Langer     int linect, sendflg;
3889b1aec48SLars Fredriksen     char *sp, *arg, quote;
3899b1aec48SLars Fredriksen     char buf [STR_LEN];
3909b1aec48SLars Fredriksen     FILE *cfp;
3919b1aec48SLars Fredriksen 
3929f65f104SPeter Wemm     cfp = fopen (chat_file, "r");
3939f65f104SPeter Wemm     if (cfp == NULL)
39401c855caSPeter Wemm 	fatal(1, "%s -- open failed: %m", chat_file);
3959b1aec48SLars Fredriksen 
3969b1aec48SLars Fredriksen     linect = 0;
3979b1aec48SLars Fredriksen     sendflg = 0;
3989b1aec48SLars Fredriksen 
39901c855caSPeter Wemm     while (fgets(buf, STR_LEN, cfp) != NULL) {
4009b1aec48SLars Fredriksen 	sp = strchr (buf, '\n');
4019b1aec48SLars Fredriksen 	if (sp)
4029b1aec48SLars Fredriksen 	    *sp = '\0';
4039b1aec48SLars Fredriksen 
4049b1aec48SLars Fredriksen 	linect++;
4059b1aec48SLars Fredriksen 	sp = buf;
4063d793cf1SPeter Wemm 
4073d793cf1SPeter Wemm         /* lines starting with '#' are comments. If a real '#'
4083d793cf1SPeter Wemm            is to be expected, it should be quoted .... */
40901c855caSPeter Wemm         if ( *sp == '#' )
41001c855caSPeter Wemm 	    continue;
4113d793cf1SPeter Wemm 
41201c855caSPeter Wemm 	while (*sp != '\0') {
41301c855caSPeter Wemm 	    if (*sp == ' ' || *sp == '\t') {
4149b1aec48SLars Fredriksen 		++sp;
4159b1aec48SLars Fredriksen 		continue;
4169b1aec48SLars Fredriksen 	    }
4179b1aec48SLars Fredriksen 
41801c855caSPeter Wemm 	    if (*sp == '"' || *sp == '\'') {
4199b1aec48SLars Fredriksen 		quote = *sp++;
4209b1aec48SLars Fredriksen 		arg = sp;
42101c855caSPeter Wemm 		while (*sp != quote) {
4229b1aec48SLars Fredriksen 		    if (*sp == '\0')
42301c855caSPeter Wemm 			fatal(1, "unterminated quote (line %d)", linect);
4249b1aec48SLars Fredriksen 
42501c855caSPeter Wemm 		    if (*sp++ == '\\') {
4269b1aec48SLars Fredriksen 			if (*sp != '\0')
4279b1aec48SLars Fredriksen 			    ++sp;
4289b1aec48SLars Fredriksen 		    }
4299b1aec48SLars Fredriksen 		}
4309f65f104SPeter Wemm 	    }
43101c855caSPeter Wemm 	    else {
4329b1aec48SLars Fredriksen 		arg = sp;
4339b1aec48SLars Fredriksen 		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
4349b1aec48SLars Fredriksen 		    ++sp;
4359b1aec48SLars Fredriksen 	    }
4369b1aec48SLars Fredriksen 
4379b1aec48SLars Fredriksen 	    if (*sp != '\0')
4389b1aec48SLars Fredriksen 		*sp++ = '\0';
4399b1aec48SLars Fredriksen 
4409b1aec48SLars Fredriksen 	    if (sendflg)
4419b1aec48SLars Fredriksen 		chat_send (arg);
4429b1aec48SLars Fredriksen 	    else
4439b1aec48SLars Fredriksen 		chat_expect (arg);
4449b1aec48SLars Fredriksen 	    sendflg = !sendflg;
4459b1aec48SLars Fredriksen 	}
4469b1aec48SLars Fredriksen     }
4479b1aec48SLars Fredriksen     fclose (cfp);
4489b1aec48SLars Fredriksen }
4499b1aec48SLars Fredriksen 
4509b1aec48SLars Fredriksen /*
4519b1aec48SLars Fredriksen  *	We got an error parsing the command line.
4529b1aec48SLars Fredriksen  */
453afaeb553SPhilippe Charnier static void
454afaeb553SPhilippe Charnier usage()
4559b1aec48SLars Fredriksen {
45601c855caSPeter Wemm     fprintf(stderr, "\
45701c855caSPeter Wemm Usage: chat [-e] [-v] [-V] [-t timeout] [-r report-file] [-T phone-number]\n\
45801c855caSPeter Wemm      [-U phone-number2] {-f chat-file | chat-script}\n");
4599b1aec48SLars Fredriksen     exit(1);
4609b1aec48SLars Fredriksen }
4619b1aec48SLars Fredriksen 
46201c855caSPeter Wemm char line[1024];
4639b1aec48SLars Fredriksen 
4649b1aec48SLars Fredriksen /*
46501c855caSPeter Wemm  * Send a message to syslog and/or stderr.
4669b1aec48SLars Fredriksen  */
46701c855caSPeter Wemm void logf __V((const char *fmt, ...))
4689b1aec48SLars Fredriksen {
46901c855caSPeter Wemm     va_list args;
47001c855caSPeter Wemm 
47101c855caSPeter Wemm #ifdef __STDC__
47201c855caSPeter Wemm     va_start(args, fmt);
47301c855caSPeter Wemm #else
47401c855caSPeter Wemm     char *fmt;
47501c855caSPeter Wemm     va_start(args);
47601c855caSPeter Wemm     fmt = va_arg(args, char *);
47701c855caSPeter Wemm #endif
47801c855caSPeter Wemm 
47901c855caSPeter Wemm     vfmtmsg(line, sizeof(line), fmt, args);
48001c855caSPeter Wemm     if (to_log)
48101c855caSPeter Wemm 	syslog(LOG_INFO, "%s", line);
48201c855caSPeter Wemm     if (to_stderr)
48301c855caSPeter Wemm 	fprintf(stderr, "%s\n", line);
4849b1aec48SLars Fredriksen }
4859b1aec48SLars Fredriksen 
4869b1aec48SLars Fredriksen /*
4879b1aec48SLars Fredriksen  *	Print an error message and terminate.
4889b1aec48SLars Fredriksen  */
4899b1aec48SLars Fredriksen 
49001c855caSPeter Wemm void fatal __V((int code, const char *fmt, ...))
4919b1aec48SLars Fredriksen {
49201c855caSPeter Wemm     va_list args;
4939b1aec48SLars Fredriksen 
49401c855caSPeter Wemm #ifdef __STDC__
49501c855caSPeter Wemm     va_start(args, fmt);
49601c855caSPeter Wemm #else
49701c855caSPeter Wemm     int code;
49801c855caSPeter Wemm     char *fmt;
49901c855caSPeter Wemm     va_start(args);
50001c855caSPeter Wemm     code = va_arg(args, int);
50101c855caSPeter Wemm     fmt = va_arg(args, char *);
50201c855caSPeter Wemm #endif
5039b1aec48SLars Fredriksen 
50401c855caSPeter Wemm     vfmtmsg(line, sizeof(line), fmt, args);
50501c855caSPeter Wemm     if (to_log)
50601c855caSPeter Wemm 	syslog(LOG_ERR, "%s", line);
50701c855caSPeter Wemm     if (to_stderr)
50801c855caSPeter Wemm 	fprintf(stderr, "%s\n", line);
50901c855caSPeter Wemm     terminate(code);
5109b1aec48SLars Fredriksen }
5119b1aec48SLars Fredriksen 
5129b1aec48SLars Fredriksen int alarmed = 0;
5139b1aec48SLars Fredriksen 
5149b1aec48SLars Fredriksen SIGTYPE sigalrm(signo)
5159b1aec48SLars Fredriksen int signo;
5169b1aec48SLars Fredriksen {
5179b1aec48SLars Fredriksen     int flags;
5189b1aec48SLars Fredriksen 
5199b1aec48SLars Fredriksen     alarm(1);
5209b1aec48SLars Fredriksen     alarmed = 1;		/* Reset alarm to avoid race window */
5219b1aec48SLars Fredriksen     signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
5229b1aec48SLars Fredriksen 
5239b1aec48SLars Fredriksen     if ((flags = fcntl(0, F_GETFL, 0)) == -1)
52401c855caSPeter Wemm 	fatal(2, "Can't get file mode flags on stdin: %m");
52501c855caSPeter Wemm 
5269f65f104SPeter Wemm     if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
52701c855caSPeter Wemm 	fatal(2, "Can't set file mode flags on stdin: %m");
5289b1aec48SLars Fredriksen 
5299b1aec48SLars Fredriksen     if (verbose)
53001c855caSPeter Wemm 	logf("alarm");
5319b1aec48SLars Fredriksen }
5329b1aec48SLars Fredriksen 
5339b1aec48SLars Fredriksen void unalarm()
5349b1aec48SLars Fredriksen {
5359b1aec48SLars Fredriksen     int flags;
5369b1aec48SLars Fredriksen 
5379b1aec48SLars Fredriksen     if ((flags = fcntl(0, F_GETFL, 0)) == -1)
53801c855caSPeter Wemm 	fatal(2, "Can't get file mode flags on stdin: %m");
53901c855caSPeter Wemm 
5409f65f104SPeter Wemm     if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
54101c855caSPeter Wemm 	fatal(2, "Can't set file mode flags on stdin: %m");
5429f65f104SPeter Wemm }
5439b1aec48SLars Fredriksen 
5449b1aec48SLars Fredriksen SIGTYPE sigint(signo)
5459b1aec48SLars Fredriksen int signo;
5469b1aec48SLars Fredriksen {
54701c855caSPeter Wemm     fatal(2, "SIGINT");
5489b1aec48SLars Fredriksen }
5499b1aec48SLars Fredriksen 
5509b1aec48SLars Fredriksen SIGTYPE sigterm(signo)
5519b1aec48SLars Fredriksen int signo;
5529b1aec48SLars Fredriksen {
55301c855caSPeter Wemm     fatal(2, "SIGTERM");
5549b1aec48SLars Fredriksen }
5559b1aec48SLars Fredriksen 
5569b1aec48SLars Fredriksen SIGTYPE sighup(signo)
5579b1aec48SLars Fredriksen int signo;
5589b1aec48SLars Fredriksen {
55901c855caSPeter Wemm     fatal(2, "SIGHUP");
5609b1aec48SLars Fredriksen }
5619b1aec48SLars Fredriksen 
5629b1aec48SLars Fredriksen void init()
5639b1aec48SLars Fredriksen {
5649b1aec48SLars Fredriksen     signal(SIGINT, sigint);
5659b1aec48SLars Fredriksen     signal(SIGTERM, sigterm);
5669b1aec48SLars Fredriksen     signal(SIGHUP, sighup);
5679b1aec48SLars Fredriksen 
5689b1aec48SLars Fredriksen     set_tty_parameters();
5699b1aec48SLars Fredriksen     signal(SIGALRM, sigalrm);
5709b1aec48SLars Fredriksen     alarm(0);
5719b1aec48SLars Fredriksen     alarmed = 0;
5729b1aec48SLars Fredriksen }
5739b1aec48SLars Fredriksen 
5749b1aec48SLars Fredriksen void set_tty_parameters()
5759b1aec48SLars Fredriksen {
5769f65f104SPeter Wemm #if defined(get_term_param)
5779f65f104SPeter Wemm     term_parms t;
5789b1aec48SLars Fredriksen 
5799f65f104SPeter Wemm     if (get_term_param (&t) < 0)
58001c855caSPeter Wemm 	fatal(2, "Can't get terminal parameters: %m");
5819b1aec48SLars Fredriksen 
5829b1aec48SLars Fredriksen     saved_tty_parameters = t;
5839b1aec48SLars Fredriksen     have_tty_parameters  = 1;
5849b1aec48SLars Fredriksen 
5859b1aec48SLars Fredriksen     t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
5869b1aec48SLars Fredriksen     t.c_oflag      = 0;
5879b1aec48SLars Fredriksen     t.c_lflag      = 0;
5889f65f104SPeter Wemm     t.c_cc[VERASE] =
5899f65f104SPeter Wemm     t.c_cc[VKILL]  = 0;
5909b1aec48SLars Fredriksen     t.c_cc[VMIN]   = 1;
5919b1aec48SLars Fredriksen     t.c_cc[VTIME]  = 0;
5929b1aec48SLars Fredriksen 
5939f65f104SPeter Wemm     if (set_term_param (&t) < 0)
59401c855caSPeter Wemm 	fatal(2, "Can't set terminal parameters: %m");
5959b1aec48SLars Fredriksen #endif
5969b1aec48SLars Fredriksen }
5979b1aec48SLars Fredriksen 
5989b1aec48SLars Fredriksen void break_sequence()
5999b1aec48SLars Fredriksen {
6009b1aec48SLars Fredriksen #ifdef TERMIOS
6019b1aec48SLars Fredriksen     tcsendbreak (0, 0);
6029b1aec48SLars Fredriksen #endif
6039b1aec48SLars Fredriksen }
6049b1aec48SLars Fredriksen 
6059b1aec48SLars Fredriksen void terminate(status)
6069b1aec48SLars Fredriksen int status;
6079b1aec48SLars Fredriksen {
6083d793cf1SPeter Wemm     echo_stderr(-1);
60901c855caSPeter Wemm     if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
6103d793cf1SPeter Wemm /*
6113d793cf1SPeter Wemm  * Allow the last of the report string to be gathered before we terminate.
6123d793cf1SPeter Wemm  */
6133d793cf1SPeter Wemm 	if (report_gathering) {
6143d793cf1SPeter Wemm 	    int c, rep_len;
6153d793cf1SPeter Wemm 
6163d793cf1SPeter Wemm 	    rep_len = strlen(report_buffer);
6173d793cf1SPeter Wemm 	    while (rep_len + 1 <= sizeof(report_buffer)) {
6183d793cf1SPeter Wemm 		alarm(1);
6193d793cf1SPeter Wemm 		c = get_char();
6203d793cf1SPeter Wemm 		alarm(0);
6213d793cf1SPeter Wemm 		if (c < 0 || iscntrl(c))
6223d793cf1SPeter Wemm 		    break;
6233d793cf1SPeter Wemm 		report_buffer[rep_len] = c;
6243d793cf1SPeter Wemm 		++rep_len;
6253d793cf1SPeter Wemm 	    }
6263d793cf1SPeter Wemm 	    report_buffer[rep_len] = 0;
6273d793cf1SPeter Wemm 	    fprintf (report_fp, "chat:  %s\n", report_buffer);
6283d793cf1SPeter Wemm 	}
6299f65f104SPeter Wemm 	if (verbose)
6309f65f104SPeter Wemm 	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
6319f65f104SPeter Wemm 	fclose (report_fp);
6329f65f104SPeter Wemm 	report_fp = (FILE *) NULL;
6339f65f104SPeter Wemm     }
6349f65f104SPeter Wemm 
6359f65f104SPeter Wemm #if defined(get_term_param)
63601c855caSPeter Wemm     if (have_tty_parameters) {
6379f65f104SPeter Wemm 	if (set_term_param (&saved_tty_parameters) < 0)
63801c855caSPeter Wemm 	    fatal(2, "Can't restore terminal parameters: %m");
6399f65f104SPeter Wemm     }
6409f65f104SPeter Wemm #endif
6419f65f104SPeter Wemm 
6429b1aec48SLars Fredriksen     exit(status);
6439b1aec48SLars Fredriksen }
6449b1aec48SLars Fredriksen 
6459b1aec48SLars Fredriksen /*
6469b1aec48SLars Fredriksen  *	'Clean up' this string.
6479b1aec48SLars Fredriksen  */
6489b1aec48SLars Fredriksen char *clean(s, sending)
6499b1aec48SLars Fredriksen register char *s;
65001c855caSPeter Wemm int sending;  /* set to 1 when sending (putting) this string. */
6519b1aec48SLars Fredriksen {
6529b1aec48SLars Fredriksen     char temp[STR_LEN], cur_chr;
65301c855caSPeter Wemm     register char *s1, *phchar;
6549b1aec48SLars Fredriksen     int add_return = sending;
6559b1aec48SLars Fredriksen #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
6569b1aec48SLars Fredriksen 
6579b1aec48SLars Fredriksen     s1 = temp;
6584e83a8feSKris Kennaway     /* Don't overflow buffer, leave room for chars we append later */
6594e83a8feSKris Kennaway     while (*s && s1 - temp < sizeof(temp) - 2 - add_return) {
6609b1aec48SLars Fredriksen 	cur_chr = *s++;
66101c855caSPeter Wemm 	if (cur_chr == '^') {
6629b1aec48SLars Fredriksen 	    cur_chr = *s++;
66301c855caSPeter Wemm 	    if (cur_chr == '\0') {
6649b1aec48SLars Fredriksen 		*s1++ = '^';
6659b1aec48SLars Fredriksen 		break;
6669b1aec48SLars Fredriksen 	    }
6679b1aec48SLars Fredriksen 	    cur_chr &= 0x1F;
66801c855caSPeter Wemm 	    if (cur_chr != 0) {
6699b1aec48SLars Fredriksen 		*s1++ = cur_chr;
6709f65f104SPeter Wemm 	    }
6719b1aec48SLars Fredriksen 	    continue;
6729b1aec48SLars Fredriksen 	}
6739b1aec48SLars Fredriksen 
67401c855caSPeter Wemm 	if (cur_chr != '\\') {
6759b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
6769b1aec48SLars Fredriksen 	    continue;
6779b1aec48SLars Fredriksen 	}
6789b1aec48SLars Fredriksen 
6799b1aec48SLars Fredriksen 	cur_chr = *s++;
68001c855caSPeter Wemm 	if (cur_chr == '\0') {
68101c855caSPeter Wemm 	    if (sending) {
6829b1aec48SLars Fredriksen 		*s1++ = '\\';
6839b1aec48SLars Fredriksen 		*s1++ = '\\';
6849b1aec48SLars Fredriksen 	    }
6859b1aec48SLars Fredriksen 	    break;
6869b1aec48SLars Fredriksen 	}
6879b1aec48SLars Fredriksen 
68801c855caSPeter Wemm 	switch (cur_chr) {
6899b1aec48SLars Fredriksen 	case 'b':
6909b1aec48SLars Fredriksen 	    *s1++ = '\b';
6919b1aec48SLars Fredriksen 	    break;
6929b1aec48SLars Fredriksen 
6939b1aec48SLars Fredriksen 	case 'c':
6949b1aec48SLars Fredriksen 	    if (sending && *s == '\0')
6959b1aec48SLars Fredriksen 		add_return = 0;
6969b1aec48SLars Fredriksen 	    else
6979b1aec48SLars Fredriksen 		*s1++ = cur_chr;
6989b1aec48SLars Fredriksen 	    break;
6999b1aec48SLars Fredriksen 
7009b1aec48SLars Fredriksen 	case '\\':
7019b1aec48SLars Fredriksen 	case 'K':
7029b1aec48SLars Fredriksen 	case 'p':
7039b1aec48SLars Fredriksen 	case 'd':
7049b1aec48SLars Fredriksen 	    if (sending)
7059b1aec48SLars Fredriksen 		*s1++ = '\\';
7069b1aec48SLars Fredriksen 
7079b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
7089b1aec48SLars Fredriksen 	    break;
7099b1aec48SLars Fredriksen 
71001c855caSPeter Wemm 	case 'T':
71101c855caSPeter Wemm 	    if (sending && phone_num) {
71201c855caSPeter Wemm 		for ( phchar = phone_num; *phchar != '\0'; phchar++)
71301c855caSPeter Wemm 		    *s1++ = *phchar;
71401c855caSPeter Wemm 	    }
71501c855caSPeter Wemm 	    else {
71601c855caSPeter Wemm 		*s1++ = '\\';
71701c855caSPeter Wemm 		*s1++ = 'T';
71801c855caSPeter Wemm 	    }
71901c855caSPeter Wemm 	    break;
72001c855caSPeter Wemm 
72101c855caSPeter Wemm 	case 'U':
72201c855caSPeter Wemm 	    if (sending && phone_num2) {
72301c855caSPeter Wemm 		for ( phchar = phone_num2; *phchar != '\0'; phchar++)
72401c855caSPeter Wemm 		    *s1++ = *phchar;
72501c855caSPeter Wemm 	    }
72601c855caSPeter Wemm 	    else {
72701c855caSPeter Wemm 		*s1++ = '\\';
72801c855caSPeter Wemm 		*s1++ = 'U';
72901c855caSPeter Wemm 	    }
73001c855caSPeter Wemm 	    break;
73101c855caSPeter Wemm 
7329b1aec48SLars Fredriksen 	case 'q':
7333d793cf1SPeter Wemm 	    quiet = 1;
7349b1aec48SLars Fredriksen 	    break;
7359b1aec48SLars Fredriksen 
7369b1aec48SLars Fredriksen 	case 'r':
7379b1aec48SLars Fredriksen 	    *s1++ = '\r';
7389b1aec48SLars Fredriksen 	    break;
7399b1aec48SLars Fredriksen 
7409b1aec48SLars Fredriksen 	case 'n':
7419b1aec48SLars Fredriksen 	    *s1++ = '\n';
7429b1aec48SLars Fredriksen 	    break;
7439b1aec48SLars Fredriksen 
7449b1aec48SLars Fredriksen 	case 's':
7459b1aec48SLars Fredriksen 	    *s1++ = ' ';
7469b1aec48SLars Fredriksen 	    break;
7479b1aec48SLars Fredriksen 
7489b1aec48SLars Fredriksen 	case 't':
7499b1aec48SLars Fredriksen 	    *s1++ = '\t';
7509b1aec48SLars Fredriksen 	    break;
7519b1aec48SLars Fredriksen 
7529b1aec48SLars Fredriksen 	case 'N':
75301c855caSPeter Wemm 	    if (sending) {
7549b1aec48SLars Fredriksen 		*s1++ = '\\';
7559b1aec48SLars Fredriksen 		*s1++ = '\0';
7569b1aec48SLars Fredriksen 	    }
7579b1aec48SLars Fredriksen 	    else
7589b1aec48SLars Fredriksen 		*s1++ = 'N';
7599b1aec48SLars Fredriksen 	    break;
7609b1aec48SLars Fredriksen 
7619b1aec48SLars Fredriksen 	default:
76201c855caSPeter Wemm 	    if (isoctal (cur_chr)) {
7639b1aec48SLars Fredriksen 		cur_chr &= 0x07;
76401c855caSPeter Wemm 		if (isoctal (*s)) {
7659b1aec48SLars Fredriksen 		    cur_chr <<= 3;
7669b1aec48SLars Fredriksen 		    cur_chr |= *s++ - '0';
76701c855caSPeter Wemm 		    if (isoctal (*s)) {
7689b1aec48SLars Fredriksen 			cur_chr <<= 3;
7699b1aec48SLars Fredriksen 			cur_chr |= *s++ - '0';
7709b1aec48SLars Fredriksen 		    }
7719b1aec48SLars Fredriksen 		}
7729b1aec48SLars Fredriksen 
77301c855caSPeter Wemm 		if (cur_chr != 0 || sending) {
7749b1aec48SLars Fredriksen 		    if (sending && (cur_chr == '\\' || cur_chr == 0))
7759b1aec48SLars Fredriksen 			*s1++ = '\\';
7769b1aec48SLars Fredriksen 		    *s1++ = cur_chr;
7779b1aec48SLars Fredriksen 		}
7789b1aec48SLars Fredriksen 		break;
7799b1aec48SLars Fredriksen 	    }
7809b1aec48SLars Fredriksen 
7819b1aec48SLars Fredriksen 	    if (sending)
7829b1aec48SLars Fredriksen 		*s1++ = '\\';
7839b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
7849b1aec48SLars Fredriksen 	    break;
7859b1aec48SLars Fredriksen 	}
7869b1aec48SLars Fredriksen     }
7879b1aec48SLars Fredriksen 
7889b1aec48SLars Fredriksen     if (add_return)
7899b1aec48SLars Fredriksen 	*s1++ = '\r';
7909b1aec48SLars Fredriksen 
7919b1aec48SLars Fredriksen     *s1++ = '\0'; /* guarantee closure */
7929b1aec48SLars Fredriksen     *s1++ = '\0'; /* terminate the string */
7939b1aec48SLars Fredriksen     return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
7949b1aec48SLars Fredriksen }
7959b1aec48SLars Fredriksen 
7969b1aec48SLars Fredriksen /*
7973d793cf1SPeter Wemm  * A modified version of 'strtok'. This version skips \ sequences.
7983d793cf1SPeter Wemm  */
7993d793cf1SPeter Wemm 
8003d793cf1SPeter Wemm char *expect_strtok (s, term)
8013d793cf1SPeter Wemm      char *s, *term;
8023d793cf1SPeter Wemm {
8033d793cf1SPeter Wemm     static  char *str   = "";
8043d793cf1SPeter Wemm     int	    escape_flag = 0;
8053d793cf1SPeter Wemm     char   *result;
80601c855caSPeter Wemm 
8073d793cf1SPeter Wemm /*
8083d793cf1SPeter Wemm  * If a string was specified then do initial processing.
8093d793cf1SPeter Wemm  */
8103d793cf1SPeter Wemm     if (s)
8113d793cf1SPeter Wemm 	str = s;
81201c855caSPeter Wemm 
8133d793cf1SPeter Wemm /*
8143d793cf1SPeter Wemm  * If this is the escape flag then reset it and ignore the character.
8153d793cf1SPeter Wemm  */
8163d793cf1SPeter Wemm     if (*str)
8173d793cf1SPeter Wemm 	result = str;
8183d793cf1SPeter Wemm     else
8193d793cf1SPeter Wemm 	result = (char *) 0;
8203d793cf1SPeter Wemm 
82101c855caSPeter Wemm     while (*str) {
82201c855caSPeter Wemm 	if (escape_flag) {
8233d793cf1SPeter Wemm 	    escape_flag = 0;
8243d793cf1SPeter Wemm 	    ++str;
8253d793cf1SPeter Wemm 	    continue;
8263d793cf1SPeter Wemm 	}
8273d793cf1SPeter Wemm 
82801c855caSPeter Wemm 	if (*str == '\\') {
8293d793cf1SPeter Wemm 	    ++str;
8303d793cf1SPeter Wemm 	    escape_flag = 1;
8313d793cf1SPeter Wemm 	    continue;
8323d793cf1SPeter Wemm 	}
83301c855caSPeter Wemm 
8343d793cf1SPeter Wemm /*
8353d793cf1SPeter Wemm  * If this is not in the termination string, continue.
8363d793cf1SPeter Wemm  */
83701c855caSPeter Wemm 	if (strchr (term, *str) == (char *) 0) {
8383d793cf1SPeter Wemm 	    ++str;
8393d793cf1SPeter Wemm 	    continue;
8403d793cf1SPeter Wemm 	}
84101c855caSPeter Wemm 
8423d793cf1SPeter Wemm /*
8433d793cf1SPeter Wemm  * This is the terminator. Mark the end of the string and stop.
8443d793cf1SPeter Wemm  */
8453d793cf1SPeter Wemm 	*str++ = '\0';
8463d793cf1SPeter Wemm 	break;
8473d793cf1SPeter Wemm     }
8483d793cf1SPeter Wemm     return (result);
8493d793cf1SPeter Wemm }
8503d793cf1SPeter Wemm 
8513d793cf1SPeter Wemm /*
8529b1aec48SLars Fredriksen  * Process the expect string
8539b1aec48SLars Fredriksen  */
8543d793cf1SPeter Wemm 
8559b1aec48SLars Fredriksen void chat_expect (s)
8563d793cf1SPeter Wemm char *s;
8579b1aec48SLars Fredriksen {
8583d793cf1SPeter Wemm     char *expect;
8593d793cf1SPeter Wemm     char *reply;
8603d793cf1SPeter Wemm 
86101c855caSPeter Wemm     if (strcmp(s, "HANGUP") == 0) {
8623d793cf1SPeter Wemm 	++hup_next;
8633d793cf1SPeter Wemm         return;
8643d793cf1SPeter Wemm     }
8653d793cf1SPeter Wemm 
86601c855caSPeter Wemm     if (strcmp(s, "ABORT") == 0) {
8679b1aec48SLars Fredriksen 	++abort_next;
8689b1aec48SLars Fredriksen 	return;
8699b1aec48SLars Fredriksen     }
8709b1aec48SLars Fredriksen 
87101c855caSPeter Wemm     if (strcmp(s, "CLR_ABORT") == 0) {
8723d793cf1SPeter Wemm 	++clear_abort_next;
8733d793cf1SPeter Wemm 	return;
8743d793cf1SPeter Wemm     }
8753d793cf1SPeter Wemm 
87601c855caSPeter Wemm     if (strcmp(s, "REPORT") == 0) {
8779f65f104SPeter Wemm 	++report_next;
8789f65f104SPeter Wemm 	return;
8799f65f104SPeter Wemm     }
8809f65f104SPeter Wemm 
88101c855caSPeter Wemm     if (strcmp(s, "CLR_REPORT") == 0) {
8823d793cf1SPeter Wemm 	++clear_report_next;
8833d793cf1SPeter Wemm 	return;
8843d793cf1SPeter Wemm     }
8853d793cf1SPeter Wemm 
88601c855caSPeter Wemm     if (strcmp(s, "TIMEOUT") == 0) {
8879b1aec48SLars Fredriksen 	++timeout_next;
8889b1aec48SLars Fredriksen 	return;
8899b1aec48SLars Fredriksen     }
8909b1aec48SLars Fredriksen 
89101c855caSPeter Wemm     if (strcmp(s, "ECHO") == 0) {
8923d793cf1SPeter Wemm 	++echo_next;
8933d793cf1SPeter Wemm 	return;
8943d793cf1SPeter Wemm     }
89501c855caSPeter Wemm 
89601c855caSPeter Wemm     if (strcmp(s, "SAY") == 0) {
8973d793cf1SPeter Wemm 	++say_next;
8983d793cf1SPeter Wemm 	return;
8993d793cf1SPeter Wemm     }
90001c855caSPeter Wemm 
9013d793cf1SPeter Wemm /*
9023d793cf1SPeter Wemm  * Fetch the expect and reply string.
9033d793cf1SPeter Wemm  */
90401c855caSPeter Wemm     for (;;) {
9053d793cf1SPeter Wemm 	expect = expect_strtok (s, "-");
9063d793cf1SPeter Wemm 	s      = (char *) 0;
9079b1aec48SLars Fredriksen 
9083d793cf1SPeter Wemm 	if (expect == (char *) 0)
9093d793cf1SPeter Wemm 	    return;
9103d793cf1SPeter Wemm 
9113d793cf1SPeter Wemm 	reply = expect_strtok (s, "-");
91201c855caSPeter Wemm 
9133d793cf1SPeter Wemm /*
9143d793cf1SPeter Wemm  * Handle the expect string. If successful then exit.
9153d793cf1SPeter Wemm  */
9163d793cf1SPeter Wemm 	if (get_string (expect))
9173d793cf1SPeter Wemm 	    return;
91801c855caSPeter Wemm 
9193d793cf1SPeter Wemm /*
9203d793cf1SPeter Wemm  * If there is a sub-reply string then send it. Otherwise any condition
9213d793cf1SPeter Wemm  * is terminal.
9223d793cf1SPeter Wemm  */
9233d793cf1SPeter Wemm 	if (reply == (char *) 0 || exit_code != 3)
9249b1aec48SLars Fredriksen 	    break;
9259b1aec48SLars Fredriksen 
9263d793cf1SPeter Wemm 	chat_send (reply);
9279f65f104SPeter Wemm     }
92801c855caSPeter Wemm 
9293d793cf1SPeter Wemm /*
9303d793cf1SPeter Wemm  * The expectation did not occur. This is terminal.
9313d793cf1SPeter Wemm  */
9329b1aec48SLars Fredriksen     if (fail_reason)
93301c855caSPeter Wemm 	logf("Failed (%s)", fail_reason);
9349b1aec48SLars Fredriksen     else
93501c855caSPeter Wemm 	logf("Failed");
9369f65f104SPeter Wemm     terminate(exit_code);
9379f65f104SPeter Wemm }
9383d793cf1SPeter Wemm 
9393d793cf1SPeter Wemm /*
9403d793cf1SPeter Wemm  * Translate the input character to the appropriate string for printing
9413d793cf1SPeter Wemm  * the data.
9423d793cf1SPeter Wemm  */
9439b1aec48SLars Fredriksen 
9449b1aec48SLars Fredriksen char *character(c)
9459f65f104SPeter Wemm int c;
9469b1aec48SLars Fredriksen {
9479b1aec48SLars Fredriksen     static char string[10];
9489b1aec48SLars Fredriksen     char *meta;
9499b1aec48SLars Fredriksen 
9509b1aec48SLars Fredriksen     meta = (c & 0x80) ? "M-" : "";
9519b1aec48SLars Fredriksen     c &= 0x7F;
9529b1aec48SLars Fredriksen 
9539b1aec48SLars Fredriksen     if (c < 32)
9549b1aec48SLars Fredriksen 	sprintf(string, "%s^%c", meta, (int)c + '@');
95501c855caSPeter Wemm     else if (c == 127)
9569b1aec48SLars Fredriksen 	sprintf(string, "%s^?", meta);
9579b1aec48SLars Fredriksen     else
9589b1aec48SLars Fredriksen 	sprintf(string, "%s%c", meta, c);
9599b1aec48SLars Fredriksen 
9609b1aec48SLars Fredriksen     return (string);
9619b1aec48SLars Fredriksen }
9629b1aec48SLars Fredriksen 
9639b1aec48SLars Fredriksen /*
9649b1aec48SLars Fredriksen  *  process the reply string
9659b1aec48SLars Fredriksen  */
9669b1aec48SLars Fredriksen void chat_send (s)
9679b1aec48SLars Fredriksen register char *s;
9689b1aec48SLars Fredriksen {
96901c855caSPeter Wemm     if (say_next) {
9703d793cf1SPeter Wemm 	say_next = 0;
9713d793cf1SPeter Wemm 	s = clean(s,0);
9723d793cf1SPeter Wemm 	write(2, s, strlen(s));
9733d793cf1SPeter Wemm         free(s);
9743d793cf1SPeter Wemm 	return;
9753d793cf1SPeter Wemm     }
97601c855caSPeter Wemm 
97701c855caSPeter Wemm     if (hup_next) {
9783d793cf1SPeter Wemm         hup_next = 0;
9793d793cf1SPeter Wemm 	if (strcmp(s, "OFF") == 0)
9803d793cf1SPeter Wemm            signal(SIGHUP, SIG_IGN);
9813d793cf1SPeter Wemm         else
9823d793cf1SPeter Wemm            signal(SIGHUP, sighup);
9833d793cf1SPeter Wemm         return;
9843d793cf1SPeter Wemm     }
98501c855caSPeter Wemm 
98601c855caSPeter Wemm     if (echo_next) {
9873d793cf1SPeter Wemm 	echo_next = 0;
9883d793cf1SPeter Wemm 	echo = (strcmp(s, "ON") == 0);
9893d793cf1SPeter Wemm 	return;
9903d793cf1SPeter Wemm     }
99101c855caSPeter Wemm 
99201c855caSPeter Wemm     if (abort_next) {
9939b1aec48SLars Fredriksen 	char *s1;
9949b1aec48SLars Fredriksen 
9959b1aec48SLars Fredriksen 	abort_next = 0;
9969b1aec48SLars Fredriksen 
9979b1aec48SLars Fredriksen 	if (n_aborts >= MAX_ABORTS)
99801c855caSPeter Wemm 	    fatal(2, "Too many ABORT strings");
9999b1aec48SLars Fredriksen 
10009b1aec48SLars Fredriksen 	s1 = clean(s, 0);
10019b1aec48SLars Fredriksen 
10029f65f104SPeter Wemm 	if (strlen(s1) > strlen(s)
10039f65f104SPeter Wemm 	    || strlen(s1) + 1 > sizeof(fail_buffer))
100401c855caSPeter Wemm 	    fatal(1, "Illegal or too-long ABORT string ('%v')", s);
10059b1aec48SLars Fredriksen 
10069b1aec48SLars Fredriksen 	abort_string[n_aborts++] = s1;
10079b1aec48SLars Fredriksen 
10089b1aec48SLars Fredriksen 	if (verbose)
100901c855caSPeter Wemm 	    logf("abort on (%v)", s);
10109f65f104SPeter Wemm 	return;
10119b1aec48SLars Fredriksen     }
10129f65f104SPeter Wemm 
101301c855caSPeter Wemm     if (clear_abort_next) {
10143d793cf1SPeter Wemm 	char *s1;
10153d793cf1SPeter Wemm 	int   i;
10163d793cf1SPeter Wemm         int   old_max;
10173d793cf1SPeter Wemm 	int   pack = 0;
10183d793cf1SPeter Wemm 
10193d793cf1SPeter Wemm 	clear_abort_next = 0;
10203d793cf1SPeter Wemm 
10213d793cf1SPeter Wemm 	s1 = clean(s, 0);
10223d793cf1SPeter Wemm 
10233d793cf1SPeter Wemm 	if (strlen(s1) > strlen(s)
10243d793cf1SPeter Wemm 	    || strlen(s1) + 1 > sizeof(fail_buffer))
102501c855caSPeter Wemm 	    fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
10263d793cf1SPeter Wemm 
10273d793cf1SPeter Wemm         old_max = n_aborts;
102801c855caSPeter Wemm 	for (i=0; i < n_aborts; i++) {
102901c855caSPeter Wemm 	    if ( strcmp(s1,abort_string[i]) == 0 ) {
10303d793cf1SPeter Wemm 		free(abort_string[i]);
10313d793cf1SPeter Wemm 		abort_string[i] = NULL;
10323d793cf1SPeter Wemm 		pack++;
10333d793cf1SPeter Wemm 		n_aborts--;
10343d793cf1SPeter Wemm 		if (verbose)
103501c855caSPeter Wemm 		    logf("clear abort on (%v)", s);
10363d793cf1SPeter Wemm 	    }
10373d793cf1SPeter Wemm 	}
10383d793cf1SPeter Wemm         free(s1);
103901c855caSPeter Wemm 	if (pack)
104001c855caSPeter Wemm 	    pack_array(abort_string,old_max);
10413d793cf1SPeter Wemm 	return;
10423d793cf1SPeter Wemm     }
10433d793cf1SPeter Wemm 
104401c855caSPeter Wemm     if (report_next) {
10459f65f104SPeter Wemm 	char *s1;
10469f65f104SPeter Wemm 
10479f65f104SPeter Wemm 	report_next = 0;
10489f65f104SPeter Wemm 	if (n_reports >= MAX_REPORTS)
104901c855caSPeter Wemm 	    fatal(2, "Too many REPORT strings");
10509f65f104SPeter Wemm 
10519f65f104SPeter Wemm 	s1 = clean(s, 0);
10529f65f104SPeter Wemm 
10539f65f104SPeter Wemm 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
105401c855caSPeter Wemm 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
10559f65f104SPeter Wemm 
10569f65f104SPeter Wemm 	report_string[n_reports++] = s1;
10579f65f104SPeter Wemm 
10589f65f104SPeter Wemm 	if (verbose)
105901c855caSPeter Wemm 	    logf("report (%v)", s);
10609f65f104SPeter Wemm 	return;
10619f65f104SPeter Wemm     }
10629f65f104SPeter Wemm 
106301c855caSPeter Wemm     if (clear_report_next) {
10643d793cf1SPeter Wemm 	char *s1;
10653d793cf1SPeter Wemm 	int   i;
10663d793cf1SPeter Wemm 	int   old_max;
10673d793cf1SPeter Wemm 	int   pack = 0;
10683d793cf1SPeter Wemm 
10693d793cf1SPeter Wemm 	clear_report_next = 0;
10703d793cf1SPeter Wemm 
10713d793cf1SPeter Wemm 	s1 = clean(s, 0);
10723d793cf1SPeter Wemm 
10733d793cf1SPeter Wemm 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
107401c855caSPeter Wemm 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
10753d793cf1SPeter Wemm 
10763d793cf1SPeter Wemm 	old_max = n_reports;
107701c855caSPeter Wemm 	for (i=0; i < n_reports; i++) {
107801c855caSPeter Wemm 	    if ( strcmp(s1,report_string[i]) == 0 ) {
10793d793cf1SPeter Wemm 		free(report_string[i]);
10803d793cf1SPeter Wemm 		report_string[i] = NULL;
10813d793cf1SPeter Wemm 		pack++;
10823d793cf1SPeter Wemm 		n_reports--;
10833d793cf1SPeter Wemm 		if (verbose)
108401c855caSPeter Wemm 		    logf("clear report (%v)", s);
10853d793cf1SPeter Wemm 	    }
10863d793cf1SPeter Wemm 	}
10873d793cf1SPeter Wemm         free(s1);
108801c855caSPeter Wemm         if (pack)
108901c855caSPeter Wemm 	    pack_array(report_string,old_max);
10903d793cf1SPeter Wemm 
10913d793cf1SPeter Wemm 	return;
10923d793cf1SPeter Wemm     }
10933d793cf1SPeter Wemm 
109401c855caSPeter Wemm     if (timeout_next) {
10959b1aec48SLars Fredriksen 	timeout_next = 0;
10969b1aec48SLars Fredriksen 	timeout = atoi(s);
10979b1aec48SLars Fredriksen 
10989b1aec48SLars Fredriksen 	if (timeout <= 0)
10999b1aec48SLars Fredriksen 	    timeout = DEFAULT_CHAT_TIMEOUT;
11009b1aec48SLars Fredriksen 
11019b1aec48SLars Fredriksen 	if (verbose)
110201c855caSPeter Wemm 	    logf("timeout set to %d seconds", timeout);
110301c855caSPeter Wemm 
11049f65f104SPeter Wemm 	return;
11059f65f104SPeter Wemm     }
11069f65f104SPeter Wemm 
11079f65f104SPeter Wemm     if (strcmp(s, "EOT") == 0)
11089f65f104SPeter Wemm 	s = "^D\\c";
110901c855caSPeter Wemm     else if (strcmp(s, "BREAK") == 0)
11109b1aec48SLars Fredriksen 	s = "\\K\\c";
11119f65f104SPeter Wemm 
11129b1aec48SLars Fredriksen     if (!put_string(s))
111301c855caSPeter Wemm 	fatal(1, "Failed");
11149b1aec48SLars Fredriksen }
11159b1aec48SLars Fredriksen 
11169b1aec48SLars Fredriksen int get_char()
11179b1aec48SLars Fredriksen {
11189b1aec48SLars Fredriksen     int status;
11199b1aec48SLars Fredriksen     char c;
11209b1aec48SLars Fredriksen 
11219b1aec48SLars Fredriksen     status = read(0, &c, 1);
11229b1aec48SLars Fredriksen 
112301c855caSPeter Wemm     switch (status) {
11249b1aec48SLars Fredriksen     case 1:
11259b1aec48SLars Fredriksen 	return ((int)c & 0x7F);
11269b1aec48SLars Fredriksen 
11279b1aec48SLars Fredriksen     default:
112801c855caSPeter Wemm 	logf("warning: read() on stdin returned %d", status);
11299b1aec48SLars Fredriksen 
11309b1aec48SLars Fredriksen     case -1:
11319b1aec48SLars Fredriksen 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
113201c855caSPeter Wemm 	    fatal(2, "Can't get file mode flags on stdin: %m");
113301c855caSPeter Wemm 
11349f65f104SPeter Wemm 	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
113501c855caSPeter Wemm 	    fatal(2, "Can't set file mode flags on stdin: %m");
11369b1aec48SLars Fredriksen 
11379b1aec48SLars Fredriksen 	return (-1);
11389b1aec48SLars Fredriksen     }
11399b1aec48SLars Fredriksen }
11409b1aec48SLars Fredriksen 
11419b1aec48SLars Fredriksen int put_char(c)
11429f65f104SPeter Wemm int c;
11439b1aec48SLars Fredriksen {
11449b1aec48SLars Fredriksen     int status;
11459f65f104SPeter Wemm     char ch = c;
11469b1aec48SLars Fredriksen 
11479f65f104SPeter Wemm     usleep(10000);		/* inter-character typing delay (?) */
11489b1aec48SLars Fredriksen 
11499f65f104SPeter Wemm     status = write(1, &ch, 1);
11509b1aec48SLars Fredriksen 
115101c855caSPeter Wemm     switch (status) {
11529b1aec48SLars Fredriksen     case 1:
11539b1aec48SLars Fredriksen 	return (0);
11549b1aec48SLars Fredriksen 
11559b1aec48SLars Fredriksen     default:
115601c855caSPeter Wemm 	logf("warning: write() on stdout returned %d", status);
11579b1aec48SLars Fredriksen 
11589b1aec48SLars Fredriksen     case -1:
11599b1aec48SLars Fredriksen 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
116001c855caSPeter Wemm 	    fatal(2, "Can't get file mode flags on stdin, %m");
116101c855caSPeter Wemm 
11629f65f104SPeter Wemm 	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
116301c855caSPeter Wemm 	    fatal(2, "Can't set file mode flags on stdin: %m");
11649b1aec48SLars Fredriksen 
11659b1aec48SLars Fredriksen 	return (-1);
11669b1aec48SLars Fredriksen     }
11679b1aec48SLars Fredriksen }
11689b1aec48SLars Fredriksen 
11699b1aec48SLars Fredriksen int write_char (c)
11709b1aec48SLars Fredriksen int c;
11719b1aec48SLars Fredriksen {
117201c855caSPeter Wemm     if (alarmed || put_char(c) < 0) {
11739f65f104SPeter Wemm 	alarm(0);
11749f65f104SPeter Wemm 	alarmed = 0;
11759b1aec48SLars Fredriksen 
117601c855caSPeter Wemm 	if (verbose) {
11779b1aec48SLars Fredriksen 	    if (errno == EINTR || errno == EWOULDBLOCK)
117801c855caSPeter Wemm 		logf(" -- write timed out");
11799b1aec48SLars Fredriksen 	    else
118001c855caSPeter Wemm 		logf(" -- write failed: %m");
11819f65f104SPeter Wemm 	}
11829b1aec48SLars Fredriksen 	return (0);
11839b1aec48SLars Fredriksen     }
11849b1aec48SLars Fredriksen     return (1);
11859b1aec48SLars Fredriksen }
11869b1aec48SLars Fredriksen 
11879b1aec48SLars Fredriksen int put_string (s)
11889b1aec48SLars Fredriksen register char *s;
11899b1aec48SLars Fredriksen {
11903d793cf1SPeter Wemm     quiet = 0;
11919b1aec48SLars Fredriksen     s = clean(s, 1);
11929b1aec48SLars Fredriksen 
119301c855caSPeter Wemm     if (verbose) {
11949b1aec48SLars Fredriksen 	if (quiet)
119501c855caSPeter Wemm 	    logf("send (??????)");
11969b1aec48SLars Fredriksen 	else
119701c855caSPeter Wemm 	    logf("send (%v)", s);
11989b1aec48SLars Fredriksen     }
11999b1aec48SLars Fredriksen 
12009b1aec48SLars Fredriksen     alarm(timeout); alarmed = 0;
12019b1aec48SLars Fredriksen 
120201c855caSPeter Wemm     while (*s) {
12039b1aec48SLars Fredriksen 	register char c = *s++;
12049b1aec48SLars Fredriksen 
120501c855caSPeter Wemm 	if (c != '\\') {
12069b1aec48SLars Fredriksen 	    if (!write_char (c))
12079b1aec48SLars Fredriksen 		return 0;
12089b1aec48SLars Fredriksen 	    continue;
12099b1aec48SLars Fredriksen 	}
12109b1aec48SLars Fredriksen 
12119b1aec48SLars Fredriksen 	c = *s++;
121201c855caSPeter Wemm 	switch (c) {
12139b1aec48SLars Fredriksen 	case 'd':
12149b1aec48SLars Fredriksen 	    sleep(1);
12159b1aec48SLars Fredriksen 	    break;
12169b1aec48SLars Fredriksen 
12179b1aec48SLars Fredriksen 	case 'K':
12189b1aec48SLars Fredriksen 	    break_sequence();
12199b1aec48SLars Fredriksen 	    break;
12209b1aec48SLars Fredriksen 
12219b1aec48SLars Fredriksen 	case 'p':
12229f65f104SPeter Wemm 	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
12239b1aec48SLars Fredriksen 	    break;
12249b1aec48SLars Fredriksen 
12259b1aec48SLars Fredriksen 	default:
12269b1aec48SLars Fredriksen 	    if (!write_char (c))
12279b1aec48SLars Fredriksen 		return 0;
12289b1aec48SLars Fredriksen 	    break;
12299b1aec48SLars Fredriksen 	}
12309b1aec48SLars Fredriksen     }
12319b1aec48SLars Fredriksen 
12329b1aec48SLars Fredriksen     alarm(0);
12339b1aec48SLars Fredriksen     alarmed = 0;
12349b1aec48SLars Fredriksen     return (1);
12359b1aec48SLars Fredriksen }
12369b1aec48SLars Fredriksen 
12379b1aec48SLars Fredriksen /*
12383d793cf1SPeter Wemm  *	Echo a character to stderr.
12393d793cf1SPeter Wemm  *	When called with -1, a '\n' character is generated when
12403d793cf1SPeter Wemm  *	the cursor is not at the beginning of a line.
12413d793cf1SPeter Wemm  */
12423d793cf1SPeter Wemm void echo_stderr(n)
12433d793cf1SPeter Wemm int n;
12443d793cf1SPeter Wemm {
12453d793cf1SPeter Wemm     static int need_lf;
12463d793cf1SPeter Wemm     char *s;
12473d793cf1SPeter Wemm 
124801c855caSPeter Wemm     switch (n) {
12493d793cf1SPeter Wemm     case '\r':		/* ignore '\r' */
12503d793cf1SPeter Wemm 	break;
12513d793cf1SPeter Wemm     case -1:
12523d793cf1SPeter Wemm 	if (need_lf == 0)
12533d793cf1SPeter Wemm 	    break;
12543d793cf1SPeter Wemm 	/* fall through */
12553d793cf1SPeter Wemm     case '\n':
12563d793cf1SPeter Wemm 	write(2, "\n", 1);
12573d793cf1SPeter Wemm 	need_lf = 0;
12583d793cf1SPeter Wemm 	break;
12593d793cf1SPeter Wemm     default:
12603d793cf1SPeter Wemm 	s = character(n);
12613d793cf1SPeter Wemm 	write(2, s, strlen(s));
12623d793cf1SPeter Wemm 	need_lf = 1;
12633d793cf1SPeter Wemm 	break;
12643d793cf1SPeter Wemm     }
12653d793cf1SPeter Wemm }
12663d793cf1SPeter Wemm 
12673d793cf1SPeter Wemm /*
12689b1aec48SLars Fredriksen  *	'Wait for' this string to appear on this file descriptor.
12699b1aec48SLars Fredriksen  */
12709b1aec48SLars Fredriksen int get_string(string)
12719b1aec48SLars Fredriksen register char *string;
12729b1aec48SLars Fredriksen {
12739b1aec48SLars Fredriksen     char temp[STR_LEN];
12749b1aec48SLars Fredriksen     int c, printed = 0, len, minlen;
12759b1aec48SLars Fredriksen     register char *s = temp, *end = s + STR_LEN;
127601c855caSPeter Wemm     char *logged = temp;
12779b1aec48SLars Fredriksen 
12789b1aec48SLars Fredriksen     fail_reason = (char *)0;
12794e83a8feSKris Kennaway 
12804e83a8feSKris Kennaway     if (strlen(string) > STR_LEN) {
12814e83a8feSKris Kennaway 	logf("expect string is too long");
12824e83a8feSKris Kennaway 	exit_code = 1;
12834e83a8feSKris Kennaway 	return 0;
12844e83a8feSKris Kennaway     }
12854e83a8feSKris Kennaway 
12869b1aec48SLars Fredriksen     string = clean(string, 0);
12879b1aec48SLars Fredriksen     len = strlen(string);
12889b1aec48SLars Fredriksen     minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
12899b1aec48SLars Fredriksen 
12909b1aec48SLars Fredriksen     if (verbose)
129101c855caSPeter Wemm 	logf("expect (%v)", string);
12929b1aec48SLars Fredriksen 
129301c855caSPeter Wemm     if (len == 0) {
12949b1aec48SLars Fredriksen 	if (verbose)
129501c855caSPeter Wemm 	    logf("got it");
12969b1aec48SLars Fredriksen 	return (1);
12979b1aec48SLars Fredriksen     }
12989b1aec48SLars Fredriksen 
12999f65f104SPeter Wemm     alarm(timeout);
13009f65f104SPeter Wemm     alarmed = 0;
13019b1aec48SLars Fredriksen 
130201c855caSPeter Wemm     while ( ! alarmed && (c = get_char()) >= 0) {
13039f65f104SPeter Wemm 	int n, abort_len, report_len;
13049b1aec48SLars Fredriksen 
13053d793cf1SPeter Wemm 	if (echo)
13063d793cf1SPeter Wemm 	    echo_stderr(c);
130701c855caSPeter Wemm 	if (verbose && c == '\n') {
130801c855caSPeter Wemm 	    if (s == logged)
130901c855caSPeter Wemm 		logf("");	/* blank line */
13109b1aec48SLars Fredriksen 	    else
131101c855caSPeter Wemm 		logf("%0.*v", s - logged, logged);
131201c855caSPeter Wemm 	    logged = s + 1;
13133d793cf1SPeter Wemm 	}
13143d793cf1SPeter Wemm 
13159b1aec48SLars Fredriksen 	*s++ = c;
13169b1aec48SLars Fredriksen 
131701c855caSPeter Wemm 	if (verbose && s >= logged + 80) {
131801c855caSPeter Wemm 	    logf("%0.*v", s - logged, logged);
131901c855caSPeter Wemm 	    logged = s;
132001c855caSPeter Wemm 	}
132101c855caSPeter Wemm 
132201c855caSPeter Wemm 	if (Verbose) {
132301c855caSPeter Wemm 	   if (c == '\n')
132401c855caSPeter Wemm 	       fputc( '\n', stderr );
132501c855caSPeter Wemm 	   else if (c != '\r')
132601c855caSPeter Wemm 	       fprintf( stderr, "%s", character(c) );
132701c855caSPeter Wemm 	}
132801c855caSPeter Wemm 
132901c855caSPeter Wemm 	if (!report_gathering) {
133001c855caSPeter Wemm 	    for (n = 0; n < n_reports; ++n) {
13319f65f104SPeter Wemm 		if ((report_string[n] != (char*) NULL) &&
13329f65f104SPeter Wemm 		    s - temp >= (report_len = strlen(report_string[n])) &&
133301c855caSPeter Wemm 		    strncmp(s - report_len, report_string[n], report_len) == 0) {
13349f65f104SPeter Wemm 		    time_t time_now   = time ((time_t*) NULL);
13359f65f104SPeter Wemm 		    struct tm* tm_now = localtime (&time_now);
13369f65f104SPeter Wemm 
13379f65f104SPeter Wemm 		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
13389f65f104SPeter Wemm 		    strcat (report_buffer, report_string[n]);
13399f65f104SPeter Wemm 
13409f65f104SPeter Wemm 		    report_string[n] = (char *) NULL;
13419f65f104SPeter Wemm 		    report_gathering = 1;
13429f65f104SPeter Wemm 		    break;
13439f65f104SPeter Wemm 		}
13449f65f104SPeter Wemm 	    }
13459f65f104SPeter Wemm 	}
134601c855caSPeter Wemm 	else {
134701c855caSPeter Wemm 	    if (!iscntrl (c)) {
13489f65f104SPeter Wemm 		int rep_len = strlen (report_buffer);
13499f65f104SPeter Wemm 		report_buffer[rep_len]     = c;
13509f65f104SPeter Wemm 		report_buffer[rep_len + 1] = '\0';
13519f65f104SPeter Wemm 	    }
135201c855caSPeter Wemm 	    else {
13539f65f104SPeter Wemm 		report_gathering = 0;
13549f65f104SPeter Wemm 		fprintf (report_fp, "chat:  %s\n", report_buffer);
13559f65f104SPeter Wemm 	    }
13569f65f104SPeter Wemm 	}
13579b1aec48SLars Fredriksen 
13583d793cf1SPeter Wemm 	if (s - temp >= len &&
13593d793cf1SPeter Wemm 	    c == string[len - 1] &&
136001c855caSPeter Wemm 	    strncmp(s - len, string, len) == 0) {
136101c855caSPeter Wemm 	    if (verbose) {
136201c855caSPeter Wemm 		if (s > logged)
136301c855caSPeter Wemm 		    logf("%0.*v", s - logged, logged);
13643d793cf1SPeter Wemm 		logf(" -- got it\n");
13653d793cf1SPeter Wemm 	    }
13663d793cf1SPeter Wemm 
13673d793cf1SPeter Wemm 	    alarm(0);
13683d793cf1SPeter Wemm 	    alarmed = 0;
13693d793cf1SPeter Wemm 	    return (1);
13703d793cf1SPeter Wemm 	}
13713d793cf1SPeter Wemm 
137201c855caSPeter Wemm 	for (n = 0; n < n_aborts; ++n) {
13733d793cf1SPeter Wemm 	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
137401c855caSPeter Wemm 		strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
137501c855caSPeter Wemm 		if (verbose) {
137601c855caSPeter Wemm 		    if (s > logged)
137701c855caSPeter Wemm 			logf("%0.*v", s - logged, logged);
137801c855caSPeter Wemm 		    logf(" -- failed");
13793d793cf1SPeter Wemm 		}
13803d793cf1SPeter Wemm 
13813d793cf1SPeter Wemm 		alarm(0);
13823d793cf1SPeter Wemm 		alarmed = 0;
13833d793cf1SPeter Wemm 		exit_code = n + 4;
13843d793cf1SPeter Wemm 		strcpy(fail_reason = fail_buffer, abort_string[n]);
13853d793cf1SPeter Wemm 		return (0);
13863d793cf1SPeter Wemm 	    }
13873d793cf1SPeter Wemm 	}
13883d793cf1SPeter Wemm 
138901c855caSPeter Wemm 	if (s >= end) {
139001c855caSPeter Wemm 	    if (logged < s - minlen) {
139101c855caSPeter Wemm 		logf("%0.*v", s - logged, logged);
139201c855caSPeter Wemm 		logged = s;
139301c855caSPeter Wemm 	    }
139401c855caSPeter Wemm 	    s -= minlen;
139501c855caSPeter Wemm 	    memmove(temp, s, minlen);
139601c855caSPeter Wemm 	    logged = temp + (logged - s);
13979b1aec48SLars Fredriksen 	    s = temp + minlen;
13989b1aec48SLars Fredriksen 	}
13999b1aec48SLars Fredriksen 
14009b1aec48SLars Fredriksen 	if (alarmed && verbose)
140101c855caSPeter Wemm 	    logf("warning: alarm synchronization problem");
14029f65f104SPeter Wemm     }
14039b1aec48SLars Fredriksen 
14049b1aec48SLars Fredriksen     alarm(0);
14059b1aec48SLars Fredriksen 
140601c855caSPeter Wemm     if (verbose && printed) {
14079b1aec48SLars Fredriksen 	if (alarmed)
140801c855caSPeter Wemm 	    logf(" -- read timed out");
14099b1aec48SLars Fredriksen 	else
141001c855caSPeter Wemm 	    logf(" -- read failed: %m");
14119b1aec48SLars Fredriksen     }
14129b1aec48SLars Fredriksen 
14139f65f104SPeter Wemm     exit_code = 3;
14149b1aec48SLars Fredriksen     alarmed   = 0;
14159b1aec48SLars Fredriksen     return (0);
14169b1aec48SLars Fredriksen }
14179b1aec48SLars Fredriksen 
14187288c503SPeter Wemm /*
14197288c503SPeter Wemm  * Gross kludge to handle Solaris versions >= 2.6 having usleep.
14207288c503SPeter Wemm  */
14217288c503SPeter Wemm #ifdef SOL2
14227288c503SPeter Wemm #include <sys/param.h>
14237288c503SPeter Wemm #if MAXUID > 65536		/* then this is Solaris 2.6 or later */
14247288c503SPeter Wemm #undef NO_USLEEP
14257288c503SPeter Wemm #endif
14267288c503SPeter Wemm #endif /* SOL2 */
14277288c503SPeter Wemm 
14289f65f104SPeter Wemm #ifdef NO_USLEEP
14299b1aec48SLars Fredriksen #include <sys/types.h>
14309b1aec48SLars Fredriksen #include <sys/time.h>
14319b1aec48SLars Fredriksen 
14329b1aec48SLars Fredriksen /*
14339b1aec48SLars Fredriksen   usleep -- support routine for 4.2BSD system call emulations
14349b1aec48SLars Fredriksen   last edit:  29-Oct-1984     D A Gwyn
14359b1aec48SLars Fredriksen   */
14369b1aec48SLars Fredriksen 
14379b1aec48SLars Fredriksen extern int	  select();
14389b1aec48SLars Fredriksen 
14399b1aec48SLars Fredriksen int
14409b1aec48SLars Fredriksen usleep( usec )				  /* returns 0 if ok, else -1 */
14419b1aec48SLars Fredriksen     long		usec;		/* delay in microseconds */
14429b1aec48SLars Fredriksen {
144301c855caSPeter Wemm     static struct {		/* `timeval' */
14449b1aec48SLars Fredriksen 	long	tv_sec;		/* seconds */
14459b1aec48SLars Fredriksen 	long	tv_usec;	/* microsecs */
14469b1aec48SLars Fredriksen     } delay;	    		/* _select() timeout */
14479b1aec48SLars Fredriksen 
14489b1aec48SLars Fredriksen     delay.tv_sec  = usec / 1000000L;
14499b1aec48SLars Fredriksen     delay.tv_usec = usec % 1000000L;
14509b1aec48SLars Fredriksen 
14519b1aec48SLars Fredriksen     return select(0, (long *)0, (long *)0, (long *)0, &delay);
14529b1aec48SLars Fredriksen }
14539b1aec48SLars Fredriksen #endif
14543d793cf1SPeter Wemm 
14553d793cf1SPeter Wemm void
14563d793cf1SPeter Wemm pack_array (array, end)
14573d793cf1SPeter Wemm     char **array; /* The address of the array of string pointers */
14583d793cf1SPeter Wemm     int    end;   /* The index of the next free entry before CLR_ */
14593d793cf1SPeter Wemm {
14603d793cf1SPeter Wemm     int i, j;
14613d793cf1SPeter Wemm 
14623d793cf1SPeter Wemm     for (i = 0; i < end; i++) {
14633d793cf1SPeter Wemm 	if (array[i] == NULL) {
14643d793cf1SPeter Wemm 	    for (j = i+1; j < end; ++j)
14653d793cf1SPeter Wemm 		if (array[j] != NULL)
14663d793cf1SPeter Wemm 		    array[i++] = array[j];
14673d793cf1SPeter Wemm 	    for (; i < end; ++i)
14683d793cf1SPeter Wemm 		array[i] = NULL;
14693d793cf1SPeter Wemm 	    break;
14703d793cf1SPeter Wemm 	}
14713d793cf1SPeter Wemm     }
14723d793cf1SPeter Wemm }
147301c855caSPeter Wemm 
147401c855caSPeter Wemm /*
147501c855caSPeter Wemm  * vfmtmsg - format a message into a buffer.  Like vsprintf except we
147601c855caSPeter Wemm  * also specify the length of the output buffer, and we handle the
147701c855caSPeter Wemm  * %m (error message) format.
147801c855caSPeter Wemm  * Doesn't do floating-point formats.
147901c855caSPeter Wemm  * Returns the number of chars put into buf.
148001c855caSPeter Wemm  */
148101c855caSPeter Wemm #define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)
148201c855caSPeter Wemm 
148301c855caSPeter Wemm int
148401c855caSPeter Wemm vfmtmsg(buf, buflen, fmt, args)
148501c855caSPeter Wemm     char *buf;
148601c855caSPeter Wemm     int buflen;
148701c855caSPeter Wemm     const char *fmt;
148801c855caSPeter Wemm     va_list args;
148901c855caSPeter Wemm {
149001c855caSPeter Wemm     int c, i, n;
149101c855caSPeter Wemm     int width, prec, fillch;
149201c855caSPeter Wemm     int base, len, neg, quoted;
149301c855caSPeter Wemm     unsigned long val = 0;
149401c855caSPeter Wemm     char *str, *buf0;
149501c855caSPeter Wemm     const char *f;
149601c855caSPeter Wemm     unsigned char *p;
149701c855caSPeter Wemm     char num[32];
149801c855caSPeter Wemm     static char hexchars[] = "0123456789abcdef";
149901c855caSPeter Wemm 
150001c855caSPeter Wemm     buf0 = buf;
150101c855caSPeter Wemm     --buflen;
150201c855caSPeter Wemm     while (buflen > 0) {
150301c855caSPeter Wemm 	for (f = fmt; *f != '%' && *f != 0; ++f)
150401c855caSPeter Wemm 	    ;
150501c855caSPeter Wemm 	if (f > fmt) {
150601c855caSPeter Wemm 	    len = f - fmt;
150701c855caSPeter Wemm 	    if (len > buflen)
150801c855caSPeter Wemm 		len = buflen;
150901c855caSPeter Wemm 	    memcpy(buf, fmt, len);
151001c855caSPeter Wemm 	    buf += len;
151101c855caSPeter Wemm 	    buflen -= len;
151201c855caSPeter Wemm 	    fmt = f;
151301c855caSPeter Wemm 	}
151401c855caSPeter Wemm 	if (*fmt == 0)
151501c855caSPeter Wemm 	    break;
151601c855caSPeter Wemm 	c = *++fmt;
151701c855caSPeter Wemm 	width = prec = 0;
151801c855caSPeter Wemm 	fillch = ' ';
151901c855caSPeter Wemm 	if (c == '0') {
152001c855caSPeter Wemm 	    fillch = '0';
152101c855caSPeter Wemm 	    c = *++fmt;
152201c855caSPeter Wemm 	}
152301c855caSPeter Wemm 	if (c == '*') {
152401c855caSPeter Wemm 	    width = va_arg(args, int);
152501c855caSPeter Wemm 	    c = *++fmt;
152601c855caSPeter Wemm 	} else {
152701c855caSPeter Wemm 	    while (isdigit(c)) {
152801c855caSPeter Wemm 		width = width * 10 + c - '0';
152901c855caSPeter Wemm 		c = *++fmt;
153001c855caSPeter Wemm 	    }
153101c855caSPeter Wemm 	}
153201c855caSPeter Wemm 	if (c == '.') {
153301c855caSPeter Wemm 	    c = *++fmt;
153401c855caSPeter Wemm 	    if (c == '*') {
153501c855caSPeter Wemm 		prec = va_arg(args, int);
153601c855caSPeter Wemm 		c = *++fmt;
153701c855caSPeter Wemm 	    } else {
153801c855caSPeter Wemm 		while (isdigit(c)) {
153901c855caSPeter Wemm 		    prec = prec * 10 + c - '0';
154001c855caSPeter Wemm 		    c = *++fmt;
154101c855caSPeter Wemm 		}
154201c855caSPeter Wemm 	    }
154301c855caSPeter Wemm 	}
154401c855caSPeter Wemm 	str = 0;
154501c855caSPeter Wemm 	base = 0;
154601c855caSPeter Wemm 	neg = 0;
154701c855caSPeter Wemm 	++fmt;
154801c855caSPeter Wemm 	switch (c) {
154901c855caSPeter Wemm 	case 'd':
155001c855caSPeter Wemm 	    i = va_arg(args, int);
155101c855caSPeter Wemm 	    if (i < 0) {
155201c855caSPeter Wemm 		neg = 1;
155301c855caSPeter Wemm 		val = -i;
155401c855caSPeter Wemm 	    } else
155501c855caSPeter Wemm 		val = i;
155601c855caSPeter Wemm 	    base = 10;
155701c855caSPeter Wemm 	    break;
155801c855caSPeter Wemm 	case 'o':
155901c855caSPeter Wemm 	    val = va_arg(args, unsigned int);
156001c855caSPeter Wemm 	    base = 8;
156101c855caSPeter Wemm 	    break;
156201c855caSPeter Wemm 	case 'x':
156301c855caSPeter Wemm 	    val = va_arg(args, unsigned int);
156401c855caSPeter Wemm 	    base = 16;
156501c855caSPeter Wemm 	    break;
156601c855caSPeter Wemm 	case 'p':
156701c855caSPeter Wemm 	    val = (unsigned long) va_arg(args, void *);
156801c855caSPeter Wemm 	    base = 16;
156901c855caSPeter Wemm 	    neg = 2;
157001c855caSPeter Wemm 	    break;
157101c855caSPeter Wemm 	case 's':
157201c855caSPeter Wemm 	    str = va_arg(args, char *);
157301c855caSPeter Wemm 	    break;
157401c855caSPeter Wemm 	case 'c':
157501c855caSPeter Wemm 	    num[0] = va_arg(args, int);
157601c855caSPeter Wemm 	    num[1] = 0;
157701c855caSPeter Wemm 	    str = num;
157801c855caSPeter Wemm 	    break;
157901c855caSPeter Wemm 	case 'm':
158001c855caSPeter Wemm 	    str = strerror(errno);
158101c855caSPeter Wemm 	    break;
158201c855caSPeter Wemm 	case 'v':		/* "visible" string */
158301c855caSPeter Wemm 	case 'q':		/* quoted string */
158401c855caSPeter Wemm 	    quoted = c == 'q';
158501c855caSPeter Wemm 	    p = va_arg(args, unsigned char *);
158601c855caSPeter Wemm 	    if (fillch == '0' && prec > 0) {
158701c855caSPeter Wemm 		n = prec;
158801c855caSPeter Wemm 	    } else {
158901c855caSPeter Wemm 		n = strlen((char *)p);
159001c855caSPeter Wemm 		if (prec > 0 && prec < n)
159101c855caSPeter Wemm 		    n = prec;
159201c855caSPeter Wemm 	    }
159301c855caSPeter Wemm 	    while (n > 0 && buflen > 0) {
159401c855caSPeter Wemm 		c = *p++;
159501c855caSPeter Wemm 		--n;
159601c855caSPeter Wemm 		if (!quoted && c >= 0x80) {
159701c855caSPeter Wemm 		    OUTCHAR('M');
159801c855caSPeter Wemm 		    OUTCHAR('-');
159901c855caSPeter Wemm 		    c -= 0x80;
160001c855caSPeter Wemm 		}
160101c855caSPeter Wemm 		if (quoted && (c == '"' || c == '\\'))
160201c855caSPeter Wemm 		    OUTCHAR('\\');
160301c855caSPeter Wemm 		if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
160401c855caSPeter Wemm 		    if (quoted) {
160501c855caSPeter Wemm 			OUTCHAR('\\');
160601c855caSPeter Wemm 			switch (c) {
160701c855caSPeter Wemm 			case '\t':	OUTCHAR('t');	break;
160801c855caSPeter Wemm 			case '\n':	OUTCHAR('n');	break;
160901c855caSPeter Wemm 			case '\b':	OUTCHAR('b');	break;
161001c855caSPeter Wemm 			case '\f':	OUTCHAR('f');	break;
161101c855caSPeter Wemm 			default:
161201c855caSPeter Wemm 			    OUTCHAR('x');
161301c855caSPeter Wemm 			    OUTCHAR(hexchars[c >> 4]);
161401c855caSPeter Wemm 			    OUTCHAR(hexchars[c & 0xf]);
161501c855caSPeter Wemm 			}
161601c855caSPeter Wemm 		    } else {
161701c855caSPeter Wemm 			if (c == '\t')
161801c855caSPeter Wemm 			    OUTCHAR(c);
161901c855caSPeter Wemm 			else {
162001c855caSPeter Wemm 			    OUTCHAR('^');
162101c855caSPeter Wemm 			    OUTCHAR(c ^ 0x40);
162201c855caSPeter Wemm 			}
162301c855caSPeter Wemm 		    }
162401c855caSPeter Wemm 		} else
162501c855caSPeter Wemm 		    OUTCHAR(c);
162601c855caSPeter Wemm 	    }
162701c855caSPeter Wemm 	    continue;
162801c855caSPeter Wemm 	default:
162901c855caSPeter Wemm 	    *buf++ = '%';
163001c855caSPeter Wemm 	    if (c != '%')
163101c855caSPeter Wemm 		--fmt;		/* so %z outputs %z etc. */
163201c855caSPeter Wemm 	    --buflen;
163301c855caSPeter Wemm 	    continue;
163401c855caSPeter Wemm 	}
163501c855caSPeter Wemm 	if (base != 0) {
163601c855caSPeter Wemm 	    str = num + sizeof(num);
163701c855caSPeter Wemm 	    *--str = 0;
163801c855caSPeter Wemm 	    while (str > num + neg) {
163901c855caSPeter Wemm 		*--str = hexchars[val % base];
164001c855caSPeter Wemm 		val = val / base;
164101c855caSPeter Wemm 		if (--prec <= 0 && val == 0)
164201c855caSPeter Wemm 		    break;
164301c855caSPeter Wemm 	    }
164401c855caSPeter Wemm 	    switch (neg) {
164501c855caSPeter Wemm 	    case 1:
164601c855caSPeter Wemm 		*--str = '-';
164701c855caSPeter Wemm 		break;
164801c855caSPeter Wemm 	    case 2:
164901c855caSPeter Wemm 		*--str = 'x';
165001c855caSPeter Wemm 		*--str = '0';
165101c855caSPeter Wemm 		break;
165201c855caSPeter Wemm 	    }
165301c855caSPeter Wemm 	    len = num + sizeof(num) - 1 - str;
165401c855caSPeter Wemm 	} else {
165501c855caSPeter Wemm 	    len = strlen(str);
165601c855caSPeter Wemm 	    if (prec > 0 && len > prec)
165701c855caSPeter Wemm 		len = prec;
165801c855caSPeter Wemm 	}
165901c855caSPeter Wemm 	if (width > 0) {
166001c855caSPeter Wemm 	    if (width > buflen)
166101c855caSPeter Wemm 		width = buflen;
166201c855caSPeter Wemm 	    if ((n = width - len) > 0) {
166301c855caSPeter Wemm 		buflen -= n;
166401c855caSPeter Wemm 		for (; n > 0; --n)
166501c855caSPeter Wemm 		    *buf++ = fillch;
166601c855caSPeter Wemm 	    }
166701c855caSPeter Wemm 	}
166801c855caSPeter Wemm 	if (len > buflen)
166901c855caSPeter Wemm 	    len = buflen;
167001c855caSPeter Wemm 	memcpy(buf, str, len);
167101c855caSPeter Wemm 	buf += len;
167201c855caSPeter Wemm 	buflen -= len;
167301c855caSPeter Wemm     }
167401c855caSPeter Wemm     *buf = 0;
167501c855caSPeter Wemm     return buf - buf0;
167601c855caSPeter Wemm }
1677