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