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