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