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