xref: /freebsd/usr.bin/chat/chat.c (revision 93b0017f88462f9af15368440324aa3be58eb18f)
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 
11601c855caSPeter Wemm #include <stdarg.h>
1179b1aec48SLars Fredriksen 
1189f65f104SPeter Wemm #ifndef O_NONBLOCK
1199f65f104SPeter Wemm #define O_NONBLOCK	O_NDELAY
1209f65f104SPeter Wemm #endif
1219f65f104SPeter Wemm 
12201c855caSPeter Wemm #ifdef SUNOS
12301c855caSPeter Wemm extern int sys_nerr;
12401c855caSPeter Wemm extern char *sys_errlist[];
12501c855caSPeter Wemm #define memmove(to, from, n)	bcopy(from, to, n)
12601c855caSPeter Wemm #define strerror(n)		((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
12701c855caSPeter Wemm 				 "unknown error")
12801c855caSPeter Wemm #endif
12901c855caSPeter Wemm 
1309b1aec48SLars Fredriksen /*************** Micro getopt() *********************************************/
1319b1aec48SLars Fredriksen #define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
1329b1aec48SLars Fredriksen 				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
1339b1aec48SLars Fredriksen 				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
1349b1aec48SLars Fredriksen #define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
1359b1aec48SLars Fredriksen 				(_O=4,(char*)0):(char*)0)
1369b1aec48SLars Fredriksen #define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
1379b1aec48SLars Fredriksen #define	ARG(c,v)	(c?(--c,*v++):(char*)0)
1389b1aec48SLars Fredriksen 
1399b1aec48SLars Fredriksen static int _O = 0;		/* Internal state */
1409b1aec48SLars Fredriksen /*************** Micro getopt() *********************************************/
1419b1aec48SLars Fredriksen 
1429b1aec48SLars Fredriksen #define	MAX_ABORTS		50
1439f65f104SPeter Wemm #define	MAX_REPORTS		50
1449b1aec48SLars Fredriksen #define	DEFAULT_CHAT_TIMEOUT	45
1459b1aec48SLars Fredriksen 
1463d793cf1SPeter Wemm int echo          = 0;
1479b1aec48SLars Fredriksen int verbose       = 0;
14801c855caSPeter Wemm int to_log        = 1;
14901c855caSPeter Wemm int to_stderr     = 0;
1503d793cf1SPeter Wemm int Verbose       = 0;
1519b1aec48SLars Fredriksen int quiet         = 0;
1529f65f104SPeter Wemm int report        = 0;
1539f65f104SPeter Wemm int exit_code     = 0;
1549f65f104SPeter Wemm FILE* report_fp   = (FILE *) 0;
1559f65f104SPeter Wemm char *report_file = (char *) 0;
1569b1aec48SLars Fredriksen char *chat_file   = (char *) 0;
15701c855caSPeter Wemm char *phone_num   = (char *) 0;
15801c855caSPeter Wemm char *phone_num2  = (char *) 0;
1599b1aec48SLars Fredriksen int timeout       = DEFAULT_CHAT_TIMEOUT;
1609b1aec48SLars Fredriksen 
1619b1aec48SLars Fredriksen int have_tty_parameters = 0;
1629f65f104SPeter Wemm 
1639b1aec48SLars Fredriksen #ifdef TERMIO
1649f65f104SPeter Wemm #define term_parms struct termio
1659f65f104SPeter Wemm #define get_term_param(param) ioctl(0, TCGETA, param)
1669f65f104SPeter Wemm #define set_term_param(param) ioctl(0, TCSETA, param)
1679b1aec48SLars Fredriksen struct termio saved_tty_parameters;
1689b1aec48SLars Fredriksen #endif
1699f65f104SPeter Wemm 
1709b1aec48SLars Fredriksen #ifdef TERMIOS
1719f65f104SPeter Wemm #define term_parms struct termios
1729f65f104SPeter Wemm #define get_term_param(param) tcgetattr(0, param)
1739f65f104SPeter Wemm #define set_term_param(param) tcsetattr(0, TCSANOW, param)
1749b1aec48SLars Fredriksen struct termios saved_tty_parameters;
1759b1aec48SLars Fredriksen #endif
1769b1aec48SLars Fredriksen 
1779b1aec48SLars Fredriksen char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
1789b1aec48SLars Fredriksen 	fail_buffer[50];
1793d793cf1SPeter Wemm int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
1803d793cf1SPeter Wemm int clear_abort_next = 0;
1819b1aec48SLars Fredriksen 
1829f65f104SPeter Wemm char *report_string[MAX_REPORTS] ;
1839f65f104SPeter Wemm char  report_buffer[50] ;
1849f65f104SPeter Wemm int n_reports = 0, report_next = 0, report_gathering = 0 ;
1853d793cf1SPeter Wemm int clear_report_next = 0;
1863d793cf1SPeter Wemm 
1873d793cf1SPeter Wemm int say_next = 0, hup_next = 0;
1889f65f104SPeter Wemm 
189f1bb2cd2SWarner Losh void *dup_mem(void *b, size_t c);
190f1bb2cd2SWarner Losh void *copy_of(char *s);
191f1bb2cd2SWarner Losh static void usage(void);
192f1bb2cd2SWarner Losh void logf(const char *fmt, ...);
193f1bb2cd2SWarner Losh void fatal(int code, const char *fmt, ...);
194f1bb2cd2SWarner Losh SIGTYPE sigalrm(int signo);
195f1bb2cd2SWarner Losh SIGTYPE sigint(int signo);
196f1bb2cd2SWarner Losh SIGTYPE sigterm(int signo);
197f1bb2cd2SWarner Losh SIGTYPE sighup(int signo);
198f1bb2cd2SWarner Losh void unalarm(void);
199f1bb2cd2SWarner Losh void init(void);
200f1bb2cd2SWarner Losh void set_tty_parameters(void);
201f1bb2cd2SWarner Losh void echo_stderr(int);
202f1bb2cd2SWarner Losh void break_sequence(void);
203f1bb2cd2SWarner Losh void terminate(int status);
204f1bb2cd2SWarner Losh void do_file(char *chat_file);
205f1bb2cd2SWarner Losh int  get_string(register char *string);
206f1bb2cd2SWarner Losh int  put_string(register char *s);
207f1bb2cd2SWarner Losh int  write_char(int c);
208f1bb2cd2SWarner Losh int  put_char(int c);
209f1bb2cd2SWarner Losh int  get_char(void);
210f1bb2cd2SWarner Losh void chat_send(register char *s);
211f1bb2cd2SWarner Losh char *character(int c);
212f1bb2cd2SWarner Losh void chat_expect(register char *s);
213f1bb2cd2SWarner Losh char *clean(register char *s, int sending);
214f1bb2cd2SWarner Losh void break_sequence(void);
215f1bb2cd2SWarner Losh void terminate(int status);
216f1bb2cd2SWarner Losh void pack_array(char **array, int end);
217f1bb2cd2SWarner Losh char *expect_strtok(char *, char *);
218f1bb2cd2SWarner Losh int vfmtmsg(char *, int, const char *, va_list);	/* vsprintf++ */
2193d793cf1SPeter Wemm 
2209b1aec48SLars Fredriksen void *dup_mem(b, c)
2219b1aec48SLars Fredriksen void *b;
2229b1aec48SLars Fredriksen size_t c;
2239b1aec48SLars Fredriksen {
2249b1aec48SLars Fredriksen     void *ans = malloc (c);
2259b1aec48SLars Fredriksen     if (!ans)
22601c855caSPeter Wemm 	fatal(2, "memory error!");
22701c855caSPeter Wemm 
2289b1aec48SLars Fredriksen     memcpy (ans, b, c);
2299b1aec48SLars Fredriksen     return ans;
2309b1aec48SLars Fredriksen }
2319b1aec48SLars Fredriksen 
2329b1aec48SLars Fredriksen void *copy_of (s)
2339b1aec48SLars Fredriksen char *s;
2349b1aec48SLars Fredriksen {
2359b1aec48SLars Fredriksen     return dup_mem (s, strlen (s) + 1);
2369b1aec48SLars Fredriksen }
2379b1aec48SLars Fredriksen 
2389b1aec48SLars Fredriksen /*
23901c855caSPeter Wemm  * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \
24001c855caSPeter Wemm  * [ -r report-file ] \
2419b1aec48SLars Fredriksen  *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
2429b1aec48SLars Fredriksen  *
2439b1aec48SLars Fredriksen  *	Perform a UUCP-dialer-like chat script on stdin and stdout.
2449b1aec48SLars Fredriksen  */
2459b1aec48SLars Fredriksen int
2469b1aec48SLars Fredriksen main(argc, argv)
2479b1aec48SLars Fredriksen      int argc;
2489b1aec48SLars Fredriksen      char **argv;
2499b1aec48SLars Fredriksen {
2509b1aec48SLars Fredriksen     int option;
2519b1aec48SLars Fredriksen     char *arg;
2529b1aec48SLars Fredriksen 
2539f65f104SPeter Wemm     tzset();
2549b1aec48SLars Fredriksen 
25501c855caSPeter Wemm     while ((option = OPTION(argc, argv)) != 0) {
25601c855caSPeter Wemm 	switch (option) {
2573d793cf1SPeter Wemm 	case 'e':
2583d793cf1SPeter Wemm 	    ++echo;
2593d793cf1SPeter Wemm 	    break;
2603d793cf1SPeter Wemm 
2619b1aec48SLars Fredriksen 	case 'v':
2629b1aec48SLars Fredriksen 	    ++verbose;
2639b1aec48SLars Fredriksen 	    break;
2649b1aec48SLars Fredriksen 
2653d793cf1SPeter Wemm 	case 'V':
2663d793cf1SPeter Wemm 	    ++Verbose;
2673d793cf1SPeter Wemm 	    break;
2683d793cf1SPeter Wemm 
26901c855caSPeter Wemm 	case 's':
27001c855caSPeter Wemm 	    ++to_stderr;
27101c855caSPeter Wemm 	    break;
27201c855caSPeter Wemm 
27301c855caSPeter Wemm 	case 'S':
27401c855caSPeter Wemm 	    to_log = 0;
27501c855caSPeter Wemm 	    break;
27601c855caSPeter Wemm 
2779b1aec48SLars Fredriksen 	case 'f':
2783d793cf1SPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
2799b1aec48SLars Fredriksen 		    chat_file = copy_of(arg);
2809b1aec48SLars Fredriksen 	    else
2819b1aec48SLars Fredriksen 		usage();
2829b1aec48SLars Fredriksen 	    break;
2839b1aec48SLars Fredriksen 
2849b1aec48SLars Fredriksen 	case 't':
2853d793cf1SPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
2869b1aec48SLars Fredriksen 		timeout = atoi(arg);
2879b1aec48SLars Fredriksen 	    else
2889b1aec48SLars Fredriksen 		usage();
2899f65f104SPeter Wemm 	    break;
2909b1aec48SLars Fredriksen 
2919f65f104SPeter Wemm 	case 'r':
2929f65f104SPeter Wemm 	    arg = OPTARG (argc, argv);
29301c855caSPeter Wemm 	    if (arg) {
2949f65f104SPeter Wemm 		if (report_fp != NULL)
2959f65f104SPeter Wemm 		    fclose (report_fp);
2969f65f104SPeter Wemm 		report_file = copy_of (arg);
2979f65f104SPeter Wemm 		report_fp   = fopen (report_file, "a");
29801c855caSPeter Wemm 		if (report_fp != NULL) {
2999f65f104SPeter Wemm 		    if (verbose)
3009f65f104SPeter Wemm 			fprintf (report_fp, "Opening \"%s\"...\n",
3019f65f104SPeter Wemm 				 report_file);
3029f65f104SPeter Wemm 		    report = 1;
3039f65f104SPeter Wemm 		}
3049f65f104SPeter Wemm 	    }
3059b1aec48SLars Fredriksen 	    break;
3069b1aec48SLars Fredriksen 
30701c855caSPeter Wemm 	case 'T':
30801c855caSPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
30901c855caSPeter Wemm 		phone_num = copy_of(arg);
31001c855caSPeter Wemm 	    else
31101c855caSPeter Wemm 		usage();
31201c855caSPeter Wemm 	    break;
31301c855caSPeter Wemm 
31401c855caSPeter Wemm 	case 'U':
31501c855caSPeter Wemm 	    if ((arg = OPTARG(argc, argv)) != NULL)
31601c855caSPeter Wemm 		phone_num2 = copy_of(arg);
31701c855caSPeter Wemm 	    else
31801c855caSPeter Wemm 		usage();
31901c855caSPeter Wemm 	    break;
32001c855caSPeter Wemm 
3219b1aec48SLars Fredriksen 	default:
3229b1aec48SLars Fredriksen 	    usage();
3239f65f104SPeter Wemm 	    break;
3249f65f104SPeter Wemm 	}
3259f65f104SPeter Wemm     }
3269f65f104SPeter Wemm /*
3279f65f104SPeter Wemm  * Default the report file to the stderr location
3289f65f104SPeter Wemm  */
3299f65f104SPeter Wemm     if (report_fp == NULL)
3309f65f104SPeter Wemm 	report_fp = stderr;
3319b1aec48SLars Fredriksen 
33201c855caSPeter Wemm     if (to_log) {
3339b1aec48SLars Fredriksen #ifdef ultrix
3349b1aec48SLars Fredriksen 	openlog("chat", LOG_PID);
3359b1aec48SLars Fredriksen #else
3369b1aec48SLars Fredriksen 	openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
3379b1aec48SLars Fredriksen 
3389f65f104SPeter Wemm 	if (verbose)
3399b1aec48SLars Fredriksen 	    setlogmask(LOG_UPTO(LOG_INFO));
3409f65f104SPeter Wemm 	else
3419b1aec48SLars Fredriksen 	    setlogmask(LOG_UPTO(LOG_WARNING));
3429b1aec48SLars Fredriksen #endif
34301c855caSPeter Wemm     }
3449b1aec48SLars Fredriksen 
3459b1aec48SLars Fredriksen     init();
3469b1aec48SLars Fredriksen 
34701c855caSPeter Wemm     if (chat_file != NULL) {
3489b1aec48SLars Fredriksen 	arg = ARG(argc, argv);
3499b1aec48SLars Fredriksen 	if (arg != NULL)
3509b1aec48SLars Fredriksen 	    usage();
3519b1aec48SLars Fredriksen 	else
3529b1aec48SLars Fredriksen 	    do_file (chat_file);
35301c855caSPeter Wemm     } else {
35401c855caSPeter Wemm 	while ((arg = ARG(argc, argv)) != NULL) {
3559b1aec48SLars Fredriksen 	    chat_expect(arg);
3569b1aec48SLars Fredriksen 
3573d793cf1SPeter Wemm 	    if ((arg = ARG(argc, argv)) != NULL)
3589b1aec48SLars Fredriksen 		chat_send(arg);
3599b1aec48SLars Fredriksen 	}
3609b1aec48SLars Fredriksen     }
3619b1aec48SLars Fredriksen 
3629b1aec48SLars Fredriksen     terminate(0);
3633d793cf1SPeter Wemm     return 0;
3649b1aec48SLars Fredriksen }
3659b1aec48SLars Fredriksen 
3669b1aec48SLars Fredriksen /*
3679b1aec48SLars Fredriksen  *  Process a chat script when read from a file.
3689b1aec48SLars Fredriksen  */
3699b1aec48SLars Fredriksen 
3709b1aec48SLars Fredriksen void do_file (chat_file)
3719b1aec48SLars Fredriksen char *chat_file;
3729b1aec48SLars Fredriksen {
373d7d10053SAlexander Langer     int linect, sendflg;
3749b1aec48SLars Fredriksen     char *sp, *arg, quote;
3759b1aec48SLars Fredriksen     char buf [STR_LEN];
3769b1aec48SLars Fredriksen     FILE *cfp;
3779b1aec48SLars Fredriksen 
3789f65f104SPeter Wemm     cfp = fopen (chat_file, "r");
3799f65f104SPeter Wemm     if (cfp == NULL)
38001c855caSPeter Wemm 	fatal(1, "%s -- open failed: %m", chat_file);
3819b1aec48SLars Fredriksen 
3829b1aec48SLars Fredriksen     linect = 0;
3839b1aec48SLars Fredriksen     sendflg = 0;
3849b1aec48SLars Fredriksen 
38501c855caSPeter Wemm     while (fgets(buf, STR_LEN, cfp) != NULL) {
3869b1aec48SLars Fredriksen 	sp = strchr (buf, '\n');
3879b1aec48SLars Fredriksen 	if (sp)
3889b1aec48SLars Fredriksen 	    *sp = '\0';
3899b1aec48SLars Fredriksen 
3909b1aec48SLars Fredriksen 	linect++;
3919b1aec48SLars Fredriksen 	sp = buf;
3923d793cf1SPeter Wemm 
3933d793cf1SPeter Wemm         /* lines starting with '#' are comments. If a real '#'
3943d793cf1SPeter Wemm            is to be expected, it should be quoted .... */
39501c855caSPeter Wemm         if ( *sp == '#' )
39601c855caSPeter Wemm 	    continue;
3973d793cf1SPeter Wemm 
39801c855caSPeter Wemm 	while (*sp != '\0') {
39901c855caSPeter Wemm 	    if (*sp == ' ' || *sp == '\t') {
4009b1aec48SLars Fredriksen 		++sp;
4019b1aec48SLars Fredriksen 		continue;
4029b1aec48SLars Fredriksen 	    }
4039b1aec48SLars Fredriksen 
40401c855caSPeter Wemm 	    if (*sp == '"' || *sp == '\'') {
4059b1aec48SLars Fredriksen 		quote = *sp++;
4069b1aec48SLars Fredriksen 		arg = sp;
40701c855caSPeter Wemm 		while (*sp != quote) {
4089b1aec48SLars Fredriksen 		    if (*sp == '\0')
40901c855caSPeter Wemm 			fatal(1, "unterminated quote (line %d)", linect);
4109b1aec48SLars Fredriksen 
41101c855caSPeter Wemm 		    if (*sp++ == '\\') {
4129b1aec48SLars Fredriksen 			if (*sp != '\0')
4139b1aec48SLars Fredriksen 			    ++sp;
4149b1aec48SLars Fredriksen 		    }
4159b1aec48SLars Fredriksen 		}
4169f65f104SPeter Wemm 	    }
41701c855caSPeter Wemm 	    else {
4189b1aec48SLars Fredriksen 		arg = sp;
4199b1aec48SLars Fredriksen 		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
4209b1aec48SLars Fredriksen 		    ++sp;
4219b1aec48SLars Fredriksen 	    }
4229b1aec48SLars Fredriksen 
4239b1aec48SLars Fredriksen 	    if (*sp != '\0')
4249b1aec48SLars Fredriksen 		*sp++ = '\0';
4259b1aec48SLars Fredriksen 
4269b1aec48SLars Fredriksen 	    if (sendflg)
4279b1aec48SLars Fredriksen 		chat_send (arg);
4289b1aec48SLars Fredriksen 	    else
4299b1aec48SLars Fredriksen 		chat_expect (arg);
4309b1aec48SLars Fredriksen 	    sendflg = !sendflg;
4319b1aec48SLars Fredriksen 	}
4329b1aec48SLars Fredriksen     }
4339b1aec48SLars Fredriksen     fclose (cfp);
4349b1aec48SLars Fredriksen }
4359b1aec48SLars Fredriksen 
4369b1aec48SLars Fredriksen /*
4379b1aec48SLars Fredriksen  *	We got an error parsing the command line.
4389b1aec48SLars Fredriksen  */
439afaeb553SPhilippe Charnier static void
440afaeb553SPhilippe Charnier usage()
4419b1aec48SLars Fredriksen {
44201c855caSPeter Wemm     fprintf(stderr, "\
44301c855caSPeter Wemm Usage: chat [-e] [-v] [-V] [-t timeout] [-r report-file] [-T phone-number]\n\
44401c855caSPeter Wemm      [-U phone-number2] {-f chat-file | chat-script}\n");
4459b1aec48SLars Fredriksen     exit(1);
4469b1aec48SLars Fredriksen }
4479b1aec48SLars Fredriksen 
44801c855caSPeter Wemm char line[1024];
4499b1aec48SLars Fredriksen 
4509b1aec48SLars Fredriksen /*
45101c855caSPeter Wemm  * Send a message to syslog and/or stderr.
4529b1aec48SLars Fredriksen  */
453f1bb2cd2SWarner Losh void logf(const char *fmt, ...)
4549b1aec48SLars Fredriksen {
45501c855caSPeter Wemm     va_list args;
45601c855caSPeter Wemm 
45701c855caSPeter Wemm     va_start(args, fmt);
45801c855caSPeter Wemm     vfmtmsg(line, sizeof(line), fmt, args);
45901c855caSPeter Wemm     if (to_log)
46001c855caSPeter Wemm 	syslog(LOG_INFO, "%s", line);
46101c855caSPeter Wemm     if (to_stderr)
46201c855caSPeter Wemm 	fprintf(stderr, "%s\n", line);
4639b1aec48SLars Fredriksen }
4649b1aec48SLars Fredriksen 
4659b1aec48SLars Fredriksen /*
4669b1aec48SLars Fredriksen  *	Print an error message and terminate.
4679b1aec48SLars Fredriksen  */
4689b1aec48SLars Fredriksen 
469f1bb2cd2SWarner Losh void fatal(int code, const char *fmt, ...)
4709b1aec48SLars Fredriksen {
47101c855caSPeter Wemm     va_list args;
4729b1aec48SLars Fredriksen 
47301c855caSPeter Wemm     va_start(args, fmt);
47401c855caSPeter Wemm     vfmtmsg(line, sizeof(line), fmt, args);
47501c855caSPeter Wemm     if (to_log)
47601c855caSPeter Wemm 	syslog(LOG_ERR, "%s", line);
47701c855caSPeter Wemm     if (to_stderr)
47801c855caSPeter Wemm 	fprintf(stderr, "%s\n", line);
47901c855caSPeter Wemm     terminate(code);
4809b1aec48SLars Fredriksen }
4819b1aec48SLars Fredriksen 
4829b1aec48SLars Fredriksen int alarmed = 0;
4839b1aec48SLars Fredriksen 
4849b1aec48SLars Fredriksen SIGTYPE sigalrm(signo)
4859b1aec48SLars Fredriksen int signo;
4869b1aec48SLars Fredriksen {
4879b1aec48SLars Fredriksen     int flags;
4889b1aec48SLars Fredriksen 
4899b1aec48SLars Fredriksen     alarm(1);
4909b1aec48SLars Fredriksen     alarmed = 1;		/* Reset alarm to avoid race window */
4919b1aec48SLars Fredriksen     signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
4929b1aec48SLars Fredriksen 
4939b1aec48SLars Fredriksen     if ((flags = fcntl(0, F_GETFL, 0)) == -1)
49401c855caSPeter Wemm 	fatal(2, "Can't get file mode flags on stdin: %m");
49501c855caSPeter Wemm 
4969f65f104SPeter Wemm     if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
49701c855caSPeter Wemm 	fatal(2, "Can't set file mode flags on stdin: %m");
4989b1aec48SLars Fredriksen 
4999b1aec48SLars Fredriksen     if (verbose)
50001c855caSPeter Wemm 	logf("alarm");
5019b1aec48SLars Fredriksen }
5029b1aec48SLars Fredriksen 
5039b1aec48SLars Fredriksen void unalarm()
5049b1aec48SLars Fredriksen {
5059b1aec48SLars Fredriksen     int flags;
5069b1aec48SLars Fredriksen 
5079b1aec48SLars Fredriksen     if ((flags = fcntl(0, F_GETFL, 0)) == -1)
50801c855caSPeter Wemm 	fatal(2, "Can't get file mode flags on stdin: %m");
50901c855caSPeter Wemm 
5109f65f104SPeter Wemm     if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
51101c855caSPeter Wemm 	fatal(2, "Can't set file mode flags on stdin: %m");
5129f65f104SPeter Wemm }
5139b1aec48SLars Fredriksen 
5149b1aec48SLars Fredriksen SIGTYPE sigint(signo)
5159b1aec48SLars Fredriksen int signo;
5169b1aec48SLars Fredriksen {
51701c855caSPeter Wemm     fatal(2, "SIGINT");
5189b1aec48SLars Fredriksen }
5199b1aec48SLars Fredriksen 
5209b1aec48SLars Fredriksen SIGTYPE sigterm(signo)
5219b1aec48SLars Fredriksen int signo;
5229b1aec48SLars Fredriksen {
52301c855caSPeter Wemm     fatal(2, "SIGTERM");
5249b1aec48SLars Fredriksen }
5259b1aec48SLars Fredriksen 
5269b1aec48SLars Fredriksen SIGTYPE sighup(signo)
5279b1aec48SLars Fredriksen int signo;
5289b1aec48SLars Fredriksen {
52901c855caSPeter Wemm     fatal(2, "SIGHUP");
5309b1aec48SLars Fredriksen }
5319b1aec48SLars Fredriksen 
5329b1aec48SLars Fredriksen void init()
5339b1aec48SLars Fredriksen {
5349b1aec48SLars Fredriksen     signal(SIGINT, sigint);
5359b1aec48SLars Fredriksen     signal(SIGTERM, sigterm);
5369b1aec48SLars Fredriksen     signal(SIGHUP, sighup);
5379b1aec48SLars Fredriksen 
5389b1aec48SLars Fredriksen     set_tty_parameters();
5399b1aec48SLars Fredriksen     signal(SIGALRM, sigalrm);
5409b1aec48SLars Fredriksen     alarm(0);
5419b1aec48SLars Fredriksen     alarmed = 0;
5429b1aec48SLars Fredriksen }
5439b1aec48SLars Fredriksen 
5449b1aec48SLars Fredriksen void set_tty_parameters()
5459b1aec48SLars Fredriksen {
5469f65f104SPeter Wemm #if defined(get_term_param)
5479f65f104SPeter Wemm     term_parms t;
5489b1aec48SLars Fredriksen 
5499f65f104SPeter Wemm     if (get_term_param (&t) < 0)
55001c855caSPeter Wemm 	fatal(2, "Can't get terminal parameters: %m");
5519b1aec48SLars Fredriksen 
5529b1aec48SLars Fredriksen     saved_tty_parameters = t;
5539b1aec48SLars Fredriksen     have_tty_parameters  = 1;
5549b1aec48SLars Fredriksen 
5559b1aec48SLars Fredriksen     t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
5569b1aec48SLars Fredriksen     t.c_oflag      = 0;
5579b1aec48SLars Fredriksen     t.c_lflag      = 0;
5589f65f104SPeter Wemm     t.c_cc[VERASE] =
5599f65f104SPeter Wemm     t.c_cc[VKILL]  = 0;
5609b1aec48SLars Fredriksen     t.c_cc[VMIN]   = 1;
5619b1aec48SLars Fredriksen     t.c_cc[VTIME]  = 0;
5629b1aec48SLars Fredriksen 
5639f65f104SPeter Wemm     if (set_term_param (&t) < 0)
56401c855caSPeter Wemm 	fatal(2, "Can't set terminal parameters: %m");
5659b1aec48SLars Fredriksen #endif
5669b1aec48SLars Fredriksen }
5679b1aec48SLars Fredriksen 
5689b1aec48SLars Fredriksen void break_sequence()
5699b1aec48SLars Fredriksen {
5709b1aec48SLars Fredriksen #ifdef TERMIOS
5719b1aec48SLars Fredriksen     tcsendbreak (0, 0);
5729b1aec48SLars Fredriksen #endif
5739b1aec48SLars Fredriksen }
5749b1aec48SLars Fredriksen 
5759b1aec48SLars Fredriksen void terminate(status)
5769b1aec48SLars Fredriksen int status;
5779b1aec48SLars Fredriksen {
5783d793cf1SPeter Wemm     echo_stderr(-1);
57901c855caSPeter Wemm     if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
5803d793cf1SPeter Wemm /*
5813d793cf1SPeter Wemm  * Allow the last of the report string to be gathered before we terminate.
5823d793cf1SPeter Wemm  */
5833d793cf1SPeter Wemm 	if (report_gathering) {
5843d793cf1SPeter Wemm 	    int c, rep_len;
5853d793cf1SPeter Wemm 
5863d793cf1SPeter Wemm 	    rep_len = strlen(report_buffer);
5873d793cf1SPeter Wemm 	    while (rep_len + 1 <= sizeof(report_buffer)) {
5883d793cf1SPeter Wemm 		alarm(1);
5893d793cf1SPeter Wemm 		c = get_char();
5903d793cf1SPeter Wemm 		alarm(0);
5913d793cf1SPeter Wemm 		if (c < 0 || iscntrl(c))
5923d793cf1SPeter Wemm 		    break;
5933d793cf1SPeter Wemm 		report_buffer[rep_len] = c;
5943d793cf1SPeter Wemm 		++rep_len;
5953d793cf1SPeter Wemm 	    }
5963d793cf1SPeter Wemm 	    report_buffer[rep_len] = 0;
5973d793cf1SPeter Wemm 	    fprintf (report_fp, "chat:  %s\n", report_buffer);
5983d793cf1SPeter Wemm 	}
5999f65f104SPeter Wemm 	if (verbose)
6009f65f104SPeter Wemm 	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
6019f65f104SPeter Wemm 	fclose (report_fp);
6029f65f104SPeter Wemm 	report_fp = (FILE *) NULL;
6039f65f104SPeter Wemm     }
6049f65f104SPeter Wemm 
6059f65f104SPeter Wemm #if defined(get_term_param)
60601c855caSPeter Wemm     if (have_tty_parameters) {
6079f65f104SPeter Wemm 	if (set_term_param (&saved_tty_parameters) < 0)
60801c855caSPeter Wemm 	    fatal(2, "Can't restore terminal parameters: %m");
6099f65f104SPeter Wemm     }
6109f65f104SPeter Wemm #endif
6119f65f104SPeter Wemm 
6129b1aec48SLars Fredriksen     exit(status);
6139b1aec48SLars Fredriksen }
6149b1aec48SLars Fredriksen 
6159b1aec48SLars Fredriksen /*
6169b1aec48SLars Fredriksen  *	'Clean up' this string.
6179b1aec48SLars Fredriksen  */
6189b1aec48SLars Fredriksen char *clean(s, sending)
6199b1aec48SLars Fredriksen register char *s;
62001c855caSPeter Wemm int sending;  /* set to 1 when sending (putting) this string. */
6219b1aec48SLars Fredriksen {
6229b1aec48SLars Fredriksen     char temp[STR_LEN], cur_chr;
62301c855caSPeter Wemm     register char *s1, *phchar;
6249b1aec48SLars Fredriksen     int add_return = sending;
6259b1aec48SLars Fredriksen #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
6269b1aec48SLars Fredriksen 
6279b1aec48SLars Fredriksen     s1 = temp;
6284e83a8feSKris Kennaway     /* Don't overflow buffer, leave room for chars we append later */
6294e83a8feSKris Kennaway     while (*s && s1 - temp < sizeof(temp) - 2 - add_return) {
6309b1aec48SLars Fredriksen 	cur_chr = *s++;
63101c855caSPeter Wemm 	if (cur_chr == '^') {
6329b1aec48SLars Fredriksen 	    cur_chr = *s++;
63301c855caSPeter Wemm 	    if (cur_chr == '\0') {
6349b1aec48SLars Fredriksen 		*s1++ = '^';
6359b1aec48SLars Fredriksen 		break;
6369b1aec48SLars Fredriksen 	    }
6379b1aec48SLars Fredriksen 	    cur_chr &= 0x1F;
63801c855caSPeter Wemm 	    if (cur_chr != 0) {
6399b1aec48SLars Fredriksen 		*s1++ = cur_chr;
6409f65f104SPeter Wemm 	    }
6419b1aec48SLars Fredriksen 	    continue;
6429b1aec48SLars Fredriksen 	}
6439b1aec48SLars Fredriksen 
64401c855caSPeter Wemm 	if (cur_chr != '\\') {
6459b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
6469b1aec48SLars Fredriksen 	    continue;
6479b1aec48SLars Fredriksen 	}
6489b1aec48SLars Fredriksen 
6499b1aec48SLars Fredriksen 	cur_chr = *s++;
65001c855caSPeter Wemm 	if (cur_chr == '\0') {
65101c855caSPeter Wemm 	    if (sending) {
6529b1aec48SLars Fredriksen 		*s1++ = '\\';
6539b1aec48SLars Fredriksen 		*s1++ = '\\';
6549b1aec48SLars Fredriksen 	    }
6559b1aec48SLars Fredriksen 	    break;
6569b1aec48SLars Fredriksen 	}
6579b1aec48SLars Fredriksen 
65801c855caSPeter Wemm 	switch (cur_chr) {
6599b1aec48SLars Fredriksen 	case 'b':
6609b1aec48SLars Fredriksen 	    *s1++ = '\b';
6619b1aec48SLars Fredriksen 	    break;
6629b1aec48SLars Fredriksen 
6639b1aec48SLars Fredriksen 	case 'c':
6649b1aec48SLars Fredriksen 	    if (sending && *s == '\0')
6659b1aec48SLars Fredriksen 		add_return = 0;
6669b1aec48SLars Fredriksen 	    else
6679b1aec48SLars Fredriksen 		*s1++ = cur_chr;
6689b1aec48SLars Fredriksen 	    break;
6699b1aec48SLars Fredriksen 
6709b1aec48SLars Fredriksen 	case '\\':
6719b1aec48SLars Fredriksen 	case 'K':
6729b1aec48SLars Fredriksen 	case 'p':
6739b1aec48SLars Fredriksen 	case 'd':
6749b1aec48SLars Fredriksen 	    if (sending)
6759b1aec48SLars Fredriksen 		*s1++ = '\\';
6769b1aec48SLars Fredriksen 
6779b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
6789b1aec48SLars Fredriksen 	    break;
6799b1aec48SLars Fredriksen 
68001c855caSPeter Wemm 	case 'T':
68101c855caSPeter Wemm 	    if (sending && phone_num) {
68201c855caSPeter Wemm 		for ( phchar = phone_num; *phchar != '\0'; phchar++)
68301c855caSPeter Wemm 		    *s1++ = *phchar;
68401c855caSPeter Wemm 	    }
68501c855caSPeter Wemm 	    else {
68601c855caSPeter Wemm 		*s1++ = '\\';
68701c855caSPeter Wemm 		*s1++ = 'T';
68801c855caSPeter Wemm 	    }
68901c855caSPeter Wemm 	    break;
69001c855caSPeter Wemm 
69101c855caSPeter Wemm 	case 'U':
69201c855caSPeter Wemm 	    if (sending && phone_num2) {
69301c855caSPeter Wemm 		for ( phchar = phone_num2; *phchar != '\0'; phchar++)
69401c855caSPeter Wemm 		    *s1++ = *phchar;
69501c855caSPeter Wemm 	    }
69601c855caSPeter Wemm 	    else {
69701c855caSPeter Wemm 		*s1++ = '\\';
69801c855caSPeter Wemm 		*s1++ = 'U';
69901c855caSPeter Wemm 	    }
70001c855caSPeter Wemm 	    break;
70101c855caSPeter Wemm 
7029b1aec48SLars Fredriksen 	case 'q':
7033d793cf1SPeter Wemm 	    quiet = 1;
7049b1aec48SLars Fredriksen 	    break;
7059b1aec48SLars Fredriksen 
7069b1aec48SLars Fredriksen 	case 'r':
7079b1aec48SLars Fredriksen 	    *s1++ = '\r';
7089b1aec48SLars Fredriksen 	    break;
7099b1aec48SLars Fredriksen 
7109b1aec48SLars Fredriksen 	case 'n':
7119b1aec48SLars Fredriksen 	    *s1++ = '\n';
7129b1aec48SLars Fredriksen 	    break;
7139b1aec48SLars Fredriksen 
7149b1aec48SLars Fredriksen 	case 's':
7159b1aec48SLars Fredriksen 	    *s1++ = ' ';
7169b1aec48SLars Fredriksen 	    break;
7179b1aec48SLars Fredriksen 
7189b1aec48SLars Fredriksen 	case 't':
7199b1aec48SLars Fredriksen 	    *s1++ = '\t';
7209b1aec48SLars Fredriksen 	    break;
7219b1aec48SLars Fredriksen 
7229b1aec48SLars Fredriksen 	case 'N':
72301c855caSPeter Wemm 	    if (sending) {
7249b1aec48SLars Fredriksen 		*s1++ = '\\';
7259b1aec48SLars Fredriksen 		*s1++ = '\0';
7269b1aec48SLars Fredriksen 	    }
7279b1aec48SLars Fredriksen 	    else
7289b1aec48SLars Fredriksen 		*s1++ = 'N';
7299b1aec48SLars Fredriksen 	    break;
7309b1aec48SLars Fredriksen 
7319b1aec48SLars Fredriksen 	default:
73201c855caSPeter Wemm 	    if (isoctal (cur_chr)) {
7339b1aec48SLars Fredriksen 		cur_chr &= 0x07;
73401c855caSPeter Wemm 		if (isoctal (*s)) {
7359b1aec48SLars Fredriksen 		    cur_chr <<= 3;
7369b1aec48SLars Fredriksen 		    cur_chr |= *s++ - '0';
73701c855caSPeter Wemm 		    if (isoctal (*s)) {
7389b1aec48SLars Fredriksen 			cur_chr <<= 3;
7399b1aec48SLars Fredriksen 			cur_chr |= *s++ - '0';
7409b1aec48SLars Fredriksen 		    }
7419b1aec48SLars Fredriksen 		}
7429b1aec48SLars Fredriksen 
74301c855caSPeter Wemm 		if (cur_chr != 0 || sending) {
7449b1aec48SLars Fredriksen 		    if (sending && (cur_chr == '\\' || cur_chr == 0))
7459b1aec48SLars Fredriksen 			*s1++ = '\\';
7469b1aec48SLars Fredriksen 		    *s1++ = cur_chr;
7479b1aec48SLars Fredriksen 		}
7489b1aec48SLars Fredriksen 		break;
7499b1aec48SLars Fredriksen 	    }
7509b1aec48SLars Fredriksen 
7519b1aec48SLars Fredriksen 	    if (sending)
7529b1aec48SLars Fredriksen 		*s1++ = '\\';
7539b1aec48SLars Fredriksen 	    *s1++ = cur_chr;
7549b1aec48SLars Fredriksen 	    break;
7559b1aec48SLars Fredriksen 	}
7569b1aec48SLars Fredriksen     }
7579b1aec48SLars Fredriksen 
7589b1aec48SLars Fredriksen     if (add_return)
7599b1aec48SLars Fredriksen 	*s1++ = '\r';
7609b1aec48SLars Fredriksen 
7619b1aec48SLars Fredriksen     *s1++ = '\0'; /* guarantee closure */
7629b1aec48SLars Fredriksen     *s1++ = '\0'; /* terminate the string */
7639b1aec48SLars Fredriksen     return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
7649b1aec48SLars Fredriksen }
7659b1aec48SLars Fredriksen 
7669b1aec48SLars Fredriksen /*
7673d793cf1SPeter Wemm  * A modified version of 'strtok'. This version skips \ sequences.
7683d793cf1SPeter Wemm  */
7693d793cf1SPeter Wemm 
7703d793cf1SPeter Wemm char *expect_strtok (s, term)
7713d793cf1SPeter Wemm      char *s, *term;
7723d793cf1SPeter Wemm {
7733d793cf1SPeter Wemm     static  char *str   = "";
7743d793cf1SPeter Wemm     int	    escape_flag = 0;
7753d793cf1SPeter Wemm     char   *result;
77601c855caSPeter Wemm 
7773d793cf1SPeter Wemm /*
7783d793cf1SPeter Wemm  * If a string was specified then do initial processing.
7793d793cf1SPeter Wemm  */
7803d793cf1SPeter Wemm     if (s)
7813d793cf1SPeter Wemm 	str = s;
78201c855caSPeter Wemm 
7833d793cf1SPeter Wemm /*
7843d793cf1SPeter Wemm  * If this is the escape flag then reset it and ignore the character.
7853d793cf1SPeter Wemm  */
7863d793cf1SPeter Wemm     if (*str)
7873d793cf1SPeter Wemm 	result = str;
7883d793cf1SPeter Wemm     else
7893d793cf1SPeter Wemm 	result = (char *) 0;
7903d793cf1SPeter Wemm 
79101c855caSPeter Wemm     while (*str) {
79201c855caSPeter Wemm 	if (escape_flag) {
7933d793cf1SPeter Wemm 	    escape_flag = 0;
7943d793cf1SPeter Wemm 	    ++str;
7953d793cf1SPeter Wemm 	    continue;
7963d793cf1SPeter Wemm 	}
7973d793cf1SPeter Wemm 
79801c855caSPeter Wemm 	if (*str == '\\') {
7993d793cf1SPeter Wemm 	    ++str;
8003d793cf1SPeter Wemm 	    escape_flag = 1;
8013d793cf1SPeter Wemm 	    continue;
8023d793cf1SPeter Wemm 	}
80301c855caSPeter Wemm 
8043d793cf1SPeter Wemm /*
8053d793cf1SPeter Wemm  * If this is not in the termination string, continue.
8063d793cf1SPeter Wemm  */
80701c855caSPeter Wemm 	if (strchr (term, *str) == (char *) 0) {
8083d793cf1SPeter Wemm 	    ++str;
8093d793cf1SPeter Wemm 	    continue;
8103d793cf1SPeter Wemm 	}
81101c855caSPeter Wemm 
8123d793cf1SPeter Wemm /*
8133d793cf1SPeter Wemm  * This is the terminator. Mark the end of the string and stop.
8143d793cf1SPeter Wemm  */
8153d793cf1SPeter Wemm 	*str++ = '\0';
8163d793cf1SPeter Wemm 	break;
8173d793cf1SPeter Wemm     }
8183d793cf1SPeter Wemm     return (result);
8193d793cf1SPeter Wemm }
8203d793cf1SPeter Wemm 
8213d793cf1SPeter Wemm /*
8229b1aec48SLars Fredriksen  * Process the expect string
8239b1aec48SLars Fredriksen  */
8243d793cf1SPeter Wemm 
8259b1aec48SLars Fredriksen void chat_expect (s)
8263d793cf1SPeter Wemm char *s;
8279b1aec48SLars Fredriksen {
8283d793cf1SPeter Wemm     char *expect;
8293d793cf1SPeter Wemm     char *reply;
8303d793cf1SPeter Wemm 
83101c855caSPeter Wemm     if (strcmp(s, "HANGUP") == 0) {
8323d793cf1SPeter Wemm 	++hup_next;
8333d793cf1SPeter Wemm         return;
8343d793cf1SPeter Wemm     }
8353d793cf1SPeter Wemm 
83601c855caSPeter Wemm     if (strcmp(s, "ABORT") == 0) {
8379b1aec48SLars Fredriksen 	++abort_next;
8389b1aec48SLars Fredriksen 	return;
8399b1aec48SLars Fredriksen     }
8409b1aec48SLars Fredriksen 
84101c855caSPeter Wemm     if (strcmp(s, "CLR_ABORT") == 0) {
8423d793cf1SPeter Wemm 	++clear_abort_next;
8433d793cf1SPeter Wemm 	return;
8443d793cf1SPeter Wemm     }
8453d793cf1SPeter Wemm 
84601c855caSPeter Wemm     if (strcmp(s, "REPORT") == 0) {
8479f65f104SPeter Wemm 	++report_next;
8489f65f104SPeter Wemm 	return;
8499f65f104SPeter Wemm     }
8509f65f104SPeter Wemm 
85101c855caSPeter Wemm     if (strcmp(s, "CLR_REPORT") == 0) {
8523d793cf1SPeter Wemm 	++clear_report_next;
8533d793cf1SPeter Wemm 	return;
8543d793cf1SPeter Wemm     }
8553d793cf1SPeter Wemm 
85601c855caSPeter Wemm     if (strcmp(s, "TIMEOUT") == 0) {
8579b1aec48SLars Fredriksen 	++timeout_next;
8589b1aec48SLars Fredriksen 	return;
8599b1aec48SLars Fredriksen     }
8609b1aec48SLars Fredriksen 
86101c855caSPeter Wemm     if (strcmp(s, "ECHO") == 0) {
8623d793cf1SPeter Wemm 	++echo_next;
8633d793cf1SPeter Wemm 	return;
8643d793cf1SPeter Wemm     }
86501c855caSPeter Wemm 
86601c855caSPeter Wemm     if (strcmp(s, "SAY") == 0) {
8673d793cf1SPeter Wemm 	++say_next;
8683d793cf1SPeter Wemm 	return;
8693d793cf1SPeter Wemm     }
87001c855caSPeter Wemm 
8713d793cf1SPeter Wemm /*
8723d793cf1SPeter Wemm  * Fetch the expect and reply string.
8733d793cf1SPeter Wemm  */
87401c855caSPeter Wemm     for (;;) {
8753d793cf1SPeter Wemm 	expect = expect_strtok (s, "-");
8763d793cf1SPeter Wemm 	s      = (char *) 0;
8779b1aec48SLars Fredriksen 
8783d793cf1SPeter Wemm 	if (expect == (char *) 0)
8793d793cf1SPeter Wemm 	    return;
8803d793cf1SPeter Wemm 
8813d793cf1SPeter Wemm 	reply = expect_strtok (s, "-");
88201c855caSPeter Wemm 
8833d793cf1SPeter Wemm /*
8843d793cf1SPeter Wemm  * Handle the expect string. If successful then exit.
8853d793cf1SPeter Wemm  */
8863d793cf1SPeter Wemm 	if (get_string (expect))
8873d793cf1SPeter Wemm 	    return;
88801c855caSPeter Wemm 
8893d793cf1SPeter Wemm /*
8903d793cf1SPeter Wemm  * If there is a sub-reply string then send it. Otherwise any condition
8913d793cf1SPeter Wemm  * is terminal.
8923d793cf1SPeter Wemm  */
8933d793cf1SPeter Wemm 	if (reply == (char *) 0 || exit_code != 3)
8949b1aec48SLars Fredriksen 	    break;
8959b1aec48SLars Fredriksen 
8963d793cf1SPeter Wemm 	chat_send (reply);
8979f65f104SPeter Wemm     }
89801c855caSPeter Wemm 
8993d793cf1SPeter Wemm /*
9003d793cf1SPeter Wemm  * The expectation did not occur. This is terminal.
9013d793cf1SPeter Wemm  */
9029b1aec48SLars Fredriksen     if (fail_reason)
90301c855caSPeter Wemm 	logf("Failed (%s)", fail_reason);
9049b1aec48SLars Fredriksen     else
90501c855caSPeter Wemm 	logf("Failed");
9069f65f104SPeter Wemm     terminate(exit_code);
9079f65f104SPeter Wemm }
9083d793cf1SPeter Wemm 
9093d793cf1SPeter Wemm /*
9103d793cf1SPeter Wemm  * Translate the input character to the appropriate string for printing
9113d793cf1SPeter Wemm  * the data.
9123d793cf1SPeter Wemm  */
9139b1aec48SLars Fredriksen 
9149b1aec48SLars Fredriksen char *character(c)
9159f65f104SPeter Wemm int c;
9169b1aec48SLars Fredriksen {
9179b1aec48SLars Fredriksen     static char string[10];
9189b1aec48SLars Fredriksen     char *meta;
9199b1aec48SLars Fredriksen 
9209b1aec48SLars Fredriksen     meta = (c & 0x80) ? "M-" : "";
9219b1aec48SLars Fredriksen     c &= 0x7F;
9229b1aec48SLars Fredriksen 
9239b1aec48SLars Fredriksen     if (c < 32)
9249b1aec48SLars Fredriksen 	sprintf(string, "%s^%c", meta, (int)c + '@');
92501c855caSPeter Wemm     else if (c == 127)
9269b1aec48SLars Fredriksen 	sprintf(string, "%s^?", meta);
9279b1aec48SLars Fredriksen     else
9289b1aec48SLars Fredriksen 	sprintf(string, "%s%c", meta, c);
9299b1aec48SLars Fredriksen 
9309b1aec48SLars Fredriksen     return (string);
9319b1aec48SLars Fredriksen }
9329b1aec48SLars Fredriksen 
9339b1aec48SLars Fredriksen /*
9349b1aec48SLars Fredriksen  *  process the reply string
9359b1aec48SLars Fredriksen  */
9369b1aec48SLars Fredriksen void chat_send (s)
9379b1aec48SLars Fredriksen register char *s;
9389b1aec48SLars Fredriksen {
93901c855caSPeter Wemm     if (say_next) {
9403d793cf1SPeter Wemm 	say_next = 0;
9413d793cf1SPeter Wemm 	s = clean(s,0);
942e1b4d8d0SSheldon Hearn 	write(STDERR_FILENO, s, strlen(s));
9433d793cf1SPeter Wemm         free(s);
9443d793cf1SPeter Wemm 	return;
9453d793cf1SPeter Wemm     }
94601c855caSPeter Wemm 
94701c855caSPeter Wemm     if (hup_next) {
9483d793cf1SPeter Wemm         hup_next = 0;
9493d793cf1SPeter Wemm 	if (strcmp(s, "OFF") == 0)
9503d793cf1SPeter Wemm            signal(SIGHUP, SIG_IGN);
9513d793cf1SPeter Wemm         else
9523d793cf1SPeter Wemm            signal(SIGHUP, sighup);
9533d793cf1SPeter Wemm         return;
9543d793cf1SPeter Wemm     }
95501c855caSPeter Wemm 
95601c855caSPeter Wemm     if (echo_next) {
9573d793cf1SPeter Wemm 	echo_next = 0;
9583d793cf1SPeter Wemm 	echo = (strcmp(s, "ON") == 0);
9593d793cf1SPeter Wemm 	return;
9603d793cf1SPeter Wemm     }
96101c855caSPeter Wemm 
96201c855caSPeter Wemm     if (abort_next) {
9639b1aec48SLars Fredriksen 	char *s1;
9649b1aec48SLars Fredriksen 
9659b1aec48SLars Fredriksen 	abort_next = 0;
9669b1aec48SLars Fredriksen 
9679b1aec48SLars Fredriksen 	if (n_aborts >= MAX_ABORTS)
96801c855caSPeter Wemm 	    fatal(2, "Too many ABORT strings");
9699b1aec48SLars Fredriksen 
9709b1aec48SLars Fredriksen 	s1 = clean(s, 0);
9719b1aec48SLars Fredriksen 
9729f65f104SPeter Wemm 	if (strlen(s1) > strlen(s)
9739f65f104SPeter Wemm 	    || strlen(s1) + 1 > sizeof(fail_buffer))
97401c855caSPeter Wemm 	    fatal(1, "Illegal or too-long ABORT string ('%v')", s);
9759b1aec48SLars Fredriksen 
9769b1aec48SLars Fredriksen 	abort_string[n_aborts++] = s1;
9779b1aec48SLars Fredriksen 
9789b1aec48SLars Fredriksen 	if (verbose)
97901c855caSPeter Wemm 	    logf("abort on (%v)", s);
9809f65f104SPeter Wemm 	return;
9819b1aec48SLars Fredriksen     }
9829f65f104SPeter Wemm 
98301c855caSPeter Wemm     if (clear_abort_next) {
9843d793cf1SPeter Wemm 	char *s1;
9853d793cf1SPeter Wemm 	int   i;
9863d793cf1SPeter Wemm         int   old_max;
9873d793cf1SPeter Wemm 	int   pack = 0;
9883d793cf1SPeter Wemm 
9893d793cf1SPeter Wemm 	clear_abort_next = 0;
9903d793cf1SPeter Wemm 
9913d793cf1SPeter Wemm 	s1 = clean(s, 0);
9923d793cf1SPeter Wemm 
9933d793cf1SPeter Wemm 	if (strlen(s1) > strlen(s)
9943d793cf1SPeter Wemm 	    || strlen(s1) + 1 > sizeof(fail_buffer))
99501c855caSPeter Wemm 	    fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
9963d793cf1SPeter Wemm 
9973d793cf1SPeter Wemm         old_max = n_aborts;
99801c855caSPeter Wemm 	for (i=0; i < n_aborts; i++) {
99901c855caSPeter Wemm 	    if ( strcmp(s1,abort_string[i]) == 0 ) {
10003d793cf1SPeter Wemm 		free(abort_string[i]);
10013d793cf1SPeter Wemm 		abort_string[i] = NULL;
10023d793cf1SPeter Wemm 		pack++;
10033d793cf1SPeter Wemm 		n_aborts--;
10043d793cf1SPeter Wemm 		if (verbose)
100501c855caSPeter Wemm 		    logf("clear abort on (%v)", s);
10063d793cf1SPeter Wemm 	    }
10073d793cf1SPeter Wemm 	}
10083d793cf1SPeter Wemm         free(s1);
100901c855caSPeter Wemm 	if (pack)
101001c855caSPeter Wemm 	    pack_array(abort_string,old_max);
10113d793cf1SPeter Wemm 	return;
10123d793cf1SPeter Wemm     }
10133d793cf1SPeter Wemm 
101401c855caSPeter Wemm     if (report_next) {
10159f65f104SPeter Wemm 	char *s1;
10169f65f104SPeter Wemm 
10179f65f104SPeter Wemm 	report_next = 0;
10189f65f104SPeter Wemm 	if (n_reports >= MAX_REPORTS)
101901c855caSPeter Wemm 	    fatal(2, "Too many REPORT strings");
10209f65f104SPeter Wemm 
10219f65f104SPeter Wemm 	s1 = clean(s, 0);
10229f65f104SPeter Wemm 
10239f65f104SPeter Wemm 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
102401c855caSPeter Wemm 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
10259f65f104SPeter Wemm 
10269f65f104SPeter Wemm 	report_string[n_reports++] = s1;
10279f65f104SPeter Wemm 
10289f65f104SPeter Wemm 	if (verbose)
102901c855caSPeter Wemm 	    logf("report (%v)", s);
10309f65f104SPeter Wemm 	return;
10319f65f104SPeter Wemm     }
10329f65f104SPeter Wemm 
103301c855caSPeter Wemm     if (clear_report_next) {
10343d793cf1SPeter Wemm 	char *s1;
10353d793cf1SPeter Wemm 	int   i;
10363d793cf1SPeter Wemm 	int   old_max;
10373d793cf1SPeter Wemm 	int   pack = 0;
10383d793cf1SPeter Wemm 
10393d793cf1SPeter Wemm 	clear_report_next = 0;
10403d793cf1SPeter Wemm 
10413d793cf1SPeter Wemm 	s1 = clean(s, 0);
10423d793cf1SPeter Wemm 
10433d793cf1SPeter Wemm 	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
104401c855caSPeter Wemm 	    fatal(1, "Illegal or too-long REPORT string ('%v')", s);
10453d793cf1SPeter Wemm 
10463d793cf1SPeter Wemm 	old_max = n_reports;
104701c855caSPeter Wemm 	for (i=0; i < n_reports; i++) {
104801c855caSPeter Wemm 	    if ( strcmp(s1,report_string[i]) == 0 ) {
10493d793cf1SPeter Wemm 		free(report_string[i]);
10503d793cf1SPeter Wemm 		report_string[i] = NULL;
10513d793cf1SPeter Wemm 		pack++;
10523d793cf1SPeter Wemm 		n_reports--;
10533d793cf1SPeter Wemm 		if (verbose)
105401c855caSPeter Wemm 		    logf("clear report (%v)", s);
10553d793cf1SPeter Wemm 	    }
10563d793cf1SPeter Wemm 	}
10573d793cf1SPeter Wemm         free(s1);
105801c855caSPeter Wemm         if (pack)
105901c855caSPeter Wemm 	    pack_array(report_string,old_max);
10603d793cf1SPeter Wemm 
10613d793cf1SPeter Wemm 	return;
10623d793cf1SPeter Wemm     }
10633d793cf1SPeter Wemm 
106401c855caSPeter Wemm     if (timeout_next) {
10659b1aec48SLars Fredriksen 	timeout_next = 0;
10669b1aec48SLars Fredriksen 	timeout = atoi(s);
10679b1aec48SLars Fredriksen 
10689b1aec48SLars Fredriksen 	if (timeout <= 0)
10699b1aec48SLars Fredriksen 	    timeout = DEFAULT_CHAT_TIMEOUT;
10709b1aec48SLars Fredriksen 
10719b1aec48SLars Fredriksen 	if (verbose)
107201c855caSPeter Wemm 	    logf("timeout set to %d seconds", timeout);
107301c855caSPeter Wemm 
10749f65f104SPeter Wemm 	return;
10759f65f104SPeter Wemm     }
10769f65f104SPeter Wemm 
10779f65f104SPeter Wemm     if (strcmp(s, "EOT") == 0)
10789f65f104SPeter Wemm 	s = "^D\\c";
107901c855caSPeter Wemm     else if (strcmp(s, "BREAK") == 0)
10809b1aec48SLars Fredriksen 	s = "\\K\\c";
10819f65f104SPeter Wemm 
10829b1aec48SLars Fredriksen     if (!put_string(s))
108301c855caSPeter Wemm 	fatal(1, "Failed");
10849b1aec48SLars Fredriksen }
10859b1aec48SLars Fredriksen 
10869b1aec48SLars Fredriksen int get_char()
10879b1aec48SLars Fredriksen {
10889b1aec48SLars Fredriksen     int status;
10899b1aec48SLars Fredriksen     char c;
10909b1aec48SLars Fredriksen 
1091e1b4d8d0SSheldon Hearn     status = read(STDIN_FILENO, &c, 1);
10929b1aec48SLars Fredriksen 
109301c855caSPeter Wemm     switch (status) {
10949b1aec48SLars Fredriksen     case 1:
10959b1aec48SLars Fredriksen 	return ((int)c & 0x7F);
10969b1aec48SLars Fredriksen 
10979b1aec48SLars Fredriksen     default:
109801c855caSPeter Wemm 	logf("warning: read() on stdin returned %d", status);
10999b1aec48SLars Fredriksen 
11009b1aec48SLars Fredriksen     case -1:
11019b1aec48SLars Fredriksen 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
110201c855caSPeter Wemm 	    fatal(2, "Can't get file mode flags on stdin: %m");
110301c855caSPeter Wemm 
11049f65f104SPeter Wemm 	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
110501c855caSPeter Wemm 	    fatal(2, "Can't set file mode flags on stdin: %m");
11069b1aec48SLars Fredriksen 
11079b1aec48SLars Fredriksen 	return (-1);
11089b1aec48SLars Fredriksen     }
11099b1aec48SLars Fredriksen }
11109b1aec48SLars Fredriksen 
11119b1aec48SLars Fredriksen int put_char(c)
11129f65f104SPeter Wemm int c;
11139b1aec48SLars Fredriksen {
11149b1aec48SLars Fredriksen     int status;
11159f65f104SPeter Wemm     char ch = c;
11169b1aec48SLars Fredriksen 
11179f65f104SPeter Wemm     usleep(10000);		/* inter-character typing delay (?) */
11189b1aec48SLars Fredriksen 
1119e1b4d8d0SSheldon Hearn     status = write(STDOUT_FILENO, &ch, 1);
11209b1aec48SLars Fredriksen 
112101c855caSPeter Wemm     switch (status) {
11229b1aec48SLars Fredriksen     case 1:
11239b1aec48SLars Fredriksen 	return (0);
11249b1aec48SLars Fredriksen 
11259b1aec48SLars Fredriksen     default:
112601c855caSPeter Wemm 	logf("warning: write() on stdout returned %d", status);
11279b1aec48SLars Fredriksen 
11289b1aec48SLars Fredriksen     case -1:
11299b1aec48SLars Fredriksen 	if ((status = fcntl(0, F_GETFL, 0)) == -1)
113001c855caSPeter Wemm 	    fatal(2, "Can't get file mode flags on stdin, %m");
113101c855caSPeter Wemm 
11329f65f104SPeter Wemm 	if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
113301c855caSPeter Wemm 	    fatal(2, "Can't set file mode flags on stdin: %m");
11349b1aec48SLars Fredriksen 
11359b1aec48SLars Fredriksen 	return (-1);
11369b1aec48SLars Fredriksen     }
11379b1aec48SLars Fredriksen }
11389b1aec48SLars Fredriksen 
11399b1aec48SLars Fredriksen int write_char (c)
11409b1aec48SLars Fredriksen int c;
11419b1aec48SLars Fredriksen {
114201c855caSPeter Wemm     if (alarmed || put_char(c) < 0) {
11439f65f104SPeter Wemm 	alarm(0);
11449f65f104SPeter Wemm 	alarmed = 0;
11459b1aec48SLars Fredriksen 
114601c855caSPeter Wemm 	if (verbose) {
11479b1aec48SLars Fredriksen 	    if (errno == EINTR || errno == EWOULDBLOCK)
114801c855caSPeter Wemm 		logf(" -- write timed out");
11499b1aec48SLars Fredriksen 	    else
115001c855caSPeter Wemm 		logf(" -- write failed: %m");
11519f65f104SPeter Wemm 	}
11529b1aec48SLars Fredriksen 	return (0);
11539b1aec48SLars Fredriksen     }
11549b1aec48SLars Fredriksen     return (1);
11559b1aec48SLars Fredriksen }
11569b1aec48SLars Fredriksen 
11579b1aec48SLars Fredriksen int put_string (s)
11589b1aec48SLars Fredriksen register char *s;
11599b1aec48SLars Fredriksen {
11603d793cf1SPeter Wemm     quiet = 0;
11619b1aec48SLars Fredriksen     s = clean(s, 1);
11629b1aec48SLars Fredriksen 
116301c855caSPeter Wemm     if (verbose) {
11649b1aec48SLars Fredriksen 	if (quiet)
116501c855caSPeter Wemm 	    logf("send (??????)");
11669b1aec48SLars Fredriksen 	else
116701c855caSPeter Wemm 	    logf("send (%v)", s);
11689b1aec48SLars Fredriksen     }
11699b1aec48SLars Fredriksen 
11709b1aec48SLars Fredriksen     alarm(timeout); alarmed = 0;
11719b1aec48SLars Fredriksen 
117201c855caSPeter Wemm     while (*s) {
11739b1aec48SLars Fredriksen 	register char c = *s++;
11749b1aec48SLars Fredriksen 
117501c855caSPeter Wemm 	if (c != '\\') {
11769b1aec48SLars Fredriksen 	    if (!write_char (c))
11779b1aec48SLars Fredriksen 		return 0;
11789b1aec48SLars Fredriksen 	    continue;
11799b1aec48SLars Fredriksen 	}
11809b1aec48SLars Fredriksen 
11819b1aec48SLars Fredriksen 	c = *s++;
118201c855caSPeter Wemm 	switch (c) {
11839b1aec48SLars Fredriksen 	case 'd':
11849b1aec48SLars Fredriksen 	    sleep(1);
11859b1aec48SLars Fredriksen 	    break;
11869b1aec48SLars Fredriksen 
11879b1aec48SLars Fredriksen 	case 'K':
11889b1aec48SLars Fredriksen 	    break_sequence();
11899b1aec48SLars Fredriksen 	    break;
11909b1aec48SLars Fredriksen 
11919b1aec48SLars Fredriksen 	case 'p':
11929f65f104SPeter Wemm 	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
11939b1aec48SLars Fredriksen 	    break;
11949b1aec48SLars Fredriksen 
11959b1aec48SLars Fredriksen 	default:
11969b1aec48SLars Fredriksen 	    if (!write_char (c))
11979b1aec48SLars Fredriksen 		return 0;
11989b1aec48SLars Fredriksen 	    break;
11999b1aec48SLars Fredriksen 	}
12009b1aec48SLars Fredriksen     }
12019b1aec48SLars Fredriksen 
12029b1aec48SLars Fredriksen     alarm(0);
12039b1aec48SLars Fredriksen     alarmed = 0;
12049b1aec48SLars Fredriksen     return (1);
12059b1aec48SLars Fredriksen }
12069b1aec48SLars Fredriksen 
12079b1aec48SLars Fredriksen /*
12083d793cf1SPeter Wemm  *	Echo a character to stderr.
12093d793cf1SPeter Wemm  *	When called with -1, a '\n' character is generated when
12103d793cf1SPeter Wemm  *	the cursor is not at the beginning of a line.
12113d793cf1SPeter Wemm  */
12123d793cf1SPeter Wemm void echo_stderr(n)
12133d793cf1SPeter Wemm int n;
12143d793cf1SPeter Wemm {
12153d793cf1SPeter Wemm     static int need_lf;
12163d793cf1SPeter Wemm     char *s;
12173d793cf1SPeter Wemm 
121801c855caSPeter Wemm     switch (n) {
12193d793cf1SPeter Wemm     case '\r':		/* ignore '\r' */
12203d793cf1SPeter Wemm 	break;
12213d793cf1SPeter Wemm     case -1:
12223d793cf1SPeter Wemm 	if (need_lf == 0)
12233d793cf1SPeter Wemm 	    break;
122493b0017fSPhilippe Charnier 	/* FALLTHROUGH */
12253d793cf1SPeter Wemm     case '\n':
1226e1b4d8d0SSheldon Hearn 	write(STDERR_FILENO, "\n", 1);
12273d793cf1SPeter Wemm 	need_lf = 0;
12283d793cf1SPeter Wemm 	break;
12293d793cf1SPeter Wemm     default:
12303d793cf1SPeter Wemm 	s = character(n);
1231e1b4d8d0SSheldon Hearn 	write(STDERR_FILENO, s, strlen(s));
12323d793cf1SPeter Wemm 	need_lf = 1;
12333d793cf1SPeter Wemm 	break;
12343d793cf1SPeter Wemm     }
12353d793cf1SPeter Wemm }
12363d793cf1SPeter Wemm 
12373d793cf1SPeter Wemm /*
12389b1aec48SLars Fredriksen  *	'Wait for' this string to appear on this file descriptor.
12399b1aec48SLars Fredriksen  */
12409b1aec48SLars Fredriksen int get_string(string)
12419b1aec48SLars Fredriksen register char *string;
12429b1aec48SLars Fredriksen {
12439b1aec48SLars Fredriksen     char temp[STR_LEN];
12449b1aec48SLars Fredriksen     int c, printed = 0, len, minlen;
12459b1aec48SLars Fredriksen     register char *s = temp, *end = s + STR_LEN;
124601c855caSPeter Wemm     char *logged = temp;
12479b1aec48SLars Fredriksen 
12489b1aec48SLars Fredriksen     fail_reason = (char *)0;
12494e83a8feSKris Kennaway 
12504e83a8feSKris Kennaway     if (strlen(string) > STR_LEN) {
12514e83a8feSKris Kennaway 	logf("expect string is too long");
12524e83a8feSKris Kennaway 	exit_code = 1;
12534e83a8feSKris Kennaway 	return 0;
12544e83a8feSKris Kennaway     }
12554e83a8feSKris Kennaway 
12569b1aec48SLars Fredriksen     string = clean(string, 0);
12579b1aec48SLars Fredriksen     len = strlen(string);
12589b1aec48SLars Fredriksen     minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
12599b1aec48SLars Fredriksen 
12609b1aec48SLars Fredriksen     if (verbose)
126101c855caSPeter Wemm 	logf("expect (%v)", string);
12629b1aec48SLars Fredriksen 
126301c855caSPeter Wemm     if (len == 0) {
12649b1aec48SLars Fredriksen 	if (verbose)
126501c855caSPeter Wemm 	    logf("got it");
12669b1aec48SLars Fredriksen 	return (1);
12679b1aec48SLars Fredriksen     }
12689b1aec48SLars Fredriksen 
12699f65f104SPeter Wemm     alarm(timeout);
12709f65f104SPeter Wemm     alarmed = 0;
12719b1aec48SLars Fredriksen 
127201c855caSPeter Wemm     while ( ! alarmed && (c = get_char()) >= 0) {
12739f65f104SPeter Wemm 	int n, abort_len, report_len;
12749b1aec48SLars Fredriksen 
12753d793cf1SPeter Wemm 	if (echo)
12763d793cf1SPeter Wemm 	    echo_stderr(c);
127701c855caSPeter Wemm 	if (verbose && c == '\n') {
127801c855caSPeter Wemm 	    if (s == logged)
127901c855caSPeter Wemm 		logf("");	/* blank line */
12809b1aec48SLars Fredriksen 	    else
128101c855caSPeter Wemm 		logf("%0.*v", s - logged, logged);
128201c855caSPeter Wemm 	    logged = s + 1;
12833d793cf1SPeter Wemm 	}
12843d793cf1SPeter Wemm 
12859b1aec48SLars Fredriksen 	*s++ = c;
12869b1aec48SLars Fredriksen 
128701c855caSPeter Wemm 	if (verbose && s >= logged + 80) {
128801c855caSPeter Wemm 	    logf("%0.*v", s - logged, logged);
128901c855caSPeter Wemm 	    logged = s;
129001c855caSPeter Wemm 	}
129101c855caSPeter Wemm 
129201c855caSPeter Wemm 	if (Verbose) {
129301c855caSPeter Wemm 	   if (c == '\n')
129401c855caSPeter Wemm 	       fputc( '\n', stderr );
129501c855caSPeter Wemm 	   else if (c != '\r')
129601c855caSPeter Wemm 	       fprintf( stderr, "%s", character(c) );
129701c855caSPeter Wemm 	}
129801c855caSPeter Wemm 
129901c855caSPeter Wemm 	if (!report_gathering) {
130001c855caSPeter Wemm 	    for (n = 0; n < n_reports; ++n) {
13019f65f104SPeter Wemm 		if ((report_string[n] != (char*) NULL) &&
13029f65f104SPeter Wemm 		    s - temp >= (report_len = strlen(report_string[n])) &&
130301c855caSPeter Wemm 		    strncmp(s - report_len, report_string[n], report_len) == 0) {
13049f65f104SPeter Wemm 		    time_t time_now   = time ((time_t*) NULL);
13059f65f104SPeter Wemm 		    struct tm* tm_now = localtime (&time_now);
13069f65f104SPeter Wemm 
13079f65f104SPeter Wemm 		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
13089f65f104SPeter Wemm 		    strcat (report_buffer, report_string[n]);
13099f65f104SPeter Wemm 
13109f65f104SPeter Wemm 		    report_string[n] = (char *) NULL;
13119f65f104SPeter Wemm 		    report_gathering = 1;
13129f65f104SPeter Wemm 		    break;
13139f65f104SPeter Wemm 		}
13149f65f104SPeter Wemm 	    }
13159f65f104SPeter Wemm 	}
131601c855caSPeter Wemm 	else {
131701c855caSPeter Wemm 	    if (!iscntrl (c)) {
13189f65f104SPeter Wemm 		int rep_len = strlen (report_buffer);
13199f65f104SPeter Wemm 		report_buffer[rep_len]     = c;
13209f65f104SPeter Wemm 		report_buffer[rep_len + 1] = '\0';
13219f65f104SPeter Wemm 	    }
132201c855caSPeter Wemm 	    else {
13239f65f104SPeter Wemm 		report_gathering = 0;
13249f65f104SPeter Wemm 		fprintf (report_fp, "chat:  %s\n", report_buffer);
13259f65f104SPeter Wemm 	    }
13269f65f104SPeter Wemm 	}
13279b1aec48SLars Fredriksen 
13283d793cf1SPeter Wemm 	if (s - temp >= len &&
13293d793cf1SPeter Wemm 	    c == string[len - 1] &&
133001c855caSPeter Wemm 	    strncmp(s - len, string, len) == 0) {
133101c855caSPeter Wemm 	    if (verbose) {
133201c855caSPeter Wemm 		if (s > logged)
133301c855caSPeter Wemm 		    logf("%0.*v", s - logged, logged);
13343d793cf1SPeter Wemm 		logf(" -- got it\n");
13353d793cf1SPeter Wemm 	    }
13363d793cf1SPeter Wemm 
13373d793cf1SPeter Wemm 	    alarm(0);
13383d793cf1SPeter Wemm 	    alarmed = 0;
13393d793cf1SPeter Wemm 	    return (1);
13403d793cf1SPeter Wemm 	}
13413d793cf1SPeter Wemm 
134201c855caSPeter Wemm 	for (n = 0; n < n_aborts; ++n) {
13433d793cf1SPeter Wemm 	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
134401c855caSPeter Wemm 		strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
134501c855caSPeter Wemm 		if (verbose) {
134601c855caSPeter Wemm 		    if (s > logged)
134701c855caSPeter Wemm 			logf("%0.*v", s - logged, logged);
134801c855caSPeter Wemm 		    logf(" -- failed");
13493d793cf1SPeter Wemm 		}
13503d793cf1SPeter Wemm 
13513d793cf1SPeter Wemm 		alarm(0);
13523d793cf1SPeter Wemm 		alarmed = 0;
13533d793cf1SPeter Wemm 		exit_code = n + 4;
13543d793cf1SPeter Wemm 		strcpy(fail_reason = fail_buffer, abort_string[n]);
13553d793cf1SPeter Wemm 		return (0);
13563d793cf1SPeter Wemm 	    }
13573d793cf1SPeter Wemm 	}
13583d793cf1SPeter Wemm 
135901c855caSPeter Wemm 	if (s >= end) {
136001c855caSPeter Wemm 	    if (logged < s - minlen) {
136101c855caSPeter Wemm 		logf("%0.*v", s - logged, logged);
136201c855caSPeter Wemm 		logged = s;
136301c855caSPeter Wemm 	    }
136401c855caSPeter Wemm 	    s -= minlen;
136501c855caSPeter Wemm 	    memmove(temp, s, minlen);
136601c855caSPeter Wemm 	    logged = temp + (logged - s);
13679b1aec48SLars Fredriksen 	    s = temp + minlen;
13689b1aec48SLars Fredriksen 	}
13699b1aec48SLars Fredriksen 
13709b1aec48SLars Fredriksen 	if (alarmed && verbose)
137101c855caSPeter Wemm 	    logf("warning: alarm synchronization problem");
13729f65f104SPeter Wemm     }
13739b1aec48SLars Fredriksen 
13749b1aec48SLars Fredriksen     alarm(0);
13759b1aec48SLars Fredriksen 
137601c855caSPeter Wemm     if (verbose && printed) {
13779b1aec48SLars Fredriksen 	if (alarmed)
137801c855caSPeter Wemm 	    logf(" -- read timed out");
13799b1aec48SLars Fredriksen 	else
138001c855caSPeter Wemm 	    logf(" -- read failed: %m");
13819b1aec48SLars Fredriksen     }
13829b1aec48SLars Fredriksen 
13839f65f104SPeter Wemm     exit_code = 3;
13849b1aec48SLars Fredriksen     alarmed   = 0;
13859b1aec48SLars Fredriksen     return (0);
13869b1aec48SLars Fredriksen }
13879b1aec48SLars Fredriksen 
13887288c503SPeter Wemm /*
13897288c503SPeter Wemm  * Gross kludge to handle Solaris versions >= 2.6 having usleep.
13907288c503SPeter Wemm  */
13917288c503SPeter Wemm #ifdef SOL2
13927288c503SPeter Wemm #include <sys/param.h>
13937288c503SPeter Wemm #if MAXUID > 65536		/* then this is Solaris 2.6 or later */
13947288c503SPeter Wemm #undef NO_USLEEP
13957288c503SPeter Wemm #endif
13967288c503SPeter Wemm #endif /* SOL2 */
13977288c503SPeter Wemm 
13989f65f104SPeter Wemm #ifdef NO_USLEEP
13999b1aec48SLars Fredriksen #include <sys/types.h>
14009b1aec48SLars Fredriksen #include <sys/time.h>
14019b1aec48SLars Fredriksen 
14029b1aec48SLars Fredriksen /*
14039b1aec48SLars Fredriksen   usleep -- support routine for 4.2BSD system call emulations
14049b1aec48SLars Fredriksen   last edit:  29-Oct-1984     D A Gwyn
14059b1aec48SLars Fredriksen   */
14069b1aec48SLars Fredriksen 
14079b1aec48SLars Fredriksen extern int	  select();
14089b1aec48SLars Fredriksen 
14099b1aec48SLars Fredriksen int
14109b1aec48SLars Fredriksen usleep( usec )				  /* returns 0 if ok, else -1 */
14119b1aec48SLars Fredriksen     long		usec;		/* delay in microseconds */
14129b1aec48SLars Fredriksen {
141301c855caSPeter Wemm     static struct {		/* `timeval' */
14149b1aec48SLars Fredriksen 	long	tv_sec;		/* seconds */
14159b1aec48SLars Fredriksen 	long	tv_usec;	/* microsecs */
14169b1aec48SLars Fredriksen     } delay;	    		/* _select() timeout */
14179b1aec48SLars Fredriksen 
14189b1aec48SLars Fredriksen     delay.tv_sec  = usec / 1000000L;
14199b1aec48SLars Fredriksen     delay.tv_usec = usec % 1000000L;
14209b1aec48SLars Fredriksen 
14219b1aec48SLars Fredriksen     return select(0, (long *)0, (long *)0, (long *)0, &delay);
14229b1aec48SLars Fredriksen }
14239b1aec48SLars Fredriksen #endif
14243d793cf1SPeter Wemm 
14253d793cf1SPeter Wemm void
14263d793cf1SPeter Wemm pack_array (array, end)
14273d793cf1SPeter Wemm     char **array; /* The address of the array of string pointers */
14283d793cf1SPeter Wemm     int    end;   /* The index of the next free entry before CLR_ */
14293d793cf1SPeter Wemm {
14303d793cf1SPeter Wemm     int i, j;
14313d793cf1SPeter Wemm 
14323d793cf1SPeter Wemm     for (i = 0; i < end; i++) {
14333d793cf1SPeter Wemm 	if (array[i] == NULL) {
14343d793cf1SPeter Wemm 	    for (j = i+1; j < end; ++j)
14353d793cf1SPeter Wemm 		if (array[j] != NULL)
14363d793cf1SPeter Wemm 		    array[i++] = array[j];
14373d793cf1SPeter Wemm 	    for (; i < end; ++i)
14383d793cf1SPeter Wemm 		array[i] = NULL;
14393d793cf1SPeter Wemm 	    break;
14403d793cf1SPeter Wemm 	}
14413d793cf1SPeter Wemm     }
14423d793cf1SPeter Wemm }
144301c855caSPeter Wemm 
144401c855caSPeter Wemm /*
144501c855caSPeter Wemm  * vfmtmsg - format a message into a buffer.  Like vsprintf except we
144601c855caSPeter Wemm  * also specify the length of the output buffer, and we handle the
144701c855caSPeter Wemm  * %m (error message) format.
144801c855caSPeter Wemm  * Doesn't do floating-point formats.
144901c855caSPeter Wemm  * Returns the number of chars put into buf.
145001c855caSPeter Wemm  */
145101c855caSPeter Wemm #define OUTCHAR(c)	(buflen > 0? (--buflen, *buf++ = (c)): 0)
145201c855caSPeter Wemm 
145301c855caSPeter Wemm int
145401c855caSPeter Wemm vfmtmsg(buf, buflen, fmt, args)
145501c855caSPeter Wemm     char *buf;
145601c855caSPeter Wemm     int buflen;
145701c855caSPeter Wemm     const char *fmt;
145801c855caSPeter Wemm     va_list args;
145901c855caSPeter Wemm {
146001c855caSPeter Wemm     int c, i, n;
146101c855caSPeter Wemm     int width, prec, fillch;
146201c855caSPeter Wemm     int base, len, neg, quoted;
146301c855caSPeter Wemm     unsigned long val = 0;
146401c855caSPeter Wemm     char *str, *buf0;
146501c855caSPeter Wemm     const char *f;
146601c855caSPeter Wemm     unsigned char *p;
146701c855caSPeter Wemm     char num[32];
146801c855caSPeter Wemm     static char hexchars[] = "0123456789abcdef";
146901c855caSPeter Wemm 
147001c855caSPeter Wemm     buf0 = buf;
147101c855caSPeter Wemm     --buflen;
147201c855caSPeter Wemm     while (buflen > 0) {
147301c855caSPeter Wemm 	for (f = fmt; *f != '%' && *f != 0; ++f)
147401c855caSPeter Wemm 	    ;
147501c855caSPeter Wemm 	if (f > fmt) {
147601c855caSPeter Wemm 	    len = f - fmt;
147701c855caSPeter Wemm 	    if (len > buflen)
147801c855caSPeter Wemm 		len = buflen;
147901c855caSPeter Wemm 	    memcpy(buf, fmt, len);
148001c855caSPeter Wemm 	    buf += len;
148101c855caSPeter Wemm 	    buflen -= len;
148201c855caSPeter Wemm 	    fmt = f;
148301c855caSPeter Wemm 	}
148401c855caSPeter Wemm 	if (*fmt == 0)
148501c855caSPeter Wemm 	    break;
148601c855caSPeter Wemm 	c = *++fmt;
148701c855caSPeter Wemm 	width = prec = 0;
148801c855caSPeter Wemm 	fillch = ' ';
148901c855caSPeter Wemm 	if (c == '0') {
149001c855caSPeter Wemm 	    fillch = '0';
149101c855caSPeter Wemm 	    c = *++fmt;
149201c855caSPeter Wemm 	}
149301c855caSPeter Wemm 	if (c == '*') {
149401c855caSPeter Wemm 	    width = va_arg(args, int);
149501c855caSPeter Wemm 	    c = *++fmt;
149601c855caSPeter Wemm 	} else {
149701c855caSPeter Wemm 	    while (isdigit(c)) {
149801c855caSPeter Wemm 		width = width * 10 + c - '0';
149901c855caSPeter Wemm 		c = *++fmt;
150001c855caSPeter Wemm 	    }
150101c855caSPeter Wemm 	}
150201c855caSPeter Wemm 	if (c == '.') {
150301c855caSPeter Wemm 	    c = *++fmt;
150401c855caSPeter Wemm 	    if (c == '*') {
150501c855caSPeter Wemm 		prec = va_arg(args, int);
150601c855caSPeter Wemm 		c = *++fmt;
150701c855caSPeter Wemm 	    } else {
150801c855caSPeter Wemm 		while (isdigit(c)) {
150901c855caSPeter Wemm 		    prec = prec * 10 + c - '0';
151001c855caSPeter Wemm 		    c = *++fmt;
151101c855caSPeter Wemm 		}
151201c855caSPeter Wemm 	    }
151301c855caSPeter Wemm 	}
151401c855caSPeter Wemm 	str = 0;
151501c855caSPeter Wemm 	base = 0;
151601c855caSPeter Wemm 	neg = 0;
151701c855caSPeter Wemm 	++fmt;
151801c855caSPeter Wemm 	switch (c) {
151901c855caSPeter Wemm 	case 'd':
152001c855caSPeter Wemm 	    i = va_arg(args, int);
152101c855caSPeter Wemm 	    if (i < 0) {
152201c855caSPeter Wemm 		neg = 1;
152301c855caSPeter Wemm 		val = -i;
152401c855caSPeter Wemm 	    } else
152501c855caSPeter Wemm 		val = i;
152601c855caSPeter Wemm 	    base = 10;
152701c855caSPeter Wemm 	    break;
152801c855caSPeter Wemm 	case 'o':
152901c855caSPeter Wemm 	    val = va_arg(args, unsigned int);
153001c855caSPeter Wemm 	    base = 8;
153101c855caSPeter Wemm 	    break;
153201c855caSPeter Wemm 	case 'x':
153301c855caSPeter Wemm 	    val = va_arg(args, unsigned int);
153401c855caSPeter Wemm 	    base = 16;
153501c855caSPeter Wemm 	    break;
153601c855caSPeter Wemm 	case 'p':
153701c855caSPeter Wemm 	    val = (unsigned long) va_arg(args, void *);
153801c855caSPeter Wemm 	    base = 16;
153901c855caSPeter Wemm 	    neg = 2;
154001c855caSPeter Wemm 	    break;
154101c855caSPeter Wemm 	case 's':
154201c855caSPeter Wemm 	    str = va_arg(args, char *);
154301c855caSPeter Wemm 	    break;
154401c855caSPeter Wemm 	case 'c':
154501c855caSPeter Wemm 	    num[0] = va_arg(args, int);
154601c855caSPeter Wemm 	    num[1] = 0;
154701c855caSPeter Wemm 	    str = num;
154801c855caSPeter Wemm 	    break;
154901c855caSPeter Wemm 	case 'm':
155001c855caSPeter Wemm 	    str = strerror(errno);
155101c855caSPeter Wemm 	    break;
155201c855caSPeter Wemm 	case 'v':		/* "visible" string */
155301c855caSPeter Wemm 	case 'q':		/* quoted string */
155401c855caSPeter Wemm 	    quoted = c == 'q';
155501c855caSPeter Wemm 	    p = va_arg(args, unsigned char *);
155601c855caSPeter Wemm 	    if (fillch == '0' && prec > 0) {
155701c855caSPeter Wemm 		n = prec;
155801c855caSPeter Wemm 	    } else {
155901c855caSPeter Wemm 		n = strlen((char *)p);
156001c855caSPeter Wemm 		if (prec > 0 && prec < n)
156101c855caSPeter Wemm 		    n = prec;
156201c855caSPeter Wemm 	    }
156301c855caSPeter Wemm 	    while (n > 0 && buflen > 0) {
156401c855caSPeter Wemm 		c = *p++;
156501c855caSPeter Wemm 		--n;
156601c855caSPeter Wemm 		if (!quoted && c >= 0x80) {
156701c855caSPeter Wemm 		    OUTCHAR('M');
156801c855caSPeter Wemm 		    OUTCHAR('-');
156901c855caSPeter Wemm 		    c -= 0x80;
157001c855caSPeter Wemm 		}
157101c855caSPeter Wemm 		if (quoted && (c == '"' || c == '\\'))
157201c855caSPeter Wemm 		    OUTCHAR('\\');
157301c855caSPeter Wemm 		if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
157401c855caSPeter Wemm 		    if (quoted) {
157501c855caSPeter Wemm 			OUTCHAR('\\');
157601c855caSPeter Wemm 			switch (c) {
157701c855caSPeter Wemm 			case '\t':	OUTCHAR('t');	break;
157801c855caSPeter Wemm 			case '\n':	OUTCHAR('n');	break;
157901c855caSPeter Wemm 			case '\b':	OUTCHAR('b');	break;
158001c855caSPeter Wemm 			case '\f':	OUTCHAR('f');	break;
158101c855caSPeter Wemm 			default:
158201c855caSPeter Wemm 			    OUTCHAR('x');
158301c855caSPeter Wemm 			    OUTCHAR(hexchars[c >> 4]);
158401c855caSPeter Wemm 			    OUTCHAR(hexchars[c & 0xf]);
158501c855caSPeter Wemm 			}
158601c855caSPeter Wemm 		    } else {
158701c855caSPeter Wemm 			if (c == '\t')
158801c855caSPeter Wemm 			    OUTCHAR(c);
158901c855caSPeter Wemm 			else {
159001c855caSPeter Wemm 			    OUTCHAR('^');
159101c855caSPeter Wemm 			    OUTCHAR(c ^ 0x40);
159201c855caSPeter Wemm 			}
159301c855caSPeter Wemm 		    }
159401c855caSPeter Wemm 		} else
159501c855caSPeter Wemm 		    OUTCHAR(c);
159601c855caSPeter Wemm 	    }
159701c855caSPeter Wemm 	    continue;
159801c855caSPeter Wemm 	default:
159901c855caSPeter Wemm 	    *buf++ = '%';
160001c855caSPeter Wemm 	    if (c != '%')
160101c855caSPeter Wemm 		--fmt;		/* so %z outputs %z etc. */
160201c855caSPeter Wemm 	    --buflen;
160301c855caSPeter Wemm 	    continue;
160401c855caSPeter Wemm 	}
160501c855caSPeter Wemm 	if (base != 0) {
160601c855caSPeter Wemm 	    str = num + sizeof(num);
160701c855caSPeter Wemm 	    *--str = 0;
160801c855caSPeter Wemm 	    while (str > num + neg) {
160901c855caSPeter Wemm 		*--str = hexchars[val % base];
161001c855caSPeter Wemm 		val = val / base;
161101c855caSPeter Wemm 		if (--prec <= 0 && val == 0)
161201c855caSPeter Wemm 		    break;
161301c855caSPeter Wemm 	    }
161401c855caSPeter Wemm 	    switch (neg) {
161501c855caSPeter Wemm 	    case 1:
161601c855caSPeter Wemm 		*--str = '-';
161701c855caSPeter Wemm 		break;
161801c855caSPeter Wemm 	    case 2:
161901c855caSPeter Wemm 		*--str = 'x';
162001c855caSPeter Wemm 		*--str = '0';
162101c855caSPeter Wemm 		break;
162201c855caSPeter Wemm 	    }
162301c855caSPeter Wemm 	    len = num + sizeof(num) - 1 - str;
162401c855caSPeter Wemm 	} else {
162501c855caSPeter Wemm 	    len = strlen(str);
162601c855caSPeter Wemm 	    if (prec > 0 && len > prec)
162701c855caSPeter Wemm 		len = prec;
162801c855caSPeter Wemm 	}
162901c855caSPeter Wemm 	if (width > 0) {
163001c855caSPeter Wemm 	    if (width > buflen)
163101c855caSPeter Wemm 		width = buflen;
163201c855caSPeter Wemm 	    if ((n = width - len) > 0) {
163301c855caSPeter Wemm 		buflen -= n;
163401c855caSPeter Wemm 		for (; n > 0; --n)
163501c855caSPeter Wemm 		    *buf++ = fillch;
163601c855caSPeter Wemm 	    }
163701c855caSPeter Wemm 	}
163801c855caSPeter Wemm 	if (len > buflen)
163901c855caSPeter Wemm 	    len = buflen;
164001c855caSPeter Wemm 	memcpy(buf, str, len);
164101c855caSPeter Wemm 	buf += len;
164201c855caSPeter Wemm 	buflen -= len;
164301c855caSPeter Wemm     }
164401c855caSPeter Wemm     *buf = 0;
164501c855caSPeter Wemm     return buf - buf0;
164601c855caSPeter Wemm }
1647