Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / pppd / chat / chat.c
blob48913df608bff86df255d4f62b8e570623259f03
1 /* $NetBSD: chat.c,v 1.7 2006/03/23 19:25:09 he Exp $ */
3 /*
4 * Chat -- a program for automatic session establishment (i.e. dial
5 * the phone and log in).
7 * Standard termination codes:
8 * 0 - successful completion of the script
9 * 1 - invalid argument, expect string too large, etc.
10 * 2 - error on an I/O operation or fatal error condition.
11 * 3 - timeout waiting for a simple string.
12 * 4 - the first string declared as "ABORT"
13 * 5 - the second string declared as "ABORT"
14 * 6 - ... and so on for successive ABORT strings.
16 * This software is in the public domain.
18 * -----------------
19 * 22-May-99 added environment substitutuion, enabled with -E switch.
20 * Andreas Arens <andras@cityweb.de>.
22 * 12-May-99 added a feature to read data to be sent from a file,
23 * if the send string starts with @. Idea from gpk <gpk@onramp.net>.
25 * added -T and -U option and \T and \U substitution to pass a phone
26 * number into chat script. Two are needed for some ISDN TA applications.
27 * Keith Dart <kdart@cisco.com>
30 * Added SAY keyword to send output to stderr.
31 * This allows to turn ECHO OFF and to output specific, user selected,
32 * text to give progress messages. This best works when stderr
33 * exists (i.e.: pppd in nodetach mode).
35 * Added HANGUP directives to allow for us to be called
36 * back. When HANGUP is set to NO, chat will not hangup at HUP signal.
37 * We rely on timeouts in that case.
39 * Added CLR_ABORT to clear previously set ABORT string. This has been
40 * dictated by the HANGUP above as "NO CARRIER" (for example) must be
41 * an ABORT condition until we know the other host is going to close
42 * the connection for call back. As soon as we have completed the
43 * first stage of the call back sequence, "NO CARRIER" is a valid, non
44 * fatal string. As soon as we got called back (probably get "CONNECT"),
45 * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
46 * Note that CLR_ABORT packs the abort_strings[] array so that we do not
47 * have unused entries not being reclaimed.
49 * In the same vein as above, added CLR_REPORT keyword.
51 * Allow for comments. Line starting with '#' are comments and are
52 * ignored. If a '#' is to be expected as the first character, the
53 * expect string must be quoted.
56 * Francis Demierre <Francis@SwissMail.Com>
57 * Thu May 15 17:15:40 MET DST 1997
60 * Added -r "report file" switch & REPORT keyword.
61 * Robert Geer <bgeer@xmission.com>
63 * Added -s "use stderr" and -S "don't use syslog" switches.
64 * June 18, 1997
65 * Karl O. Pinc <kop@meme.com>
68 * Added -e "echo" switch & ECHO keyword
69 * Dick Streefland <dicks@tasking.nl>
72 * Considerable updates and modifications by
73 * Al Longyear <longyear@pobox.com>
74 * Paul Mackerras <paulus@cs.anu.edu.au>
77 * The original author is:
79 * Karl Fox <karl@MorningStar.Com>
80 * Morning Star Technologies, Inc.
81 * 1760 Zollinger Road
82 * Columbus, OH 43221
83 * (614)451-1883
87 #ifndef __STDC__
88 #define const
89 #endif
91 #include <sys/cdefs.h>
92 #ifndef lint
93 #if 0
94 static const char rcsid[] = "Id: chat.c,v 1.30 2004/01/17 05:47:55 carlsonj Exp";
95 #else
96 __RCSID("$NetBSD: chat.c,v 1.7 2006/03/23 19:25:09 he Exp $");
97 #endif
98 #endif
100 #include <stdio.h>
101 #include <ctype.h>
102 #include <time.h>
103 #include <fcntl.h>
104 #include <signal.h>
105 #include <errno.h>
106 #include <string.h>
107 #include <stdlib.h>
108 #include <unistd.h>
109 #include <sys/types.h>
110 #include <sys/stat.h>
111 #include <syslog.h>
113 #ifndef TERMIO
114 #undef TERMIOS
115 #define TERMIOS
116 #endif
118 #ifdef TERMIO
119 #include <termio.h>
120 #endif
121 #ifdef TERMIOS
122 #include <termios.h>
123 #endif
125 #define STR_LEN 1024
127 #ifndef SIGTYPE
128 #define SIGTYPE void
129 #endif
131 #undef __P
132 #undef __V
134 #ifdef __STDC__
135 #include <stdarg.h>
136 #define __V(x) x
137 #define __P(x) x
138 #else
139 #include <varargs.h>
140 #define __V(x) (va_alist) va_dcl
141 #define __P(x) ()
142 #define const
143 #endif
145 #ifndef O_NONBLOCK
146 #define O_NONBLOCK O_NDELAY
147 #endif
149 #ifdef SUNOS
150 extern int sys_nerr;
151 extern char *sys_errlist[];
152 #define memmove(to, from, n) bcopy(from, to, n)
153 #define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
154 "unknown error")
155 #endif
157 char *program_name;
159 #define BUFFER_SIZE 256
160 #define MAX_ABORTS 50
161 #define MAX_REPORTS 50
162 #define DEFAULT_CHAT_TIMEOUT 45
164 int echo = 0;
165 int verbose = 0;
166 int to_log = 1;
167 int to_stderr = 0;
168 int Verbose = 0;
169 int quiet = 0;
170 int report = 0;
171 int use_env = 0;
172 int exit_code = 0;
173 FILE* report_fp = (FILE *) 0;
174 char *report_file = (char *) 0;
175 char *chat_file = (char *) 0;
176 char *phone_num = (char *) 0;
177 char *phone_num2 = (char *) 0;
178 int timeout = DEFAULT_CHAT_TIMEOUT;
180 int have_tty_parameters = 0;
182 #ifdef TERMIO
183 #define term_parms struct termio
184 #define get_term_param(param) ioctl(0, TCGETA, param)
185 #define set_term_param(param) ioctl(0, TCSETA, param)
186 struct termio saved_tty_parameters;
187 #endif
189 #ifdef TERMIOS
190 #define term_parms struct termios
191 #define get_term_param(param) tcgetattr(0, param)
192 #define set_term_param(param) tcsetattr(0, TCSANOW, param)
193 struct termios saved_tty_parameters;
194 #endif
196 char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
197 fail_buffer[BUFFER_SIZE];
198 int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
199 int clear_abort_next = 0;
201 char *report_string[MAX_REPORTS] ;
202 char report_buffer[BUFFER_SIZE] ;
203 int n_reports = 0, report_next = 0, report_gathering = 0 ;
204 int clear_report_next = 0;
206 int say_next = 0, hup_next = 0;
208 void *dup_mem __P((void *b, size_t c));
209 void *copy_of __P((char *s));
210 char *grow __P((char *s, char **p, size_t len));
211 void usage __P((void));
212 void msgf __P((const char *fmt, ...));
213 void fatal __P((int code, const char *fmt, ...));
214 SIGTYPE sigalrm __P((int signo));
215 SIGTYPE sigint __P((int signo));
216 SIGTYPE sigterm __P((int signo));
217 SIGTYPE sighup __P((int signo));
218 void unalarm __P((void));
219 void init __P((void));
220 void set_tty_parameters __P((void));
221 void echo_stderr __P((int));
222 void break_sequence __P((void));
223 void terminate __P((int status));
224 void do_file __P((char *chat_file));
225 int get_string __P((register char *string));
226 int put_string __P((register char *s));
227 int write_char __P((int c));
228 int put_char __P((int c));
229 int get_char __P((void));
230 void chat_send __P((register char *s));
231 char *character __P((int c));
232 void chat_expect __P((register char *s));
233 char *clean __P((register char *s, int sending));
234 void break_sequence __P((void));
235 void terminate __P((int status));
236 void pack_array __P((char **array, int end));
237 char *expect_strtok __P((char *, char *));
238 int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */
240 int main __P((int, char *[]));
242 void *dup_mem(b, c)
243 void *b;
244 size_t c;
246 void *ans = malloc (c);
247 if (!ans)
248 fatal(2, "memory error!");
250 memcpy (ans, b, c);
251 return ans;
254 void *copy_of (s)
255 char *s;
257 return dup_mem (s, strlen (s) + 1);
260 /* grow a char buffer and keep a pointer offset */
261 char *grow(s, p, len)
262 char *s;
263 char **p;
264 size_t len;
266 size_t l = *p - s; /* save p as distance into s */
268 s = realloc(s, len);
269 if (!s)
270 fatal(2, "memory error!");
271 *p = s + l; /* restore p */
272 return s;
276 * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
277 * [ -r report-file ] \
278 * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
280 * Perform a UUCP-dialer-like chat script on stdin and stdout.
283 main(argc, argv)
284 int argc;
285 char **argv;
287 int option;
288 int i;
290 program_name = *argv;
291 tzset();
293 while ((option = getopt(argc, argv, ":eEvVf:t:r:sST:U:")) != -1) {
294 switch (option) {
295 case 'e':
296 ++echo;
297 break;
299 case 'E':
300 ++use_env;
301 break;
303 case 'v':
304 ++verbose;
305 break;
307 case 'V':
308 ++Verbose;
309 break;
311 case 's':
312 ++to_stderr;
313 break;
315 case 'S':
316 to_log = 0;
317 break;
319 case 'f':
320 if (optarg != NULL)
321 chat_file = copy_of(optarg);
322 else
323 usage();
324 break;
326 case 't':
327 if (optarg != NULL)
328 timeout = atoi(optarg);
329 else
330 usage();
331 break;
333 case 'r':
334 if (optarg) {
335 if (report_fp != NULL)
336 fclose (report_fp);
337 report_file = copy_of (optarg);
338 report_fp = fopen (report_file, "a");
339 if (report_fp != NULL) {
340 if (verbose)
341 fprintf (report_fp, "Opening \"%s\"...\n",
342 report_file);
343 report = 1;
346 break;
348 case 'T':
349 if (optarg != NULL)
350 phone_num = copy_of(optarg);
351 else
352 usage();
353 break;
355 case 'U':
356 if (optarg != NULL)
357 phone_num2 = copy_of(optarg);
358 else
359 usage();
360 break;
362 default:
363 usage();
364 break;
367 argc -= optind;
368 argv += optind;
370 * Default the report file to the stderr location
372 if (report_fp == NULL)
373 report_fp = stderr;
375 if (to_log) {
376 #ifdef ultrix
377 openlog("chat", LOG_PID);
378 #else
379 openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
381 if (verbose)
382 setlogmask(LOG_UPTO(LOG_INFO));
383 else
384 setlogmask(LOG_UPTO(LOG_WARNING));
385 #endif
388 init();
390 if (chat_file != NULL) {
391 if (argc)
392 usage();
393 else
394 do_file (chat_file);
395 } else {
396 for (i = 0; i < argc; i++) {
397 chat_expect(argv[i]);
398 if (++i < argc)
399 chat_send(argv[i]);
403 terminate(0);
404 return 0;
408 * Process a chat script when read from a file.
411 void do_file (chat_file)
412 char *chat_file;
414 int linect, sendflg;
415 char *sp, *arg, quote;
416 char buf [STR_LEN];
417 FILE *cfp;
419 cfp = fopen (chat_file, "r");
420 if (cfp == NULL)
421 fatal(1, "%s -- open failed: %m", chat_file);
423 linect = 0;
424 sendflg = 0;
426 while (fgets(buf, STR_LEN, cfp) != NULL) {
427 sp = strchr (buf, '\n');
428 if (sp)
429 *sp = '\0';
431 linect++;
432 sp = buf;
434 /* lines starting with '#' are comments. If a real '#'
435 is to be expected, it should be quoted .... */
436 if ( *sp == '#' )
437 continue;
439 while (*sp != '\0') {
440 if (*sp == ' ' || *sp == '\t') {
441 ++sp;
442 continue;
445 if (*sp == '"' || *sp == '\'') {
446 quote = *sp++;
447 arg = sp;
448 while (*sp != quote) {
449 if (*sp == '\0')
450 fatal(1, "unterminated quote (line %d)", linect);
452 if (*sp++ == '\\') {
453 if (*sp != '\0')
454 ++sp;
458 else {
459 arg = sp;
460 while (*sp != '\0' && *sp != ' ' && *sp != '\t')
461 ++sp;
464 if (*sp != '\0')
465 *sp++ = '\0';
467 if (sendflg)
468 chat_send (arg);
469 else
470 chat_expect (arg);
471 sendflg = !sendflg;
474 fclose (cfp);
478 * We got an error parsing the command line.
480 void usage()
482 fprintf(stderr, "\
483 Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file]\n\
484 [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
485 exit(1);
488 char line[1024];
491 * Send a message to syslog and/or stderr.
493 void msgf __V((const char *fmt, ...))
495 va_list args;
497 #ifdef __STDC__
498 va_start(args, fmt);
499 #else
500 char *fmt;
501 va_start(args);
502 fmt = va_arg(args, char *);
503 #endif
505 vfmtmsg(line, sizeof(line), fmt, args);
506 va_end(args);
507 if (to_log)
508 syslog(LOG_INFO, "%s", line);
509 if (to_stderr)
510 fprintf(stderr, "%s\n", line);
514 * Print an error message and terminate.
517 void fatal __V((int code, const char *fmt, ...))
519 va_list args;
521 #ifdef __STDC__
522 va_start(args, fmt);
523 #else
524 int code;
525 char *fmt;
526 va_start(args);
527 code = va_arg(args, int);
528 fmt = va_arg(args, char *);
529 #endif
531 vfmtmsg(line, sizeof(line), fmt, args);
532 va_end(args);
533 if (to_log)
534 syslog(LOG_ERR, "%s", line);
535 if (to_stderr)
536 fprintf(stderr, "%s\n", line);
537 terminate(code);
540 int alarmed = 0;
542 SIGTYPE sigalrm(signo)
543 int signo;
545 int flags;
547 alarm(1);
548 alarmed = 1; /* Reset alarm to avoid race window */
549 signal(SIGALRM, sigalrm); /* that can cause hanging in read() */
551 if ((flags = fcntl(0, F_GETFL, 0)) == -1)
552 fatal(2, "Can't get file mode flags on stdin: %m");
554 if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
555 fatal(2, "Can't set file mode flags on stdin: %m");
557 if (verbose)
558 msgf("alarm");
561 void unalarm()
563 int flags;
565 if ((flags = fcntl(0, F_GETFL, 0)) == -1)
566 fatal(2, "Can't get file mode flags on stdin: %m");
568 if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
569 fatal(2, "Can't set file mode flags on stdin: %m");
572 SIGTYPE sigint(signo)
573 int signo;
575 fatal(2, "SIGINT");
578 SIGTYPE sigterm(signo)
579 int signo;
581 fatal(2, "SIGTERM");
584 SIGTYPE sighup(signo)
585 int signo;
587 fatal(2, "SIGHUP");
590 void init()
592 signal(SIGINT, sigint);
593 signal(SIGTERM, sigterm);
594 signal(SIGHUP, sighup);
596 set_tty_parameters();
597 signal(SIGALRM, sigalrm);
598 alarm(0);
599 alarmed = 0;
602 void set_tty_parameters()
604 #if defined(get_term_param)
605 term_parms t;
607 if (get_term_param (&t) < 0)
608 fatal(2, "Can't get terminal parameters: %m");
610 saved_tty_parameters = t;
611 have_tty_parameters = 1;
613 t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
614 t.c_oflag |= OPOST | ONLCR;
615 t.c_lflag = 0;
616 t.c_cc[VERASE] =
617 t.c_cc[VKILL] = 0;
618 t.c_cc[VMIN] = 1;
619 t.c_cc[VTIME] = 0;
621 if (set_term_param (&t) < 0)
622 fatal(2, "Can't set terminal parameters: %m");
623 #endif
626 void break_sequence()
628 #ifdef TERMIOS
629 tcsendbreak (0, 0);
630 #endif
633 void terminate(status)
634 int status;
636 static int terminating = 0;
638 if (terminating)
639 exit(status);
640 terminating = 1;
641 echo_stderr(-1);
643 * Allow the last of the report string to be gathered before we terminate.
645 if (report_gathering) {
646 int c, rep_len;
648 rep_len = strlen(report_buffer);
649 while (rep_len < sizeof(report_buffer) - 1) {
650 alarm(1);
651 c = get_char();
652 alarm(0);
653 if (c < 0 || iscntrl(c))
654 break;
655 report_buffer[rep_len] = c;
656 ++rep_len;
658 report_buffer[rep_len] = 0;
659 fprintf (report_fp, "chat: %s\n", report_buffer);
661 if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
662 if (verbose)
663 fprintf (report_fp, "Closing \"%s\".\n", report_file);
664 fclose (report_fp);
665 report_fp = (FILE *) NULL;
668 #if defined(get_term_param)
669 if (have_tty_parameters) {
670 if (set_term_param (&saved_tty_parameters) < 0)
671 fatal(2, "Can't restore terminal parameters: %m");
673 #endif
675 exit(status);
679 * 'Clean up' this string.
681 char *clean(s, sending)
682 register char *s;
683 int sending; /* set to 1 when sending (putting) this string. */
685 char cur_chr;
686 char *s1, *p, *phchar;
687 int add_return = sending;
688 size_t len = strlen(s) + 3; /* see len comments below */
690 #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
691 #define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
692 || (((chr) >= 'a') && ((chr) <= 'z')) \
693 || (((chr) >= 'A') && ((chr) <= 'Z')) \
694 || (chr) == '_')
696 p = s1 = malloc(len);
697 if (!p)
698 fatal(2, "memory error!");
699 while (*s) {
700 cur_chr = *s++;
701 if (cur_chr == '^') {
702 cur_chr = *s++;
703 if (cur_chr == '\0') {
704 *p++ = '^';
705 break;
707 cur_chr &= 0x1F;
708 if (cur_chr != 0) {
709 *p++ = cur_chr;
711 continue;
714 if (use_env && cur_chr == '$') { /* ARI */
715 char c;
717 phchar = s;
718 while (isalnumx(*s))
719 s++;
720 c = *s; /* save */
721 *s = '\0';
722 phchar = getenv(phchar);
723 *s = c; /* restore */
724 if (phchar) {
725 len += strlen(phchar);
726 s1 = grow(s1, &p, len);
727 while (*phchar)
728 *p++ = *phchar++;
730 continue;
733 if (cur_chr != '\\') {
734 *p++ = cur_chr;
735 continue;
738 cur_chr = *s++;
739 if (cur_chr == '\0') {
740 if (sending) {
741 *p++ = '\\';
742 *p++ = '\\'; /* +1 for len */
744 break;
747 switch (cur_chr) {
748 case 'b':
749 *p++ = '\b';
750 break;
752 case 'c':
753 if (sending && *s == '\0')
754 add_return = 0;
755 else
756 *p++ = cur_chr;
757 break;
759 case '\\':
760 case 'K':
761 case 'p':
762 case 'd':
763 if (sending)
764 *p++ = '\\';
765 *p++ = cur_chr;
766 break;
768 case 'T':
769 if (sending && phone_num) {
770 len += strlen(phone_num);
771 s1 = grow(s1, &p, len);
772 for (phchar = phone_num; *phchar != '\0'; phchar++)
773 *p++ = *phchar;
775 else {
776 *p++ = '\\';
777 *p++ = 'T';
779 break;
781 case 'U':
782 if (sending && phone_num2) {
783 len += strlen(phone_num2);
784 s1 = grow(s1, &p, len);
785 for (phchar = phone_num2; *phchar != '\0'; phchar++)
786 *p++ = *phchar;
788 else {
789 *p++ = '\\';
790 *p++ = 'U';
792 break;
794 case 'q':
795 quiet = 1;
796 break;
798 case 'r':
799 *p++ = '\r';
800 break;
802 case 'n':
803 *p++ = '\n';
804 break;
806 case 's':
807 *p++ = ' ';
808 break;
810 case 't':
811 *p++ = '\t';
812 break;
814 case 'N':
815 if (sending) {
816 *p++ = '\\';
817 *p++ = '\0';
819 else
820 *p++ = 'N';
821 break;
823 case '$': /* ARI */
824 if (use_env) {
825 *p++ = cur_chr;
826 break;
828 /* FALL THROUGH */
830 default:
831 if (isoctal (cur_chr)) {
832 cur_chr &= 0x07;
833 if (isoctal (*s)) {
834 cur_chr <<= 3;
835 cur_chr |= *s++ - '0';
836 if (isoctal (*s)) {
837 cur_chr <<= 3;
838 cur_chr |= *s++ - '0';
842 if (cur_chr != 0 || sending) {
843 if (sending && (cur_chr == '\\' || cur_chr == 0))
844 *p++ = '\\';
845 *p++ = cur_chr;
847 break;
850 if (sending)
851 *p++ = '\\';
852 *p++ = cur_chr;
853 break;
857 if (add_return)
858 *p++ = '\r'; /* +2 for len */
860 *p = '\0'; /* +3 for len */
861 return s1;
865 * A modified version of 'strtok'. This version skips \ sequences.
868 char *expect_strtok (s, term)
869 char *s, *term;
871 static char *str = "";
872 int escape_flag = 0;
873 char *result;
876 * If a string was specified then do initial processing.
878 if (s)
879 str = s;
882 * If this is the escape flag then reset it and ignore the character.
884 if (*str)
885 result = str;
886 else
887 result = (char *) 0;
889 while (*str) {
890 if (escape_flag) {
891 escape_flag = 0;
892 ++str;
893 continue;
896 if (*str == '\\') {
897 ++str;
898 escape_flag = 1;
899 continue;
903 * If this is not in the termination string, continue.
905 if (strchr (term, *str) == (char *) 0) {
906 ++str;
907 continue;
911 * This is the terminator. Mark the end of the string and stop.
913 *str++ = '\0';
914 break;
916 return (result);
920 * Process the expect string
923 void chat_expect (s)
924 char *s;
926 char *expect;
927 char *reply;
929 if (strcmp(s, "HANGUP") == 0) {
930 ++hup_next;
931 return;
934 if (strcmp(s, "ABORT") == 0) {
935 ++abort_next;
936 return;
939 if (strcmp(s, "CLR_ABORT") == 0) {
940 ++clear_abort_next;
941 return;
944 if (strcmp(s, "REPORT") == 0) {
945 ++report_next;
946 return;
949 if (strcmp(s, "CLR_REPORT") == 0) {
950 ++clear_report_next;
951 return;
954 if (strcmp(s, "TIMEOUT") == 0) {
955 ++timeout_next;
956 return;
959 if (strcmp(s, "ECHO") == 0) {
960 ++echo_next;
961 return;
964 if (strcmp(s, "SAY") == 0) {
965 ++say_next;
966 return;
970 * Fetch the expect and reply string.
972 for (;;) {
973 expect = expect_strtok (s, "-");
974 s = (char *) 0;
976 if (expect == (char *) 0)
977 return;
979 reply = expect_strtok (s, "-");
982 * Handle the expect string. If successful then exit.
984 if (get_string (expect))
985 return;
988 * If there is a sub-reply string then send it. Otherwise any condition
989 * is terminal.
991 if (reply == (char *) 0 || exit_code != 3)
992 break;
994 chat_send (reply);
998 * The expectation did not occur. This is terminal.
1000 if (fail_reason)
1001 msgf("Failed (%s)", fail_reason);
1002 else
1003 msgf("Failed");
1004 terminate(exit_code);
1008 * Translate the input character to the appropriate string for printing
1009 * the data.
1012 char *character(c)
1013 int c;
1015 static char string[10];
1016 char *meta;
1018 meta = (c & 0x80) ? "M-" : "";
1019 c &= 0x7F;
1021 if (c < 32)
1022 snprintf(string, sizeof(string), "%s^%c", meta, (int)c + '@');
1023 else if (c == 127)
1024 snprintf(string, sizeof(string), "%s^?", meta);
1025 else
1026 snprintf(string, sizeof(string), "%s%c", meta, c);
1028 return (string);
1032 * process the reply string
1034 void chat_send (s)
1035 register char *s;
1037 char file_data[STR_LEN];
1039 if (say_next) {
1040 say_next = 0;
1041 s = clean(s, 1);
1042 write(2, s, strlen(s));
1043 free(s);
1044 return;
1047 if (hup_next) {
1048 hup_next = 0;
1049 if (strcmp(s, "OFF") == 0)
1050 signal(SIGHUP, SIG_IGN);
1051 else
1052 signal(SIGHUP, sighup);
1053 return;
1056 if (echo_next) {
1057 echo_next = 0;
1058 echo = (strcmp(s, "ON") == 0);
1059 return;
1062 if (abort_next) {
1063 char *s1;
1065 abort_next = 0;
1067 if (n_aborts >= MAX_ABORTS)
1068 fatal(2, "Too many ABORT strings");
1070 s1 = clean(s, 0);
1072 if (strlen(s1) > strlen(s)
1073 || strlen(s1) + 1 > sizeof(fail_buffer))
1074 fatal(1, "Illegal or too-long ABORT string ('%v')", s);
1076 abort_string[n_aborts++] = s1;
1078 if (verbose)
1079 msgf("abort on (%v)", s);
1080 return;
1083 if (clear_abort_next) {
1084 char *s1;
1085 int i;
1086 int old_max;
1087 int pack = 0;
1089 clear_abort_next = 0;
1091 s1 = clean(s, 0);
1093 if (strlen(s1) > strlen(s)
1094 || strlen(s1) + 1 > sizeof(fail_buffer))
1095 fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
1097 old_max = n_aborts;
1098 for (i=0; i < n_aborts; i++) {
1099 if ( strcmp(s1,abort_string[i]) == 0 ) {
1100 free(abort_string[i]);
1101 abort_string[i] = NULL;
1102 pack++;
1103 n_aborts--;
1104 if (verbose)
1105 msgf("clear abort on (%v)", s);
1108 free(s1);
1109 if (pack)
1110 pack_array(abort_string,old_max);
1111 return;
1114 if (report_next) {
1115 char *s1;
1117 report_next = 0;
1118 if (n_reports >= MAX_REPORTS)
1119 fatal(2, "Too many REPORT strings");
1121 s1 = clean(s, 0);
1122 if (strlen(s1) > strlen(s)
1123 || strlen(s1) + 1 > sizeof(fail_buffer))
1124 fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1126 report_string[n_reports++] = s1;
1128 if (verbose)
1129 msgf("report (%v)", s);
1130 return;
1133 if (clear_report_next) {
1134 char *s1;
1135 int i;
1136 int old_max;
1137 int pack = 0;
1139 clear_report_next = 0;
1141 s1 = clean(s, 0);
1143 if (strlen(s1) > strlen(s)
1144 || strlen(s1) + 1 > sizeof(fail_buffer))
1145 fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1147 old_max = n_reports;
1148 for (i=0; i < n_reports; i++) {
1149 if ( strcmp(s1,report_string[i]) == 0 ) {
1150 free(report_string[i]);
1151 report_string[i] = NULL;
1152 pack++;
1153 n_reports--;
1154 if (verbose)
1155 msgf("clear report (%v)", s);
1158 free(s1);
1159 if (pack)
1160 pack_array(report_string,old_max);
1162 return;
1165 if (timeout_next) {
1166 timeout_next = 0;
1167 timeout = atoi(s);
1169 if (timeout <= 0)
1170 timeout = DEFAULT_CHAT_TIMEOUT;
1172 if (verbose)
1173 msgf("timeout set to %d seconds", timeout);
1175 return;
1179 * The syntax @filename means read the string to send from the
1180 * file `filename'.
1182 if (s[0] == '@') {
1183 /* skip the @ and any following white-space */
1184 char *fn = s;
1185 while (*++fn == ' ' || *fn == '\t')
1188 if (*fn != 0) {
1189 FILE *f;
1190 int n = 0;
1192 /* open the file and read until STR_LEN-1 bytes or end-of-file */
1193 f = fopen(fn, "r");
1194 if (f == NULL)
1195 fatal(1, "%s -- open failed: %m", fn);
1196 while (n < STR_LEN - 1) {
1197 int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
1198 if (nr < 0)
1199 fatal(1, "%s -- read error", fn);
1200 if (nr == 0)
1201 break;
1202 n += nr;
1204 fclose(f);
1206 /* use the string we got as the string to send,
1207 but trim off the final newline if any. */
1208 if (n > 0 && file_data[n-1] == '\n')
1209 --n;
1210 file_data[n] = 0;
1211 s = file_data;
1215 if (strcmp(s, "EOT") == 0)
1216 s = "^D\\c";
1217 else if (strcmp(s, "BREAK") == 0)
1218 s = "\\K\\c";
1220 if (!put_string(s))
1221 fatal(1, "Failed");
1224 int get_char()
1226 int status;
1227 char c;
1229 status = read(0, &c, 1);
1231 switch (status) {
1232 case 1:
1233 return ((int)c & 0x7F);
1235 default:
1236 msgf("warning: read() on stdin returned %d", status);
1238 case -1:
1239 if ((status = fcntl(0, F_GETFL, 0)) == -1)
1240 fatal(2, "Can't get file mode flags on stdin: %m");
1242 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1243 fatal(2, "Can't set file mode flags on stdin: %m");
1245 return (-1);
1249 int put_char(c)
1250 int c;
1252 int status;
1253 char ch = c;
1255 usleep(10000); /* inter-character typing delay (?) */
1257 status = write(1, &ch, 1);
1259 switch (status) {
1260 case 1:
1261 return (0);
1263 default:
1264 msgf("warning: write() on stdout returned %d", status);
1266 case -1:
1267 if ((status = fcntl(0, F_GETFL, 0)) == -1)
1268 fatal(2, "Can't get file mode flags on stdin, %m");
1270 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1271 fatal(2, "Can't set file mode flags on stdin: %m");
1273 return (-1);
1277 int write_char (c)
1278 int c;
1280 if (alarmed || put_char(c) < 0) {
1281 alarm(0);
1282 alarmed = 0;
1284 if (verbose) {
1285 if (errno == EINTR || errno == EWOULDBLOCK)
1286 msgf(" -- write timed out");
1287 else
1288 msgf(" -- write failed: %m");
1290 return (0);
1292 return (1);
1295 int put_string (s)
1296 register char *s;
1298 char *ss;
1300 quiet = 0;
1301 s = ss = clean(s, 1);
1303 if (verbose) {
1304 if (quiet)
1305 msgf("send (?????\?)"); /* backslash to avoid trigraph ??) */
1306 else
1307 msgf("send (%v)", s);
1310 alarm(timeout); alarmed = 0;
1312 while (*s) {
1313 register char c = *s++;
1315 if (c != '\\') {
1316 if (!write_char (c)) {
1317 free(ss);
1318 return 0;
1320 continue;
1323 c = *s++;
1324 switch (c) {
1325 case 'd':
1326 sleep(1);
1327 break;
1329 case 'K':
1330 break_sequence();
1331 break;
1333 case 'p':
1334 usleep(10000); /* 1/100th of a second (arg is microseconds) */
1335 break;
1337 default:
1338 if (!write_char (c)) {
1339 free(ss);
1340 return 0;
1342 break;
1346 alarm(0);
1347 alarmed = 0;
1348 free(ss);
1349 return (1);
1353 * Echo a character to stderr.
1354 * When called with -1, a '\n' character is generated when
1355 * the cursor is not at the beginning of a line.
1357 void echo_stderr(n)
1358 int n;
1360 static int need_lf;
1361 char *s;
1363 switch (n) {
1364 case '\r': /* ignore '\r' */
1365 break;
1366 case -1:
1367 if (need_lf == 0)
1368 break;
1369 /* fall through */
1370 case '\n':
1371 write(2, "\n", 1);
1372 need_lf = 0;
1373 break;
1374 default:
1375 s = character(n);
1376 write(2, s, strlen(s));
1377 need_lf = 1;
1378 break;
1383 * 'Wait for' this string to appear on this file descriptor.
1385 int get_string(string)
1386 register char *string;
1388 char temp[STR_LEN];
1389 int c, len, minlen;
1390 register char *s = temp, *end = s + STR_LEN;
1391 char *logged = temp;
1393 fail_reason = (char *)0;
1394 string = clean(string, 0);
1395 len = strlen(string);
1396 minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1398 if (verbose)
1399 msgf("expect (%v)", string);
1401 if (len > STR_LEN) {
1402 msgf("expect string is too long");
1403 exit_code = 1;
1404 free(string);
1405 return 0;
1408 if (len == 0) {
1409 if (verbose)
1410 msgf("got it");
1411 free(string);
1412 return (1);
1415 alarm(timeout);
1416 alarmed = 0;
1418 while ( ! alarmed && (c = get_char()) >= 0) {
1419 int n, abort_len, report_len;
1421 if (echo)
1422 echo_stderr(c);
1423 if (verbose && c == '\n') {
1424 if (s == logged)
1425 msgf(""); /* blank line */
1426 else
1427 msgf("%0.*v", s - logged, logged);
1428 logged = s + 1;
1431 *s++ = c;
1433 if (verbose && s >= logged + 80) {
1434 msgf("%0.*v", s - logged, logged);
1435 logged = s;
1438 if (Verbose) {
1439 if (c == '\n')
1440 fputc( '\n', stderr );
1441 else if (c != '\r')
1442 fprintf( stderr, "%s", character(c) );
1445 if (!report_gathering) {
1446 for (n = 0; n < n_reports; ++n) {
1447 if ((report_string[n] != (char*) NULL) &&
1448 s - temp >= (report_len = strlen(report_string[n])) &&
1449 strncmp(s - report_len, report_string[n], report_len) == 0) {
1450 time_t time_now = time ((time_t*) NULL);
1451 struct tm* tm_now = localtime (&time_now);
1453 strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1454 strcat (report_buffer, report_string[n]);
1455 strlcat(report_buffer, report_string[n],
1456 sizeof(report_buffer));
1458 report_string[n] = (char *) NULL;
1459 report_gathering = 1;
1460 break;
1464 else {
1465 if (!iscntrl (c)) {
1466 int rep_len = strlen (report_buffer);
1467 report_buffer[rep_len] = c;
1468 report_buffer[rep_len + 1] = '\0';
1470 else {
1471 report_gathering = 0;
1472 fprintf (report_fp, "chat: %s\n", report_buffer);
1476 if (s - temp >= len &&
1477 c == string[len - 1] &&
1478 strncmp(s - len, string, len) == 0) {
1479 if (verbose) {
1480 if (s > logged)
1481 msgf("%0.*v", s - logged, logged);
1482 msgf(" -- got it\n");
1485 alarm(0);
1486 alarmed = 0;
1487 free(string);
1488 return (1);
1491 for (n = 0; n < n_aborts; ++n) {
1492 if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1493 strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
1494 if (verbose) {
1495 if (s > logged)
1496 msgf("%0.*v", s - logged, logged);
1497 msgf(" -- failed");
1500 alarm(0);
1501 alarmed = 0;
1502 exit_code = n + 4;
1503 strlcpy(fail_buffer, abort_string[n], sizeof(fail_buffer));
1504 fail_reason = fail_buffer;
1505 free(string);
1506 return (0);
1510 if (s >= end) {
1511 if (logged < s - minlen) {
1512 if (verbose)
1513 msgf("%0.*v", s - logged, logged);
1514 logged = s;
1516 s -= minlen;
1517 memmove(temp, s, minlen);
1518 logged = temp + (logged - s);
1519 s = temp + minlen;
1522 if (alarmed && verbose)
1523 msgf("warning: alarm synchronization problem");
1526 alarm(0);
1528 exit_code = 3;
1529 alarmed = 0;
1530 free(string);
1531 return (0);
1535 * Gross kludge to handle Solaris versions >= 2.6 having usleep.
1537 #ifdef SOL2
1538 #include <sys/param.h>
1539 #if MAXUID > 65536 /* then this is Solaris 2.6 or later */
1540 #undef NO_USLEEP
1541 #endif
1542 #endif /* SOL2 */
1544 #ifdef NO_USLEEP
1545 #include <sys/types.h>
1546 #include <sys/time.h>
1549 usleep -- support routine for 4.2BSD system call emulations
1550 last edit: 29-Oct-1984 D A Gwyn
1553 extern int select();
1556 usleep( usec ) /* returns 0 if ok, else -1 */
1557 long usec; /* delay in microseconds */
1559 static struct { /* `timeval' */
1560 long tv_sec; /* seconds */
1561 long tv_usec; /* microsecs */
1562 } delay; /* _select() timeout */
1564 delay.tv_sec = usec / 1000000L;
1565 delay.tv_usec = usec % 1000000L;
1567 return select(0, (long *)0, (long *)0, (long *)0, &delay);
1569 #endif
1571 void
1572 pack_array (array, end)
1573 char **array; /* The address of the array of string pointers */
1574 int end; /* The index of the next free entry before CLR_ */
1576 int i, j;
1578 for (i = 0; i < end; i++) {
1579 if (array[i] == NULL) {
1580 for (j = i+1; j < end; ++j)
1581 if (array[j] != NULL)
1582 array[i++] = array[j];
1583 for (; i < end; ++i)
1584 array[i] = NULL;
1585 break;
1591 * vfmtmsg - format a message into a buffer. Like vsprintf except we
1592 * also specify the length of the output buffer, and we handle the
1593 * %m (error message) format.
1594 * Doesn't do floating-point formats.
1595 * Returns the number of chars put into buf.
1597 #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
1600 vfmtmsg(buf, buflen, fmt, args)
1601 char *buf;
1602 int buflen;
1603 const char *fmt;
1604 va_list args;
1606 int c, i, n;
1607 int width, prec, fillch;
1608 int base, len, neg, quoted;
1609 unsigned long val = 0;
1610 char *str, *buf0;
1611 const char *f;
1612 unsigned char *p;
1613 char num[32];
1614 static char hexchars[] = "0123456789abcdef";
1616 buf0 = buf;
1617 --buflen;
1618 while (buflen > 0) {
1619 for (f = fmt; *f != '%' && *f != 0; ++f)
1621 if (f > fmt) {
1622 len = f - fmt;
1623 if (len > buflen)
1624 len = buflen;
1625 memcpy(buf, fmt, len);
1626 buf += len;
1627 buflen -= len;
1628 fmt = f;
1630 if (*fmt == 0)
1631 break;
1632 c = *++fmt;
1633 width = prec = 0;
1634 fillch = ' ';
1635 if (c == '0') {
1636 fillch = '0';
1637 c = *++fmt;
1639 if (c == '*') {
1640 width = va_arg(args, int);
1641 c = *++fmt;
1642 } else {
1643 while (isdigit(c)) {
1644 width = width * 10 + c - '0';
1645 c = *++fmt;
1648 if (c == '.') {
1649 c = *++fmt;
1650 if (c == '*') {
1651 prec = va_arg(args, int);
1652 c = *++fmt;
1653 } else {
1654 while (isdigit(c)) {
1655 prec = prec * 10 + c - '0';
1656 c = *++fmt;
1660 str = 0;
1661 base = 0;
1662 neg = 0;
1663 ++fmt;
1664 switch (c) {
1665 case 'd':
1666 i = va_arg(args, int);
1667 if (i < 0) {
1668 neg = 1;
1669 val = -i;
1670 } else
1671 val = i;
1672 base = 10;
1673 break;
1674 case 'o':
1675 val = va_arg(args, unsigned int);
1676 base = 8;
1677 break;
1678 case 'x':
1679 val = va_arg(args, unsigned int);
1680 base = 16;
1681 break;
1682 case 'p':
1683 val = (unsigned long) va_arg(args, void *);
1684 base = 16;
1685 neg = 2;
1686 break;
1687 case 's':
1688 str = va_arg(args, char *);
1689 break;
1690 case 'c':
1691 num[0] = va_arg(args, int);
1692 num[1] = 0;
1693 str = num;
1694 break;
1695 case 'm':
1696 str = strerror(errno);
1697 break;
1698 case 'v': /* "visible" string */
1699 case 'q': /* quoted string */
1700 quoted = c == 'q';
1701 p = va_arg(args, unsigned char *);
1702 if (fillch == '0' && prec > 0) {
1703 n = prec;
1704 } else {
1705 n = strlen((char *)p);
1706 if (prec > 0 && prec < n)
1707 n = prec;
1709 while (n > 0 && buflen > 0) {
1710 c = *p++;
1711 --n;
1712 if (!quoted && c >= 0x80) {
1713 OUTCHAR('M');
1714 OUTCHAR('-');
1715 c -= 0x80;
1717 if (quoted && (c == '"' || c == '\\'))
1718 OUTCHAR('\\');
1719 if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
1720 if (quoted) {
1721 OUTCHAR('\\');
1722 switch (c) {
1723 case '\t': OUTCHAR('t'); break;
1724 case '\n': OUTCHAR('n'); break;
1725 case '\b': OUTCHAR('b'); break;
1726 case '\f': OUTCHAR('f'); break;
1727 default:
1728 OUTCHAR('x');
1729 OUTCHAR(hexchars[c >> 4]);
1730 OUTCHAR(hexchars[c & 0xf]);
1732 } else {
1733 if (c == '\t')
1734 OUTCHAR(c);
1735 else {
1736 OUTCHAR('^');
1737 OUTCHAR(c ^ 0x40);
1740 } else
1741 OUTCHAR(c);
1743 continue;
1744 default:
1745 *buf++ = '%';
1746 if (c != '%')
1747 --fmt; /* so %z outputs %z etc. */
1748 --buflen;
1749 continue;
1751 if (base != 0) {
1752 str = num + sizeof(num);
1753 *--str = 0;
1754 while (str > num + neg) {
1755 *--str = hexchars[val % base];
1756 val = val / base;
1757 if (--prec <= 0 && val == 0)
1758 break;
1760 switch (neg) {
1761 case 1:
1762 *--str = '-';
1763 break;
1764 case 2:
1765 *--str = 'x';
1766 *--str = '0';
1767 break;
1769 len = num + sizeof(num) - 1 - str;
1770 } else {
1771 len = strlen(str);
1772 if (prec > 0 && len > prec)
1773 len = prec;
1775 if (width > 0) {
1776 if (width > buflen)
1777 width = buflen;
1778 if ((n = width - len) > 0) {
1779 buflen -= n;
1780 for (; n > 0; --n)
1781 *buf++ = fillch;
1784 if (len > buflen)
1785 len = buflen;
1786 memcpy(buf, str, len);
1787 buf += len;
1788 buflen -= len;
1790 *buf = 0;
1791 return buf - buf0;