blob: 336c0f4995f4ab3dadd1612673d251ec0d3507d5 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
2 * Chat -- a program for automatic session establishment (i.e. dial
3 * the phone and log in).
4 *
5 * Standard termination codes:
6 * 0 - successful completion of the script
7 * 1 - invalid argument, expect string too large, etc.
8 * 2 - error on an I/O operation or fatal error condition.
9 * 3 - timeout waiting for a simple string.
10 * 4 - the first string declared as "ABORT"
11 * 5 - the second string declared as "ABORT"
12 * 6 - ... and so on for successive ABORT strings.
13 *
14 * This software is in the public domain.
15 *
16 * -----------------
lh9ed821d2023-04-07 01:36:19 -070017 * 22-May-99 added environment substitutuion, enabled with -E switch.
18 * Andreas Arens <andras@cityweb.de>.
19 *
20 * 12-May-99 added a feature to read data to be sent from a file,
21 * if the send string starts with @. Idea from gpk <gpk@onramp.net>.
22 *
23 * added -T and -U option and \T and \U substitution to pass a phone
24 * number into chat script. Two are needed for some ISDN TA applications.
25 * Keith Dart <kdart@cisco.com>
26 *
27 *
28 * Added SAY keyword to send output to stderr.
29 * This allows to turn ECHO OFF and to output specific, user selected,
30 * text to give progress messages. This best works when stderr
31 * exists (i.e.: pppd in nodetach mode).
32 *
33 * Added HANGUP directives to allow for us to be called
34 * back. When HANGUP is set to NO, chat will not hangup at HUP signal.
35 * We rely on timeouts in that case.
36 *
37 * Added CLR_ABORT to clear previously set ABORT string. This has been
38 * dictated by the HANGUP above as "NO CARRIER" (for example) must be
39 * an ABORT condition until we know the other host is going to close
40 * the connection for call back. As soon as we have completed the
41 * first stage of the call back sequence, "NO CARRIER" is a valid, non
42 * fatal string. As soon as we got called back (probably get "CONNECT"),
43 * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
44 * Note that CLR_ABORT packs the abort_strings[] array so that we do not
45 * have unused entries not being reclaimed.
46 *
47 * In the same vein as above, added CLR_REPORT keyword.
48 *
49 * Allow for comments. Line starting with '#' are comments and are
50 * ignored. If a '#' is to be expected as the first character, the
51 * expect string must be quoted.
52 *
53 *
54 * Francis Demierre <Francis@SwissMail.Com>
55 * Thu May 15 17:15:40 MET DST 1997
56 *
57 *
58 * Added -r "report file" switch & REPORT keyword.
59 * Robert Geer <bgeer@xmission.com>
60 *
61 * Added -s "use stderr" and -S "don't use syslog" switches.
62 * June 18, 1997
63 * Karl O. Pinc <kop@meme.com>
64 *
65 *
66 * Added -e "echo" switch & ECHO keyword
67 * Dick Streefland <dicks@tasking.nl>
68 *
69 *
70 * Considerable updates and modifications by
71 * Al Longyear <longyear@pobox.com>
72 * Paul Mackerras <paulus@cs.anu.edu.au>
73 *
74 *
75 * The original author is:
76 *
77 * Karl Fox <karl@MorningStar.Com>
78 * Morning Star Technologies, Inc.
79 * 1760 Zollinger Road
80 * Columbus, OH 43221
81 * (614)451-1883
82 *
83 */
84
lh9ed821d2023-04-07 01:36:19 -070085#include <stdio.h>
86#include <ctype.h>
87#include <time.h>
88#include <fcntl.h>
89#include <signal.h>
90#include <errno.h>
91#include <string.h>
92#include <stdlib.h>
93#include <unistd.h>
94#include <sys/types.h>
95#include <sys/stat.h>
96#include <syslog.h>
xf.li84027492024-04-09 00:17:51 -070097#include <stdarg.h>
lh9ed821d2023-04-07 01:36:19 -070098
99#ifndef TERMIO
100#undef TERMIOS
101#define TERMIOS
102#endif
103
104#ifdef TERMIO
105#include <termio.h>
106#endif
107#ifdef TERMIOS
108#include <termios.h>
109#endif
110
111#define STR_LEN 1024
112
113#ifndef SIGTYPE
114#define SIGTYPE void
115#endif
116
lh9ed821d2023-04-07 01:36:19 -0700117#ifndef O_NONBLOCK
118#define O_NONBLOCK O_NDELAY
119#endif
120
121#ifdef SUNOS
122extern int sys_nerr;
123extern char *sys_errlist[];
124#define memmove(to, from, n) bcopy(from, to, n)
125#define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
126 "unknown error")
127#endif
128
129/*************** Micro getopt() *********************************************/
130#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
131 (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
132 &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
133#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
134 (_O=4,(char*)0):(char*)0)
135#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
136#define ARG(c,v) (c?(--c,*v++):(char*)0)
137
138static int _O = 0; /* Internal state */
139/*************** Micro getopt() *********************************************/
140
141char *program_name;
142
143#define MAX_ABORTS 50
144#define MAX_REPORTS 50
145#define DEFAULT_CHAT_TIMEOUT 45
146
147int echo = 0;
148int verbose = 0;
149int to_log = 1;
150int to_stderr = 0;
151int Verbose = 0;
152int quiet = 0;
153int report = 0;
154int use_env = 0;
155int exit_code = 0;
156FILE* report_fp = (FILE *) 0;
157char *report_file = (char *) 0;
158char *chat_file = (char *) 0;
159char *phone_num = (char *) 0;
160char *phone_num2 = (char *) 0;
161int timeout = DEFAULT_CHAT_TIMEOUT;
lh9ed821d2023-04-07 01:36:19 -0700162
163int have_tty_parameters = 0;
164
165#ifdef TERMIO
166#define term_parms struct termio
167#define get_term_param(param) ioctl(0, TCGETA, param)
168#define set_term_param(param) ioctl(0, TCSETA, param)
169struct termio saved_tty_parameters;
170#endif
171
172#ifdef TERMIOS
173#define term_parms struct termios
174#define get_term_param(param) tcgetattr(0, param)
175#define set_term_param(param) tcsetattr(0, TCSANOW, param)
176struct termios saved_tty_parameters;
177#endif
178
179char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
180 fail_buffer[50];
181int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
182int clear_abort_next = 0;
183
184char *report_string[MAX_REPORTS] ;
xf.li84027492024-04-09 00:17:51 -0700185char report_buffer[4096] ;
lh9ed821d2023-04-07 01:36:19 -0700186int n_reports = 0, report_next = 0, report_gathering = 0 ;
187int clear_report_next = 0;
188
189int say_next = 0, hup_next = 0;
190
xf.li84027492024-04-09 00:17:51 -0700191void *dup_mem (void *b, size_t c);
192void *copy_of (char *s);
193char *grow (char *s, char **p, size_t len);
194void usage (void);
195void msgf (const char *fmt, ...);
196void fatal (int code, const char *fmt, ...);
197SIGTYPE sigalrm (int signo);
198SIGTYPE sigint (int signo);
199SIGTYPE sigterm (int signo);
200SIGTYPE sighup (int signo);
201void checksigs(void);
202void init (void);
203void set_tty_parameters (void);
204int echo_stderr (int);
205void break_sequence (void);
206void terminate (int status);
207void do_file (char *chat_file);
208int get_string (register char *string);
209int put_string (register char *s);
210int write_char (int c);
211int put_char (int c);
212int get_char (void);
213int chat_send (register char *s);
214char *character (int c);
215void chat_expect (register char *s);
216char *clean (register char *s, int sending);
217void break_sequence (void);
218void pack_array (char **array, int end);
219char *expect_strtok (char *, char *);
220int vfmtmsg (char *, int, const char *, va_list); /* vsprintf++ */
lh9ed821d2023-04-07 01:36:19 -0700221
xf.li84027492024-04-09 00:17:51 -0700222int main (int, char *[]);
lh9ed821d2023-04-07 01:36:19 -0700223
xf.li84027492024-04-09 00:17:51 -0700224void *dup_mem(void *b, size_t c)
lh9ed821d2023-04-07 01:36:19 -0700225{
226 void *ans = malloc (c);
227 if (!ans)
228 fatal(2, "memory error!");
229
230 memcpy (ans, b, c);
231 return ans;
232}
233
xf.li84027492024-04-09 00:17:51 -0700234void *copy_of (char *s)
lh9ed821d2023-04-07 01:36:19 -0700235{
236 return dup_mem (s, strlen (s) + 1);
237}
238
239/* grow a char buffer and keep a pointer offset */
xf.li84027492024-04-09 00:17:51 -0700240char *grow(char *s, char **p, size_t len)
lh9ed821d2023-04-07 01:36:19 -0700241{
242 size_t l = *p - s; /* save p as distance into s */
243
244 s = realloc(s, len);
245 if (!s)
246 fatal(2, "memory error!");
247 *p = s + l; /* restore p */
248 return s;
249}
250
251/*
252 * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
xf.li84027492024-04-09 00:17:51 -0700253 * [ -r report-file ] \
lh9ed821d2023-04-07 01:36:19 -0700254 * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
255 *
256 * Perform a UUCP-dialer-like chat script on stdin and stdout.
lh9ed821d2023-04-07 01:36:19 -0700257 */
258int
xf.li84027492024-04-09 00:17:51 -0700259main(int argc, char **argv)
lh9ed821d2023-04-07 01:36:19 -0700260{
261 int option;
262 char *arg;
xf.li84027492024-04-09 00:17:51 -0700263
lh9ed821d2023-04-07 01:36:19 -0700264 program_name = *argv;
xf.li84027492024-04-09 00:17:51 -0700265 tzset();
lh9ed821d2023-04-07 01:36:19 -0700266
267 while ((option = OPTION(argc, argv)) != 0) {
268 switch (option) {
269 case 'e':
270 ++echo;
271 break;
272
273 case 'E':
274 ++use_env;
275 break;
276
277 case 'v':
278 ++verbose;
279 break;
280
lh9ed821d2023-04-07 01:36:19 -0700281 case 'V':
282 ++Verbose;
283 break;
284
285 case 's':
286 ++to_stderr;
287 break;
288
289 case 'S':
290 to_log = 0;
291 break;
292
293 case 'f':
294 if ((arg = OPTARG(argc, argv)) != NULL)
295 chat_file = copy_of(arg);
296 else
297 usage();
298 break;
299
300 case 't':
301 if ((arg = OPTARG(argc, argv)) != NULL)
302 timeout = atoi(arg);
303 else
304 usage();
305 break;
306
lh9ed821d2023-04-07 01:36:19 -0700307 case 'r':
lh9ed821d2023-04-07 01:36:19 -0700308 arg = OPTARG (argc, argv);
309 if (arg) {
310 if (report_fp != NULL)
311 fclose (report_fp);
312 report_file = copy_of (arg);
xf.li84027492024-04-09 00:17:51 -0700313 report_fp = fopen (report_file, "a");
lh9ed821d2023-04-07 01:36:19 -0700314 if (report_fp != NULL) {
315 if (verbose)
316 fprintf (report_fp, "Opening \"%s\"...\n",
317 report_file);
318 report = 1;
319 }
320 }
321 break;
322
323 case 'T':
324 if ((arg = OPTARG(argc, argv)) != NULL)
325 phone_num = copy_of(arg);
326 else
327 usage();
328 break;
329
330 case 'U':
331 if ((arg = OPTARG(argc, argv)) != NULL)
332 phone_num2 = copy_of(arg);
333 else
334 usage();
335 break;
336
337 default:
338 usage();
339 break;
340 }
341 }
lh9ed821d2023-04-07 01:36:19 -0700342/*
343 * Default the report file to the stderr location
344 */
345 if (report_fp == NULL)
346 report_fp = stderr;
347
348 if (to_log) {
lh9ed821d2023-04-07 01:36:19 -0700349 openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
350
351 if (verbose)
352 setlogmask(LOG_UPTO(LOG_INFO));
353 else
354 setlogmask(LOG_UPTO(LOG_WARNING));
lh9ed821d2023-04-07 01:36:19 -0700355 }
356
357 init();
358
359 if (chat_file != NULL) {
360 arg = ARG(argc, argv);
361 if (arg != NULL)
362 usage();
363 else
364 do_file (chat_file);
365 } else {
366 while ((arg = ARG(argc, argv)) != NULL) {
367 chat_expect(arg);
368
369 if ((arg = ARG(argc, argv)) != NULL)
370 chat_send(arg);
xf.li84027492024-04-09 00:17:51 -0700371 checksigs();
lh9ed821d2023-04-07 01:36:19 -0700372 }
373 }
374
375 terminate(0);
376 return 0;
377}
378
379/*
380 * Process a chat script when read from a file.
381 */
382
xf.li84027492024-04-09 00:17:51 -0700383void do_file (char *chat_file)
lh9ed821d2023-04-07 01:36:19 -0700384{
385 int linect, sendflg;
386 char *sp, *arg, quote;
387 char buf [STR_LEN];
388 FILE *cfp;
xf.li84027492024-04-09 00:17:51 -0700389
lh9ed821d2023-04-07 01:36:19 -0700390 cfp = fopen (chat_file, "r");
391 if (cfp == NULL)
392 fatal(1, "%s -- open failed: %m", chat_file);
393
394 linect = 0;
395 sendflg = 0;
396
397 while (fgets(buf, STR_LEN, cfp) != NULL) {
398 sp = strchr (buf, '\n');
399 if (sp)
400 *sp = '\0';
401
402 linect++;
403 sp = buf;
404
405 /* lines starting with '#' are comments. If a real '#'
406 is to be expected, it should be quoted .... */
407 if ( *sp == '#' )
408 continue;
409
410 while (*sp != '\0') {
411 if (*sp == ' ' || *sp == '\t') {
412 ++sp;
413 continue;
414 }
415
416 if (*sp == '"' || *sp == '\'') {
417 quote = *sp++;
418 arg = sp;
lh9ed821d2023-04-07 01:36:19 -0700419 while (*sp != quote) {
420 if (*sp == '\0')
421 fatal(1, "unterminated quote (line %d)", linect);
422
423 if (*sp++ == '\\') {
424 if (*sp != '\0')
425 ++sp;
426 }
427 }
428 }
429 else {
430 arg = sp;
lh9ed821d2023-04-07 01:36:19 -0700431 while (*sp != '\0' && *sp != ' ' && *sp != '\t')
432 ++sp;
433 }
434
435 if (*sp != '\0')
436 *sp++ = '\0';
437
438 if (sendflg)
lh9ed821d2023-04-07 01:36:19 -0700439 chat_send (arg);
lh9ed821d2023-04-07 01:36:19 -0700440 else
lh9ed821d2023-04-07 01:36:19 -0700441 chat_expect (arg);
lh9ed821d2023-04-07 01:36:19 -0700442 sendflg = !sendflg;
xf.li84027492024-04-09 00:17:51 -0700443 checksigs();
lh9ed821d2023-04-07 01:36:19 -0700444 }
445 }
xf.li84027492024-04-09 00:17:51 -0700446 checksigs();
lh9ed821d2023-04-07 01:36:19 -0700447 fclose (cfp);
448}
449
450/*
451 * We got an error parsing the command line.
452 */
xf.li84027492024-04-09 00:17:51 -0700453void usage(void)
lh9ed821d2023-04-07 01:36:19 -0700454{
455 fprintf(stderr, "\
xf.li84027492024-04-09 00:17:51 -0700456Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file]\n\
457 [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
lh9ed821d2023-04-07 01:36:19 -0700458 exit(1);
459}
460
461char line[1024];
462
463/*
464 * Send a message to syslog and/or stderr.
465 */
xf.li84027492024-04-09 00:17:51 -0700466void msgf(const char *fmt, ...)
lh9ed821d2023-04-07 01:36:19 -0700467{
468 va_list args;
469
lh9ed821d2023-04-07 01:36:19 -0700470 va_start(args, fmt);
lh9ed821d2023-04-07 01:36:19 -0700471
472 vfmtmsg(line, sizeof(line), fmt, args);
473 if (to_log)
xf.li84027492024-04-09 00:17:51 -0700474 syslog(LOG_INFO, "%s", line);
lh9ed821d2023-04-07 01:36:19 -0700475 if (to_stderr)
476 fprintf(stderr, "%s\n", line);
xf.li84027492024-04-09 00:17:51 -0700477 va_end(args);
lh9ed821d2023-04-07 01:36:19 -0700478}
479
480/*
481 * Print an error message and terminate.
482 */
483
xf.li84027492024-04-09 00:17:51 -0700484void fatal(int code, const char *fmt, ...)
lh9ed821d2023-04-07 01:36:19 -0700485{
486 va_list args;
487
lh9ed821d2023-04-07 01:36:19 -0700488 va_start(args, fmt);
lh9ed821d2023-04-07 01:36:19 -0700489
490 vfmtmsg(line, sizeof(line), fmt, args);
491 if (to_log)
xf.li84027492024-04-09 00:17:51 -0700492 syslog(LOG_ERR, "%s", line);
lh9ed821d2023-04-07 01:36:19 -0700493 if (to_stderr)
494 fprintf(stderr, "%s\n", line);
xf.li84027492024-04-09 00:17:51 -0700495 va_end(args);
lh9ed821d2023-04-07 01:36:19 -0700496 terminate(code);
497}
498
499int alarmed = 0;
xf.li84027492024-04-09 00:17:51 -0700500int alarmsig = 0;
lh9ed821d2023-04-07 01:36:19 -0700501
xf.li84027492024-04-09 00:17:51 -0700502SIGTYPE sigalrm(int signo)
lh9ed821d2023-04-07 01:36:19 -0700503{
504 int flags;
505
506 alarm(1);
xf.li84027492024-04-09 00:17:51 -0700507 alarmed = 1;
508 alarmsig = 1;
509}
lh9ed821d2023-04-07 01:36:19 -0700510
xf.li84027492024-04-09 00:17:51 -0700511const char *fatalsig = NULL;
lh9ed821d2023-04-07 01:36:19 -0700512
xf.li84027492024-04-09 00:17:51 -0700513SIGTYPE sigint(int signo)
514{
515 fatalsig = "SIGINT";
516}
lh9ed821d2023-04-07 01:36:19 -0700517
xf.li84027492024-04-09 00:17:51 -0700518SIGTYPE sigterm(int signo)
519{
520 fatalsig = "SIGTERM";
521}
522
523SIGTYPE sighup(int signo)
524{
525 fatalsig = "SIGHUP";
526}
527
528void checksigs(void)
529{
530 int err;
531 const char *signame;
532
533 if (fatalsig) {
534 signame = fatalsig;
535 fatalsig = NULL;
536 alarmsig = 0;
537 fatal(2, signame);
538 }
539 if (alarmsig && verbose) {
540 err = errno;
lh9ed821d2023-04-07 01:36:19 -0700541 msgf("alarm");
xf.li84027492024-04-09 00:17:51 -0700542 errno = err;
543 alarmsig = 0;
544 }
lh9ed821d2023-04-07 01:36:19 -0700545}
546
xf.li84027492024-04-09 00:17:51 -0700547void init(void)
lh9ed821d2023-04-07 01:36:19 -0700548{
xf.li84027492024-04-09 00:17:51 -0700549 struct sigaction sa;
lh9ed821d2023-04-07 01:36:19 -0700550
xf.li84027492024-04-09 00:17:51 -0700551 memset(&sa, 0, sizeof(sa));
552 sa.sa_handler = sigint;
553 sigaction(SIGINT, &sa, NULL);
554 sa.sa_handler = sigterm;
555 sigaction(SIGTERM, &sa, NULL);
556 sa.sa_handler = sighup;
557 sigaction(SIGHUP, &sa, NULL);
lh9ed821d2023-04-07 01:36:19 -0700558
lh9ed821d2023-04-07 01:36:19 -0700559 set_tty_parameters();
xf.li84027492024-04-09 00:17:51 -0700560 sa.sa_handler = sigalrm;
561 sigaction(SIGALRM, &sa, NULL);
lh9ed821d2023-04-07 01:36:19 -0700562 alarm(0);
563 alarmed = 0;
564}
565
xf.li84027492024-04-09 00:17:51 -0700566void set_tty_parameters(void)
lh9ed821d2023-04-07 01:36:19 -0700567{
568#if defined(get_term_param)
569 term_parms t;
570
571 if (get_term_param (&t) < 0)
lh9ed821d2023-04-07 01:36:19 -0700572 fatal(2, "Can't get terminal parameters: %m");
lh9ed821d2023-04-07 01:36:19 -0700573
574 saved_tty_parameters = t;
575 have_tty_parameters = 1;
576
577 t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
578 t.c_oflag = 0;
579 t.c_lflag = 0;
580 t.c_cc[VERASE] =
581 t.c_cc[VKILL] = 0;
582 t.c_cc[VMIN] = 1;
583 t.c_cc[VTIME] = 0;
584
585 if (set_term_param (&t) < 0)
lh9ed821d2023-04-07 01:36:19 -0700586 fatal(2, "Can't set terminal parameters: %m");
lh9ed821d2023-04-07 01:36:19 -0700587#endif
588}
589
xf.li84027492024-04-09 00:17:51 -0700590void break_sequence(void)
lh9ed821d2023-04-07 01:36:19 -0700591{
592#ifdef TERMIOS
593 tcsendbreak (0, 0);
594#endif
595}
596
xf.li84027492024-04-09 00:17:51 -0700597void terminate(int status)
lh9ed821d2023-04-07 01:36:19 -0700598{
599 static int terminating = 0;
600
601 if (terminating)
602 exit(status);
603 terminating = 1;
604 echo_stderr(-1);
605/*
606 * Allow the last of the report string to be gathered before we terminate.
607 */
xf.li84027492024-04-09 00:17:51 -0700608 if (report_gathering) {
lh9ed821d2023-04-07 01:36:19 -0700609 int c, rep_len;
610
611 rep_len = strlen(report_buffer);
xf.li84027492024-04-09 00:17:51 -0700612 while (rep_len + 1 < sizeof(report_buffer)) {
lh9ed821d2023-04-07 01:36:19 -0700613 alarm(1);
614 c = get_char();
615 alarm(0);
616 if (c < 0 || iscntrl(c))
617 break;
618 report_buffer[rep_len] = c;
619 ++rep_len;
620 }
621 report_buffer[rep_len] = 0;
622 fprintf (report_fp, "chat: %s\n", report_buffer);
623 }
624 if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
625 if (verbose)
626 fprintf (report_fp, "Closing \"%s\".\n", report_file);
627 fclose (report_fp);
628 report_fp = (FILE *) NULL;
629 }
630
631#if defined(get_term_param)
632 if (have_tty_parameters) {
633 if (set_term_param (&saved_tty_parameters) < 0)
lh9ed821d2023-04-07 01:36:19 -0700634 fatal(2, "Can't restore terminal parameters: %m");
lh9ed821d2023-04-07 01:36:19 -0700635 }
636#endif
xf.li84027492024-04-09 00:17:51 -0700637
lh9ed821d2023-04-07 01:36:19 -0700638 exit(status);
639}
640
641/*
642 * 'Clean up' this string.
643 */
xf.li84027492024-04-09 00:17:51 -0700644char *clean(register char *s,
645 int sending) /* set to 1 when sending (putting) this string. */
lh9ed821d2023-04-07 01:36:19 -0700646{
647 char cur_chr;
648 char *s1, *p, *phchar;
649 int add_return = sending;
650 size_t len = strlen(s) + 3; /* see len comments below */
651
652#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
653#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
654 || (((chr) >= 'a') && ((chr) <= 'z')) \
655 || (((chr) >= 'A') && ((chr) <= 'Z')) \
656 || (chr) == '_')
657
658 p = s1 = malloc(len);
659 if (!p)
660 fatal(2, "memory error!");
661 while (*s) {
662 cur_chr = *s++;
663 if (cur_chr == '^') {
664 cur_chr = *s++;
665 if (cur_chr == '\0') {
666 *p++ = '^';
667 break;
668 }
669 cur_chr &= 0x1F;
670 if (cur_chr != 0) {
671 *p++ = cur_chr;
672 }
673 continue;
674 }
675
676 if (use_env && cur_chr == '$') { /* ARI */
677 char c;
678
679 phchar = s;
680 while (isalnumx(*s))
681 s++;
682 c = *s; /* save */
683 *s = '\0';
684 phchar = getenv(phchar);
685 *s = c; /* restore */
686 if (phchar) {
687 len += strlen(phchar);
688 s1 = grow(s1, &p, len);
689 while (*phchar)
690 *p++ = *phchar++;
691 }
692 continue;
693 }
694
695 if (cur_chr != '\\') {
696 *p++ = cur_chr;
697 continue;
698 }
699
700 cur_chr = *s++;
701 if (cur_chr == '\0') {
702 if (sending) {
703 *p++ = '\\';
704 *p++ = '\\'; /* +1 for len */
705 }
706 break;
707 }
708
709 switch (cur_chr) {
710 case 'b':
711 *p++ = '\b';
712 break;
713
714 case 'c':
715 if (sending && *s == '\0')
716 add_return = 0;
717 else
718 *p++ = cur_chr;
719 break;
720
721 case '\\':
722 case 'K':
723 case 'p':
724 case 'd':
725 if (sending)
726 *p++ = '\\';
727 *p++ = cur_chr;
728 break;
729
730 case 'T':
731 if (sending && phone_num) {
732 len += strlen(phone_num);
733 s1 = grow(s1, &p, len);
734 for (phchar = phone_num; *phchar != '\0'; phchar++)
735 *p++ = *phchar;
736 }
737 else {
738 *p++ = '\\';
739 *p++ = 'T';
740 }
741 break;
742
743 case 'U':
744 if (sending && phone_num2) {
745 len += strlen(phone_num2);
746 s1 = grow(s1, &p, len);
747 for (phchar = phone_num2; *phchar != '\0'; phchar++)
748 *p++ = *phchar;
749 }
750 else {
751 *p++ = '\\';
752 *p++ = 'U';
753 }
754 break;
755
756 case 'q':
757 quiet = 1;
758 break;
759
760 case 'r':
761 *p++ = '\r';
762 break;
763
764 case 'n':
765 *p++ = '\n';
766 break;
767
768 case 's':
769 *p++ = ' ';
770 break;
771
772 case 't':
773 *p++ = '\t';
774 break;
775
776 case 'N':
777 if (sending) {
778 *p++ = '\\';
779 *p++ = '\0';
780 }
781 else
782 *p++ = 'N';
783 break;
784
785 case '$': /* ARI */
786 if (use_env) {
787 *p++ = cur_chr;
788 break;
789 }
790 /* FALL THROUGH */
791
792 default:
793 if (isoctal (cur_chr)) {
794 cur_chr &= 0x07;
795 if (isoctal (*s)) {
796 cur_chr <<= 3;
797 cur_chr |= *s++ - '0';
798 if (isoctal (*s)) {
799 cur_chr <<= 3;
800 cur_chr |= *s++ - '0';
801 }
802 }
803
804 if (cur_chr != 0 || sending) {
805 if (sending && (cur_chr == '\\' || cur_chr == 0))
806 *p++ = '\\';
807 *p++ = cur_chr;
808 }
809 break;
810 }
811
812 if (sending)
813 *p++ = '\\';
814 *p++ = cur_chr;
815 break;
816 }
817 }
818
819 if (add_return)
820 *p++ = '\r'; /* +2 for len */
821
822 *p = '\0'; /* +3 for len */
823 return s1;
824}
825
826/*
827 * A modified version of 'strtok'. This version skips \ sequences.
828 */
829
xf.li84027492024-04-09 00:17:51 -0700830char *expect_strtok (char *s, char *term)
lh9ed821d2023-04-07 01:36:19 -0700831{
832 static char *str = "";
833 int escape_flag = 0;
834 char *result;
835
836/*
837 * If a string was specified then do initial processing.
838 */
839 if (s)
840 str = s;
841
842/*
843 * If this is the escape flag then reset it and ignore the character.
844 */
845 if (*str)
846 result = str;
847 else
848 result = (char *) 0;
849
850 while (*str) {
851 if (escape_flag) {
852 escape_flag = 0;
853 ++str;
854 continue;
855 }
856
857 if (*str == '\\') {
858 ++str;
859 escape_flag = 1;
860 continue;
861 }
862
863/*
864 * If this is not in the termination string, continue.
865 */
866 if (strchr (term, *str) == (char *) 0) {
867 ++str;
868 continue;
869 }
870
871/*
872 * This is the terminator. Mark the end of the string and stop.
873 */
874 *str++ = '\0';
875 break;
876 }
877 return (result);
878}
879
880/*
881 * Process the expect string
882 */
883
xf.li84027492024-04-09 00:17:51 -0700884void chat_expect (char *s)
lh9ed821d2023-04-07 01:36:19 -0700885{
886 char *expect;
887 char *reply;
888
889 if (strcmp(s, "HANGUP") == 0) {
890 ++hup_next;
891 return;
892 }
893
894 if (strcmp(s, "ABORT") == 0) {
895 ++abort_next;
896 return;
897 }
898
899 if (strcmp(s, "CLR_ABORT") == 0) {
900 ++clear_abort_next;
901 return;
902 }
903
904 if (strcmp(s, "REPORT") == 0) {
905 ++report_next;
906 return;
907 }
908
909 if (strcmp(s, "CLR_REPORT") == 0) {
910 ++clear_report_next;
911 return;
912 }
913
914 if (strcmp(s, "TIMEOUT") == 0) {
915 ++timeout_next;
916 return;
917 }
918
919 if (strcmp(s, "ECHO") == 0) {
920 ++echo_next;
921 return;
922 }
923
924 if (strcmp(s, "SAY") == 0) {
925 ++say_next;
926 return;
927 }
928
929/*
930 * Fetch the expect and reply string.
931 */
932 for (;;) {
933 expect = expect_strtok (s, "-");
934 s = (char *) 0;
935
936 if (expect == (char *) 0)
937 return;
938
939 reply = expect_strtok (s, "-");
940
941/*
942 * Handle the expect string. If successful then exit.
943 */
944 if (get_string (expect))
945 return;
946
947/*
948 * If there is a sub-reply string then send it. Otherwise any condition
949 * is terminal.
950 */
951 if (reply == (char *) 0 || exit_code != 3)
952 break;
953
954 chat_send (reply);
xf.li84027492024-04-09 00:17:51 -0700955 checksigs();
lh9ed821d2023-04-07 01:36:19 -0700956 }
957
958/*
959 * The expectation did not occur. This is terminal.
960 */
961 if (fail_reason)
962 msgf("Failed (%s)", fail_reason);
963 else
964 msgf("Failed");
965 terminate(exit_code);
966}
967
968/*
969 * Translate the input character to the appropriate string for printing
970 * the data.
971 */
972
xf.li84027492024-04-09 00:17:51 -0700973char *character(int c)
lh9ed821d2023-04-07 01:36:19 -0700974{
975 static char string[10];
976 char *meta;
977
978 meta = (c & 0x80) ? "M-" : "";
979 c &= 0x7F;
980
981 if (c < 32)
982 sprintf(string, "%s^%c", meta, (int)c + '@');
983 else if (c == 127)
984 sprintf(string, "%s^?", meta);
985 else
986 sprintf(string, "%s%c", meta, c);
987
988 return (string);
989}
990
991/*
992 * process the reply string
993 */
xf.li84027492024-04-09 00:17:51 -0700994int chat_send (register char *s)
lh9ed821d2023-04-07 01:36:19 -0700995{
996 char file_data[STR_LEN];
xf.li84027492024-04-09 00:17:51 -0700997 int len, ret = 0;
998 struct sigaction sa;
lh9ed821d2023-04-07 01:36:19 -0700999
1000 if (say_next) {
1001 say_next = 0;
1002 s = clean(s, 1);
xf.li84027492024-04-09 00:17:51 -07001003 len = strlen(s);
1004 ret = write(2, s, len) != len;
lh9ed821d2023-04-07 01:36:19 -07001005 free(s);
xf.li84027492024-04-09 00:17:51 -07001006 return ret;
lh9ed821d2023-04-07 01:36:19 -07001007 }
1008
1009 if (hup_next) {
1010 hup_next = 0;
xf.li84027492024-04-09 00:17:51 -07001011 memset(&sa, 0, sizeof(sa));
1012
lh9ed821d2023-04-07 01:36:19 -07001013 if (strcmp(s, "OFF") == 0)
xf.li84027492024-04-09 00:17:51 -07001014 sa.sa_handler = SIG_IGN;
lh9ed821d2023-04-07 01:36:19 -07001015 else
xf.li84027492024-04-09 00:17:51 -07001016 sa.sa_handler = sighup;
1017 sigaction(SIGHUP, &sa, NULL);
1018 return 0;
lh9ed821d2023-04-07 01:36:19 -07001019 }
1020
1021 if (echo_next) {
1022 echo_next = 0;
1023 echo = (strcmp(s, "ON") == 0);
xf.li84027492024-04-09 00:17:51 -07001024 return 0;
lh9ed821d2023-04-07 01:36:19 -07001025 }
1026
1027 if (abort_next) {
1028 char *s1;
1029
1030 abort_next = 0;
1031
1032 if (n_aborts >= MAX_ABORTS)
1033 fatal(2, "Too many ABORT strings");
1034
1035 s1 = clean(s, 0);
1036
xf.li84027492024-04-09 00:17:51 -07001037 if (strlen(s1) + 1 > sizeof(fail_buffer))
lh9ed821d2023-04-07 01:36:19 -07001038 fatal(1, "Illegal or too-long ABORT string ('%v')", s);
1039
1040 abort_string[n_aborts++] = s1;
1041
1042 if (verbose)
xf.li84027492024-04-09 00:17:51 -07001043 msgf("abort on (%v)", s1);
1044 return 0;
lh9ed821d2023-04-07 01:36:19 -07001045 }
1046
1047 if (clear_abort_next) {
1048 char *s1;
1049 int i;
1050 int old_max;
1051 int pack = 0;
1052
1053 clear_abort_next = 0;
1054
1055 s1 = clean(s, 0);
1056
xf.li84027492024-04-09 00:17:51 -07001057 if (strlen(s1) + 1 > sizeof(fail_buffer))
lh9ed821d2023-04-07 01:36:19 -07001058 fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
1059
1060 old_max = n_aborts;
1061 for (i=0; i < n_aborts; i++) {
1062 if ( strcmp(s1,abort_string[i]) == 0 ) {
1063 free(abort_string[i]);
1064 abort_string[i] = NULL;
1065 pack++;
1066 n_aborts--;
1067 if (verbose)
xf.li84027492024-04-09 00:17:51 -07001068 msgf("clear abort on (%v)", s1);
lh9ed821d2023-04-07 01:36:19 -07001069 }
1070 }
1071 free(s1);
1072 if (pack)
1073 pack_array(abort_string,old_max);
xf.li84027492024-04-09 00:17:51 -07001074 return 0;
lh9ed821d2023-04-07 01:36:19 -07001075 }
1076
1077 if (report_next) {
1078 char *s1;
1079
1080 report_next = 0;
1081 if (n_reports >= MAX_REPORTS)
1082 fatal(2, "Too many REPORT strings");
1083
1084 s1 = clean(s, 0);
xf.li84027492024-04-09 00:17:51 -07001085 if (strlen(s1) + 1 > sizeof(fail_buffer))
lh9ed821d2023-04-07 01:36:19 -07001086 fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1087
1088 report_string[n_reports++] = s1;
1089
1090 if (verbose)
xf.li84027492024-04-09 00:17:51 -07001091 msgf("report (%v)", s1);
1092 return 0;
lh9ed821d2023-04-07 01:36:19 -07001093 }
1094
1095 if (clear_report_next) {
1096 char *s1;
1097 int i;
1098 int old_max;
1099 int pack = 0;
1100
1101 clear_report_next = 0;
1102
1103 s1 = clean(s, 0);
1104
xf.li84027492024-04-09 00:17:51 -07001105 if (strlen(s1) + 1 > sizeof(fail_buffer))
lh9ed821d2023-04-07 01:36:19 -07001106 fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1107
1108 old_max = n_reports;
1109 for (i=0; i < n_reports; i++) {
1110 if ( strcmp(s1,report_string[i]) == 0 ) {
1111 free(report_string[i]);
1112 report_string[i] = NULL;
1113 pack++;
1114 n_reports--;
1115 if (verbose)
xf.li84027492024-04-09 00:17:51 -07001116 msgf("clear report (%v)", s1);
lh9ed821d2023-04-07 01:36:19 -07001117 }
1118 }
1119 free(s1);
1120 if (pack)
1121 pack_array(report_string,old_max);
1122
xf.li84027492024-04-09 00:17:51 -07001123 return 0;
lh9ed821d2023-04-07 01:36:19 -07001124 }
1125
1126 if (timeout_next) {
1127 timeout_next = 0;
xf.li84027492024-04-09 00:17:51 -07001128 s = clean(s, 0);
lh9ed821d2023-04-07 01:36:19 -07001129 timeout = atoi(s);
1130
1131 if (timeout <= 0)
1132 timeout = DEFAULT_CHAT_TIMEOUT;
1133
1134 if (verbose)
1135 msgf("timeout set to %d seconds", timeout);
xf.li84027492024-04-09 00:17:51 -07001136 free(s);
1137 return 0;
lh9ed821d2023-04-07 01:36:19 -07001138 }
1139
1140 /*
1141 * The syntax @filename means read the string to send from the
1142 * file `filename'.
1143 */
1144 if (s[0] == '@') {
1145 /* skip the @ and any following white-space */
1146 char *fn = s;
1147 while (*++fn == ' ' || *fn == '\t')
1148 ;
1149
1150 if (*fn != 0) {
1151 FILE *f;
1152 int n = 0;
1153
1154 /* open the file and read until STR_LEN-1 bytes or end-of-file */
1155 f = fopen(fn, "r");
1156 if (f == NULL)
1157 fatal(1, "%s -- open failed: %m", fn);
1158 while (n < STR_LEN - 1) {
1159 int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
1160 if (nr < 0)
1161 fatal(1, "%s -- read error", fn);
1162 if (nr == 0)
1163 break;
1164 n += nr;
1165 }
1166 fclose(f);
1167
1168 /* use the string we got as the string to send,
1169 but trim off the final newline if any. */
1170 if (n > 0 && file_data[n-1] == '\n')
1171 --n;
1172 file_data[n] = 0;
1173 s = file_data;
1174 }
1175 }
1176
1177 if (strcmp(s, "EOT") == 0)
1178 s = "^D\\c";
1179 else if (strcmp(s, "BREAK") == 0)
1180 s = "\\K\\c";
1181
1182 if (!put_string(s))
1183 fatal(1, "Failed");
xf.li84027492024-04-09 00:17:51 -07001184
1185 return 0;
lh9ed821d2023-04-07 01:36:19 -07001186}
1187
xf.li84027492024-04-09 00:17:51 -07001188int get_char(void)
lh9ed821d2023-04-07 01:36:19 -07001189{
1190 int status;
xf.li84027492024-04-09 00:17:51 -07001191 char c;
lh9ed821d2023-04-07 01:36:19 -07001192
1193 status = read(0, &c, 1);
xf.li84027492024-04-09 00:17:51 -07001194 checksigs();
1195
lh9ed821d2023-04-07 01:36:19 -07001196 switch (status) {
1197 case 1:
lh9ed821d2023-04-07 01:36:19 -07001198 return ((int)c & 0x7F);
1199
1200 default:
1201 msgf("warning: read() on stdin returned %d", status);
1202
1203 case -1:
lh9ed821d2023-04-07 01:36:19 -07001204 return (-1);
1205 }
1206}
1207
xf.li84027492024-04-09 00:17:51 -07001208int put_char(int c)
lh9ed821d2023-04-07 01:36:19 -07001209{
1210 int status;
1211 char ch = c;
1212
1213 usleep(10000); /* inter-character typing delay (?) */
xf.li84027492024-04-09 00:17:51 -07001214 checksigs();
lh9ed821d2023-04-07 01:36:19 -07001215
xf.li84027492024-04-09 00:17:51 -07001216 status = write(1, &ch, 1);
1217 checksigs();
lh9ed821d2023-04-07 01:36:19 -07001218
1219 switch (status) {
1220 case 1:
1221 return (0);
1222
1223 default:
1224 msgf("warning: write() on stdout returned %d", status);
1225
1226 case -1:
lh9ed821d2023-04-07 01:36:19 -07001227 return (-1);
1228 }
1229}
1230
xf.li84027492024-04-09 00:17:51 -07001231int write_char(int c)
lh9ed821d2023-04-07 01:36:19 -07001232{
1233 if (alarmed || put_char(c) < 0) {
1234 alarm(0);
1235 alarmed = 0;
1236
1237 if (verbose) {
1238 if (errno == EINTR || errno == EWOULDBLOCK)
1239 msgf(" -- write timed out");
1240 else
1241 msgf(" -- write failed: %m");
1242 }
1243 return (0);
1244 }
1245 return (1);
1246}
1247
xf.li84027492024-04-09 00:17:51 -07001248int put_string(register char *s)
lh9ed821d2023-04-07 01:36:19 -07001249{
xf.li84027492024-04-09 00:17:51 -07001250 char *s1;
lh9ed821d2023-04-07 01:36:19 -07001251 quiet = 0;
xf.li84027492024-04-09 00:17:51 -07001252
lh9ed821d2023-04-07 01:36:19 -07001253 s = clean(s, 1);
xf.li84027492024-04-09 00:17:51 -07001254 s1 = s;
lh9ed821d2023-04-07 01:36:19 -07001255
1256 if (verbose) {
1257 if (quiet)
1258 msgf("send (?????\?)");
1259 else
1260 msgf("send (%v)", s);
1261 }
1262
1263 alarm(timeout); alarmed = 0;
1264
1265 while (*s) {
1266 register char c = *s++;
1267
1268 if (c != '\\') {
xf.li84027492024-04-09 00:17:51 -07001269 if (!write_char (c)) {
1270 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001271 return 0;
xf.li84027492024-04-09 00:17:51 -07001272 }
lh9ed821d2023-04-07 01:36:19 -07001273 continue;
1274 }
1275
1276 c = *s++;
1277 switch (c) {
1278 case 'd':
1279 sleep(1);
1280 break;
1281
1282 case 'K':
1283 break_sequence();
1284 break;
1285
1286 case 'p':
1287 usleep(10000); /* 1/100th of a second (arg is microseconds) */
1288 break;
1289
1290 default:
xf.li84027492024-04-09 00:17:51 -07001291 if (!write_char (c)) {
1292 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001293 return 0;
xf.li84027492024-04-09 00:17:51 -07001294 }
lh9ed821d2023-04-07 01:36:19 -07001295 break;
1296 }
xf.li84027492024-04-09 00:17:51 -07001297 checksigs();
lh9ed821d2023-04-07 01:36:19 -07001298 }
1299
1300 alarm(0);
1301 alarmed = 0;
xf.li84027492024-04-09 00:17:51 -07001302 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001303 return (1);
1304}
1305
1306/*
1307 * Echo a character to stderr.
1308 * When called with -1, a '\n' character is generated when
1309 * the cursor is not at the beginning of a line.
1310 */
xf.li84027492024-04-09 00:17:51 -07001311int echo_stderr(int n)
lh9ed821d2023-04-07 01:36:19 -07001312{
1313 static int need_lf;
1314 char *s;
xf.li84027492024-04-09 00:17:51 -07001315 int len, ret = 0;
lh9ed821d2023-04-07 01:36:19 -07001316
1317 switch (n) {
1318 case '\r': /* ignore '\r' */
1319 break;
1320 case -1:
1321 if (need_lf == 0)
1322 break;
1323 /* fall through */
1324 case '\n':
xf.li84027492024-04-09 00:17:51 -07001325 ret = write(2, "\n", 1) != 1;
lh9ed821d2023-04-07 01:36:19 -07001326 need_lf = 0;
1327 break;
1328 default:
1329 s = character(n);
xf.li84027492024-04-09 00:17:51 -07001330 len = strlen(s);
1331 ret = write(2, s, len) != len;
lh9ed821d2023-04-07 01:36:19 -07001332 need_lf = 1;
1333 break;
1334 }
xf.li84027492024-04-09 00:17:51 -07001335 checksigs();
1336 return ret;
lh9ed821d2023-04-07 01:36:19 -07001337}
1338
1339/*
1340 * 'Wait for' this string to appear on this file descriptor.
1341 */
xf.li84027492024-04-09 00:17:51 -07001342int get_string(register char *string)
lh9ed821d2023-04-07 01:36:19 -07001343{
1344 char temp[STR_LEN];
1345 int c, printed = 0, len, minlen;
1346 register char *s = temp, *end = s + STR_LEN;
xf.li84027492024-04-09 00:17:51 -07001347 char *s1, *logged = temp;
lh9ed821d2023-04-07 01:36:19 -07001348
1349 fail_reason = (char *)0;
xf.li84027492024-04-09 00:17:51 -07001350 string = s1 = clean(string, 0);
lh9ed821d2023-04-07 01:36:19 -07001351 len = strlen(string);
1352 minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
xf.li84027492024-04-09 00:17:51 -07001353
lh9ed821d2023-04-07 01:36:19 -07001354 if (verbose)
1355 msgf("expect (%v)", string);
1356
1357 if (len > STR_LEN) {
1358 msgf("expect string is too long");
1359 exit_code = 1;
xf.li84027492024-04-09 00:17:51 -07001360 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001361 return 0;
1362 }
1363
1364 if (len == 0) {
1365 if (verbose)
1366 msgf("got it");
xf.li84027492024-04-09 00:17:51 -07001367 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001368 return (1);
1369 }
xf.li84027492024-04-09 00:17:51 -07001370
lh9ed821d2023-04-07 01:36:19 -07001371 alarm(timeout);
1372 alarmed = 0;
1373
1374 while ( ! alarmed && (c = get_char()) >= 0) {
1375 int n, abort_len, report_len;
1376
xf.li84027492024-04-09 00:17:51 -07001377 if (echo) {
1378 if (echo_stderr(c) != 0) {
1379 fatal(2, "Could not write to stderr, %m");
1380 }
1381 }
lh9ed821d2023-04-07 01:36:19 -07001382 if (verbose && c == '\n') {
1383 if (s == logged)
1384 msgf(""); /* blank line */
1385 else
1386 msgf("%0.*v", s - logged, logged);
1387 logged = s + 1;
1388 }
1389
1390 *s++ = c;
1391
1392 if (verbose && s >= logged + 80) {
1393 msgf("%0.*v", s - logged, logged);
1394 logged = s;
1395 }
1396
1397 if (Verbose) {
1398 if (c == '\n')
1399 fputc( '\n', stderr );
1400 else if (c != '\r')
1401 fprintf( stderr, "%s", character(c) );
1402 }
1403
1404 if (!report_gathering) {
1405 for (n = 0; n < n_reports; ++n) {
1406 if ((report_string[n] != (char*) NULL) &&
1407 s - temp >= (report_len = strlen(report_string[n])) &&
1408 strncmp(s - report_len, report_string[n], report_len) == 0) {
1409 time_t time_now = time ((time_t*) NULL);
1410 struct tm* tm_now = localtime (&time_now);
1411
1412 strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1413 strcat (report_buffer, report_string[n]);
1414
xf.li84027492024-04-09 00:17:51 -07001415 free(report_string[n]);
lh9ed821d2023-04-07 01:36:19 -07001416 report_string[n] = (char *) NULL;
1417 report_gathering = 1;
1418 break;
1419 }
1420 }
1421 }
1422 else {
1423 if (!iscntrl (c)) {
1424 int rep_len = strlen (report_buffer);
xf.li84027492024-04-09 00:17:51 -07001425 if ((rep_len + 1) < sizeof(report_buffer)) {
1426 report_buffer[rep_len] = c;
1427 report_buffer[rep_len + 1] = '\0';
1428 }
lh9ed821d2023-04-07 01:36:19 -07001429 }
1430 else {
1431 report_gathering = 0;
1432 fprintf (report_fp, "chat: %s\n", report_buffer);
1433 }
1434 }
1435
1436 if (s - temp >= len &&
1437 c == string[len - 1] &&
1438 strncmp(s - len, string, len) == 0) {
1439 if (verbose) {
1440 if (s > logged)
1441 msgf("%0.*v", s - logged, logged);
1442 msgf(" -- got it\n");
1443 }
1444
1445 alarm(0);
1446 alarmed = 0;
xf.li84027492024-04-09 00:17:51 -07001447 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001448 return (1);
1449 }
1450
1451 for (n = 0; n < n_aborts; ++n) {
1452 if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1453 strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
1454 if (verbose) {
1455 if (s > logged)
1456 msgf("%0.*v", s - logged, logged);
1457 msgf(" -- failed");
1458 }
1459
1460 alarm(0);
1461 alarmed = 0;
1462 exit_code = n + 4;
1463 strcpy(fail_reason = fail_buffer, abort_string[n]);
xf.li84027492024-04-09 00:17:51 -07001464 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001465 return (0);
1466 }
1467 }
1468
1469 if (s >= end) {
1470 if (logged < s - minlen) {
1471 if (verbose)
1472 msgf("%0.*v", s - logged, logged);
1473 logged = s;
1474 }
1475 s -= minlen;
1476 memmove(temp, s, minlen);
1477 logged = temp + (logged - s);
1478 s = temp + minlen;
1479 }
1480
1481 if (alarmed && verbose)
1482 msgf("warning: alarm synchronization problem");
1483 }
1484
1485 alarm(0);
1486
1487 if (verbose && printed) {
1488 if (alarmed)
1489 msgf(" -- read timed out");
1490 else
1491 msgf(" -- read failed: %m");
1492 }
1493
1494 exit_code = 3;
1495 alarmed = 0;
xf.li84027492024-04-09 00:17:51 -07001496 free(s1);
lh9ed821d2023-04-07 01:36:19 -07001497 return (0);
1498}
1499
1500/*
1501 * Gross kludge to handle Solaris versions >= 2.6 having usleep.
1502 */
1503#ifdef SOL2
1504#include <sys/param.h>
1505#if MAXUID > 65536 /* then this is Solaris 2.6 or later */
1506#undef NO_USLEEP
1507#endif
1508#endif /* SOL2 */
1509
1510#ifdef NO_USLEEP
1511#include <sys/types.h>
1512#include <sys/time.h>
1513
1514/*
1515 usleep -- support routine for 4.2BSD system call emulations
1516 last edit: 29-Oct-1984 D A Gwyn
1517 */
1518
1519extern int select();
1520
xf.li84027492024-04-09 00:17:51 -07001521/* returns 0 if ok, else -1 */
1522int usleep(long usec) /* delay in microseconds */
lh9ed821d2023-04-07 01:36:19 -07001523{
1524 static struct { /* `timeval' */
1525 long tv_sec; /* seconds */
1526 long tv_usec; /* microsecs */
1527 } delay; /* _select() timeout */
1528
1529 delay.tv_sec = usec / 1000000L;
1530 delay.tv_usec = usec % 1000000L;
1531
1532 return select(0, (long *)0, (long *)0, (long *)0, &delay);
1533}
1534#endif
1535
xf.li84027492024-04-09 00:17:51 -07001536void pack_array (
1537 char **array, /* The address of the array of string pointers */
1538 int end) /* The index of the next free entry before CLR_ */
lh9ed821d2023-04-07 01:36:19 -07001539{
1540 int i, j;
1541
1542 for (i = 0; i < end; i++) {
1543 if (array[i] == NULL) {
1544 for (j = i+1; j < end; ++j)
1545 if (array[j] != NULL)
1546 array[i++] = array[j];
1547 for (; i < end; ++i)
1548 array[i] = NULL;
1549 break;
1550 }
1551 }
1552}
1553
1554/*
1555 * vfmtmsg - format a message into a buffer. Like vsprintf except we
1556 * also specify the length of the output buffer, and we handle the
1557 * %m (error message) format.
1558 * Doesn't do floating-point formats.
1559 * Returns the number of chars put into buf.
1560 */
1561#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
1562
1563int
xf.li84027492024-04-09 00:17:51 -07001564vfmtmsg(char *buf, int buflen, const char *fmt, va_list args)
lh9ed821d2023-04-07 01:36:19 -07001565{
1566 int c, i, n;
1567 int width, prec, fillch;
1568 int base, len, neg, quoted;
1569 unsigned long val = 0;
1570 char *str, *buf0;
1571 const char *f;
1572 unsigned char *p;
1573 char num[32];
1574 static char hexchars[] = "0123456789abcdef";
1575
1576 buf0 = buf;
1577 --buflen;
1578 while (buflen > 0) {
1579 for (f = fmt; *f != '%' && *f != 0; ++f)
1580 ;
1581 if (f > fmt) {
1582 len = f - fmt;
1583 if (len > buflen)
1584 len = buflen;
1585 memcpy(buf, fmt, len);
1586 buf += len;
1587 buflen -= len;
1588 fmt = f;
1589 }
1590 if (*fmt == 0)
1591 break;
1592 c = *++fmt;
1593 width = prec = 0;
1594 fillch = ' ';
1595 if (c == '0') {
1596 fillch = '0';
1597 c = *++fmt;
1598 }
1599 if (c == '*') {
1600 width = va_arg(args, int);
1601 c = *++fmt;
1602 } else {
1603 while (isdigit(c)) {
1604 width = width * 10 + c - '0';
1605 c = *++fmt;
1606 }
1607 }
1608 if (c == '.') {
1609 c = *++fmt;
1610 if (c == '*') {
1611 prec = va_arg(args, int);
1612 c = *++fmt;
1613 } else {
1614 while (isdigit(c)) {
1615 prec = prec * 10 + c - '0';
1616 c = *++fmt;
1617 }
1618 }
1619 }
1620 str = 0;
1621 base = 0;
1622 neg = 0;
1623 ++fmt;
1624 switch (c) {
1625 case 'd':
1626 i = va_arg(args, int);
1627 if (i < 0) {
1628 neg = 1;
1629 val = -i;
1630 } else
1631 val = i;
1632 base = 10;
1633 break;
1634 case 'o':
1635 val = va_arg(args, unsigned int);
1636 base = 8;
1637 break;
1638 case 'x':
1639 val = va_arg(args, unsigned int);
1640 base = 16;
1641 break;
1642 case 'p':
1643 val = (unsigned long) va_arg(args, void *);
1644 base = 16;
1645 neg = 2;
1646 break;
1647 case 's':
1648 str = va_arg(args, char *);
1649 break;
1650 case 'c':
1651 num[0] = va_arg(args, int);
1652 num[1] = 0;
1653 str = num;
1654 break;
1655 case 'm':
1656 str = strerror(errno);
1657 break;
1658 case 'v': /* "visible" string */
1659 case 'q': /* quoted string */
1660 quoted = c == 'q';
1661 p = va_arg(args, unsigned char *);
1662 if (fillch == '0' && prec > 0) {
1663 n = prec;
1664 } else {
1665 n = strlen((char *)p);
1666 if (prec > 0 && prec < n)
1667 n = prec;
1668 }
1669 while (n > 0 && buflen > 0) {
1670 c = *p++;
1671 --n;
1672 if (!quoted && c >= 0x80) {
1673 OUTCHAR('M');
1674 OUTCHAR('-');
1675 c -= 0x80;
1676 }
1677 if (quoted && (c == '"' || c == '\\'))
1678 OUTCHAR('\\');
1679 if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
1680 if (quoted) {
1681 OUTCHAR('\\');
1682 switch (c) {
1683 case '\t': OUTCHAR('t'); break;
1684 case '\n': OUTCHAR('n'); break;
1685 case '\b': OUTCHAR('b'); break;
1686 case '\f': OUTCHAR('f'); break;
1687 default:
1688 OUTCHAR('x');
1689 OUTCHAR(hexchars[c >> 4]);
1690 OUTCHAR(hexchars[c & 0xf]);
1691 }
1692 } else {
1693 if (c == '\t')
1694 OUTCHAR(c);
1695 else {
1696 OUTCHAR('^');
1697 OUTCHAR(c ^ 0x40);
1698 }
1699 }
1700 } else
1701 OUTCHAR(c);
1702 }
1703 continue;
1704 default:
1705 *buf++ = '%';
1706 if (c != '%')
1707 --fmt; /* so %z outputs %z etc. */
1708 --buflen;
1709 continue;
1710 }
1711 if (base != 0) {
1712 str = num + sizeof(num);
1713 *--str = 0;
1714 while (str > num + neg) {
1715 *--str = hexchars[val % base];
1716 val = val / base;
1717 if (--prec <= 0 && val == 0)
1718 break;
1719 }
1720 switch (neg) {
1721 case 1:
1722 *--str = '-';
1723 break;
1724 case 2:
1725 *--str = 'x';
1726 *--str = '0';
1727 break;
1728 }
1729 len = num + sizeof(num) - 1 - str;
1730 } else {
1731 len = strlen(str);
1732 if (prec > 0 && len > prec)
1733 len = prec;
1734 }
1735 if (width > 0) {
1736 if (width > buflen)
1737 width = buflen;
1738 if ((n = width - len) > 0) {
1739 buflen -= n;
1740 for (; n > 0; --n)
1741 *buf++ = fillch;
1742 }
1743 }
1744 if (len > buflen)
1745 len = buflen;
1746 memcpy(buf, str, len);
1747 buf += len;
1748 buflen -= len;
1749 }
1750 *buf = 0;
1751 return buf - buf0;
1752}