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