]> git.ozlabs.org Git - ppp.git/blob - chat/chat.c
put linux ioctls in sys-linux.c
[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         strcpy(s, s1);
829         abort_string[n_aborts++] = s;
830
831         if (verbose)
832             {
833             register char *s1 = s;
834
835             logf("abort on (");
836
837             for (s1 = s; *s1; ++s1)
838                 logf(character(*s1));
839
840             logf(")\n");
841             }
842         }
843     else
844         if (timeout_next)
845             {
846             timeout_next = 0;
847             timeout = atoi(s);
848
849             if (timeout <= 0)
850                 timeout = DEFAULT_CHAT_TIMEOUT;
851
852             if (verbose)
853                 {
854                 syslog(LOG_INFO, "timeout set to %d seconds", timeout);
855                 }
856             }
857         else
858             {
859             if (strcmp(s, "EOT") == 0)
860                 s = "^D\\c";
861             else
862                 if (strcmp(s, "BREAK") == 0)
863                     s = "\\K\\c";
864             if ( ! put_string(s))
865                 {
866                 syslog(LOG_INFO, "Failed");
867                 unlock();
868                 terminate(1);
869                 }
870             }
871     }
872
873 int get_char()
874     {
875     int status;
876     char c;
877
878     status = read(0, &c, 1);
879
880     switch (status)
881         {
882         case 1:
883             return ((int)c & 0x7F);
884
885         default:
886             syslog(LOG_WARNING, "warning: read() on stdin returned %d",
887                    status);
888
889         case -1:
890             if ((status = fcntl(0, F_GETFL, 0)) == -1)
891                 sysfatal("Can't get file mode flags on stdin");
892             else
893                 if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
894                     sysfatal("Can't set file mode flags on stdin");
895
896             return (-1);
897         }
898     }
899
900 int put_char(c)
901 char c;
902     {
903     int status;
904
905     delay();
906
907     status = write(1, &c, 1);
908
909     switch (status)
910         {
911         case 1:
912             return (0);
913
914         default:
915             syslog(LOG_WARNING, "warning: write() on stdout returned %d",
916                    status);
917
918         case -1:
919             if ((status = fcntl(0, F_GETFL, 0)) == -1)
920                 sysfatal("Can't get file mode flags on stdin");
921             else
922                 if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
923                     sysfatal("Can't set file mode flags on stdin");
924
925             return (-1);
926         }
927     }
928
929 int write_char (c)
930 int c;
931     {
932     if (alarmed || put_char(c) < 0)
933         {
934         extern int errno;
935
936         alarm(0); alarmed = 0;
937
938         if (verbose)
939             {
940             if (errno == EINTR || errno == EWOULDBLOCK)
941                 syslog(LOG_INFO, " -- write timed out");
942             else
943                 syslog(LOG_INFO, " -- write failed: %m");
944             }
945         return (0);
946         }
947     return (1);
948     }
949
950 int put_string (s)
951 register char *s;
952     {
953     s = clean(s, 1);
954
955     if (verbose)
956         {
957         logf("send (");
958
959         if (quiet)
960             logf("??????");
961         else
962             {
963             register char *s1 = s;
964
965             for (s1 = s; *s1; ++s1)
966                 logf(character(*s1));
967             }
968
969         logf(")\n");
970         }
971
972     alarm(timeout); alarmed = 0;
973
974     while (*s)
975         {
976         register char c = *s++;
977
978         if (c != '\\')
979             {
980             if (!write_char (c))
981                 return 0;
982             continue;
983             }
984
985         c = *s++;
986         switch (c)
987             {
988         case 'd':
989             sleep(1);
990             break;
991
992         case 'K':
993             break_sequence();
994             break;
995
996         case 'p':
997             usleep(10000); /* 1/100th of a second. */
998             break;
999
1000         default:
1001             if (!write_char (c))
1002                 return 0;
1003             break;
1004             }
1005         }
1006
1007     alarm(0);
1008     alarmed = 0;
1009     return (1);
1010     }
1011
1012 /*
1013  *      'Wait for' this string to appear on this file descriptor.
1014  */
1015 int get_string(string)
1016 register char *string;
1017     {
1018     char temp[STR_LEN];
1019     int c, printed = 0, len, minlen;
1020     register char *s = temp, *end = s + STR_LEN;
1021
1022     fail_reason = (char *)0;
1023     string = clean(string, 0);
1024     len = strlen(string);
1025     minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1026
1027     if (verbose)
1028         {
1029         register char *s1;
1030
1031         logf("expect (");
1032
1033         for (s1 = string; *s1; ++s1)
1034             logf(character(*s1));
1035
1036         logf(")\n");
1037         }
1038
1039     if (len > STR_LEN)
1040         {
1041         syslog(LOG_INFO, "expect string is too long");
1042         return 0;
1043         }
1044
1045     if (len == 0)
1046         {
1047         if (verbose)
1048             {
1049             syslog(LOG_INFO, "got it");
1050             }
1051
1052         return (1);
1053         }
1054
1055     alarm(timeout); alarmed = 0;
1056
1057     while ( ! alarmed && (c = get_char()) >= 0)
1058         {
1059         int n, abort_len;
1060
1061         if (verbose)
1062             {
1063             if (c == '\n')
1064                 logf("\n");
1065             else
1066                 logf(character(c));
1067             }
1068
1069         *s++ = c;
1070
1071         if (s - temp >= len &&
1072             c == string[len - 1] &&
1073             strncmp(s - len, string, len) == 0)
1074             {
1075             if (verbose)
1076                 {
1077                 logf(" -- got it\n");
1078                 }
1079
1080             alarm(0); alarmed = 0;
1081             return (1);
1082             }
1083
1084         for (n = 0; n < n_aborts; ++n)
1085             if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1086                 strncmp(s - abort_len, abort_string[n], abort_len) == 0)
1087                 {
1088                 if (verbose)
1089                     {
1090                     logf(" -- failed\n");
1091                     }
1092
1093                 alarm(0); alarmed = 0;
1094                 strcpy(fail_reason = fail_buffer, abort_string[n]);
1095                 return (0);
1096                 }
1097
1098         if (s >= end)
1099             {
1100             strncpy(temp, s - minlen, minlen);
1101             s = temp + minlen;
1102             }
1103
1104         if (alarmed && verbose)
1105             syslog(LOG_WARNING, "warning: alarm synchronization problem");
1106         }
1107
1108     alarm(0);
1109     
1110     if (verbose && printed)
1111         {
1112         if (alarmed)
1113             logf(" -- read timed out\n");
1114         else
1115             {
1116             logflush();
1117             syslog(LOG_INFO, " -- read failed: %m");
1118             }
1119         }
1120
1121     alarmed = 0;
1122     return (0);
1123     }
1124
1125 #ifdef ultrix
1126 #undef NO_USLEEP
1127 #include <sys/types.h>
1128 #include <sys/time.h>
1129
1130 /*
1131   usleep -- support routine for 4.2BSD system call emulations
1132   last edit:  29-Oct-1984     D A Gwyn
1133   */
1134
1135 extern int        select();
1136
1137 int
1138 usleep( usec )                            /* returns 0 if ok, else -1 */
1139     long                usec;           /* delay in microseconds */
1140 {
1141     static struct                       /* `timeval' */
1142         {
1143             long        tv_sec;         /* seconds */
1144             long        tv_usec;        /* microsecs */
1145         }   delay;          /* _select() timeout */
1146
1147     delay.tv_sec = usec / 1000000L;
1148     delay.tv_usec = usec % 1000000L;
1149
1150     return select( 0, (long *)0, (long *)0, (long *)0, &delay );
1151 }
1152 #endif
1153
1154 /*
1155  *      Delay an amount appropriate for between typed characters.
1156  */
1157 void delay()
1158     {
1159 # ifdef NO_USLEEP
1160     register int i;
1161
1162     for (i = 0; i < 30000; ++i)         /* ... did we just say appropriate? */
1163         ;
1164 # else /* NO_USLEEP */
1165     usleep(100);
1166 # endif /* NO_USLEEP */
1167     }