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