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