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