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