blob: a399892a309f6a4e27c800d9f25d7b36e8e0d65c [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
2 * Chat -- a program for automatic session establishment (i.e. dial
3 * the phone and log in).
4 *
5 * Standard termination codes:
6 * 0 - successful completion of the script
7 * 1 - invalid argument, expect string too large, etc.
8 * 2 - error on an I/O operation or fatal error condition.
9 * 3 - timeout waiting for a simple string.
10 * 4 - the first string declared as "ABORT"
11 * 5 - the second string declared as "ABORT"
12 * 6 - ... and so on for successive ABORT strings.
13 *
14 * This software is in the public domain.
15 *
16 * -----------------
17 * 21st March 2003 - added network/telnet code option <davidm@snapgear.com>
18 * - added -R (truncated/created report file option)
19 *
20 * 22-May-99 added environment substitutuion, enabled with -E switch.
21 * Andreas Arens <andras@cityweb.de>.
22 *
23 * 12-May-99 added a feature to read data to be sent from a file,
24 * if the send string starts with @. Idea from gpk <gpk@onramp.net>.
25 *
26 * added -T and -U option and \T and \U substitution to pass a phone
27 * number into chat script. Two are needed for some ISDN TA applications.
28 * Keith Dart <kdart@cisco.com>
29 *
30 *
31 * Added SAY keyword to send output to stderr.
32 * This allows to turn ECHO OFF and to output specific, user selected,
33 * text to give progress messages. This best works when stderr
34 * exists (i.e.: pppd in nodetach mode).
35 *
36 * Added HANGUP directives to allow for us to be called
37 * back. When HANGUP is set to NO, chat will not hangup at HUP signal.
38 * We rely on timeouts in that case.
39 *
40 * Added CLR_ABORT to clear previously set ABORT string. This has been
41 * dictated by the HANGUP above as "NO CARRIER" (for example) must be
42 * an ABORT condition until we know the other host is going to close
43 * the connection for call back. As soon as we have completed the
44 * first stage of the call back sequence, "NO CARRIER" is a valid, non
45 * fatal string. As soon as we got called back (probably get "CONNECT"),
46 * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command.
47 * Note that CLR_ABORT packs the abort_strings[] array so that we do not
48 * have unused entries not being reclaimed.
49 *
50 * In the same vein as above, added CLR_REPORT keyword.
51 *
52 * Allow for comments. Line starting with '#' are comments and are
53 * ignored. If a '#' is to be expected as the first character, the
54 * expect string must be quoted.
55 *
56 *
57 * Francis Demierre <Francis@SwissMail.Com>
58 * Thu May 15 17:15:40 MET DST 1997
59 *
60 *
61 * Added -r "report file" switch & REPORT keyword.
62 * Robert Geer <bgeer@xmission.com>
63 *
64 * Added -s "use stderr" and -S "don't use syslog" switches.
65 * June 18, 1997
66 * Karl O. Pinc <kop@meme.com>
67 *
68 *
69 * Added -e "echo" switch & ECHO keyword
70 * Dick Streefland <dicks@tasking.nl>
71 *
72 *
73 * Considerable updates and modifications by
74 * Al Longyear <longyear@pobox.com>
75 * Paul Mackerras <paulus@cs.anu.edu.au>
76 *
77 *
78 * The original author is:
79 *
80 * Karl Fox <karl@MorningStar.Com>
81 * Morning Star Technologies, Inc.
82 * 1760 Zollinger Road
83 * Columbus, OH 43221
84 * (614)451-1883
85 *
86 */
87
88#ifndef __STDC__
89#define const
90#endif
91
92#ifndef lint
93static const char rcsid[] = "$Id: chat.c,v 1.8 2007-10-31 03:38:05 davidm Exp $";
94#endif
95
96#include <stdio.h>
97#include <ctype.h>
98#include <time.h>
99#include <fcntl.h>
100#include <signal.h>
101#include <errno.h>
102#include <string.h>
103#include <stdlib.h>
104#include <unistd.h>
105#include <sys/types.h>
106#include <sys/stat.h>
107#include <syslog.h>
108
109#ifndef TERMIO
110#undef TERMIOS
111#define TERMIOS
112#endif
113
114#ifdef TERMIO
115#include <termio.h>
116#endif
117#ifdef TERMIOS
118#include <termios.h>
119#endif
120
121#define STR_LEN 1024
122
123#ifndef SIGTYPE
124#define SIGTYPE void
125#endif
126
127#undef __P
128#undef __V
129
130#ifdef __STDC__
131#include <stdarg.h>
132#define __V(x) x
133#define __P(x) x
134#else
135#include <varargs.h>
136#define __V(x) (va_alist) va_dcl
137#define __P(x) ()
138#define const
139#endif
140
141#ifndef O_NONBLOCK
142#define O_NONBLOCK O_NDELAY
143#endif
144
145#ifdef SUNOS
146extern int sys_nerr;
147extern char *sys_errlist[];
148#define memmove(to, from, n) bcopy(from, to, n)
149#define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\
150 "unknown error")
151#endif
152
153/*************** Micro getopt() *********************************************/
154#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
155 (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
156 &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
157#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
158 (_O=4,(char*)0):(char*)0)
159#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
160#define ARG(c,v) (c?(--c,*v++):(char*)0)
161
162static int _O = 0; /* Internal state */
163/*************** Micro getopt() *********************************************/
164
165char *program_name;
166
167#define MAX_ABORTS 50
168#define MAX_REPORTS 50
169#define DEFAULT_CHAT_TIMEOUT 45
170
171int echo = 0;
172int verbose = 0;
173int to_log = 1;
174int to_stderr = 0;
175int Verbose = 0;
176int quiet = 0;
177int report = 0;
178int use_env = 0;
179int exit_code = 0;
180FILE* report_fp = (FILE *) 0;
181char *report_file = (char *) 0;
182char *chat_file = (char *) 0;
183char *phone_num = (char *) 0;
184char *phone_num2 = (char *) 0;
185int timeout = DEFAULT_CHAT_TIMEOUT;
186char *device = 0;
187//#define syslog(...)
188#ifdef ENABLE_NETWORK_SUPPORT
189
190int network = 0;
191char *net_host = NULL;
192int net_port = 23; /* telnet by default */
193
194#define IAC 0xff
195#define DONT 0xfe
196#define DO 0xfd
197#define WONT 0xfc
198#define WILL 0xfb
199
200extern int net_get_char(unsigned char *cp);
201extern void net_open();
202
203#endif /* ENABLE_NETWORK_SUPPORT */
204
205int have_tty_parameters = 0;
206
207#ifdef TERMIO
208#define term_parms struct termio
209#define get_term_param(param) ioctl(0, TCGETA, param)
210#define set_term_param(param) ioctl(0, TCSETA, param)
211struct termio saved_tty_parameters;
212#endif
213
214#ifdef TERMIOS
215#define term_parms struct termios
216#define get_term_param(param) tcgetattr(0, param)
217#define set_term_param(param) tcsetattr(0, TCSANOW, param)
218struct termios saved_tty_parameters;
219#endif
220
221char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
222 fail_buffer[50];
223int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0;
224int clear_abort_next = 0;
225
226char *report_string[MAX_REPORTS] ;
227char report_buffer[256] ;
228int n_reports = 0, report_next = 0, report_gathering = 0 ;
229int clear_report_next = 0;
230
231int say_next = 0, hup_next = 0;
232
233void *dup_mem __P((void *b, size_t c));
234void *copy_of __P((char *s));
235char *grow __P((char *s, char **p, size_t len));
236void usage __P((void));
237void msgf __P((const char *fmt, ...));
238void fatal __P((int code, const char *fmt, ...));
239SIGTYPE sigalrm __P((int signo));
240SIGTYPE sigint __P((int signo));
241SIGTYPE sigterm __P((int signo));
242SIGTYPE sighup __P((int signo));
243void unalarm __P((void));
244void init __P((void));
245void set_tty_parameters __P((void));
246void echo_stderr __P((int));
247void break_sequence __P((void));
248void terminate __P((int status));
249void do_file __P((char *chat_file));
250int get_string __P((register char *string));
251int put_string __P((register char *s));
252int write_char __P((int c));
253int put_char __P((int c));
254int get_char __P((void));
255void chat_send __P((register char *s));
256char *character __P((int c));
257void chat_expect __P((register char *s));
258char *clean __P((register char *s, int sending));
259void break_sequence __P((void));
260void terminate __P((int status));
261void pack_array __P((char **array, int end));
262char *expect_strtok __P((char *, char *));
263int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */
264
265int main __P((int, char *[]));
266
267void *dup_mem(b, c)
268void *b;
269size_t c;
270{
271 void *ans = malloc (c);
272 if (!ans)
273 fatal(2, "memory error!");
274
275 memcpy (ans, b, c);
276 return ans;
277}
278
279void *copy_of (s)
280char *s;
281{
282 return dup_mem (s, strlen (s) + 1);
283}
284
285/* grow a char buffer and keep a pointer offset */
286char *grow(s, p, len)
287char *s;
288char **p;
289size_t len;
290{
291 size_t l = *p - s; /* save p as distance into s */
292
293 s = realloc(s, len);
294 if (!s)
295 fatal(2, "memory error!");
296 *p = s + l; /* restore p */
297 return s;
298}
299
300/*
301 * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \
302 * [ -r report-file ] [-d device] \
303 * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
304 *
305 * Perform a UUCP-dialer-like chat script on stdin and stdout.
306 * (or the given device if specified)
307 */
308int
309main(argc, argv)
310 int argc;
311 char **argv;
312{
313 int option;
314 char *arg;
315 program_name = *argv;
316#ifndef EMBED
317//aaaaaaaa
318 //tzset();
319#endif
320
321 while ((option = OPTION(argc, argv)) != 0) {
322 switch (option) {
323 case 'e':
324 ++echo;
325 break;
326
327 case 'E':
328 ++use_env;
329 break;
330
331 case 'v':
332 ++verbose;
333 break;
334
335#ifdef ENABLE_NETWORK_SUPPORT
336 case 'h':
337 ++network;
338 if ((arg = OPTARG(argc, argv)) != NULL)
339 net_host = copy_of(arg);
340 else
341 usage();
342 break;
343
344 case 'p':
345 if ((arg = OPTARG(argc, argv)) != NULL)
346 net_port = atoi(arg);
347 else
348 usage();
349 break;
350#endif
351
352 case 'V':
353 ++Verbose;
354 break;
355
356 case 's':
357 ++to_stderr;
358 break;
359
360 case 'S':
361 to_log = 0;
362 break;
363
364 case 'f':
365 if ((arg = OPTARG(argc, argv)) != NULL)
366 chat_file = copy_of(arg);
367 else
368 usage();
369 break;
370
371 case 't':
372 if ((arg = OPTARG(argc, argv)) != NULL)
373 timeout = atoi(arg);
374 else
375 usage();
376 break;
377
378 case 'd':
379 if ((arg = OPTARG(argc, argv)) != NULL)
380 {
381 //syslog(LOG_ERR,"device is %s", arg);
382 device = arg;
383 }
384 else
385 usage();
386 break;
387
388 case 'r':
389 case 'R':
390 arg = OPTARG (argc, argv);
391 if (arg) {
392 if (report_fp != NULL)
393 fclose (report_fp);
394 report_file = copy_of (arg);
395 report_fp = fopen (report_file, option == 'r' ? "a" : "w");
396 if (report_fp != NULL) {
397 if (verbose)
398 fprintf (report_fp, "Opening \"%s\"...\n",
399 report_file);
400 report = 1;
401 }
402 }
403 break;
404
405 case 'T':
406 if ((arg = OPTARG(argc, argv)) != NULL)
407 phone_num = copy_of(arg);
408 else
409 usage();
410 break;
411
412 case 'U':
413 if ((arg = OPTARG(argc, argv)) != NULL)
414 phone_num2 = copy_of(arg);
415 else
416 usage();
417 break;
418
419 default:
420 usage();
421 break;
422 }
423 }
424
425/*
426 * Default the report file to the stderr location
427 */
428 if (report_fp == NULL)
429 report_fp = stderr;
430
431 if (to_log) {
432#ifdef ultrix
433 openlog("chat", LOG_PID);
434#else
435 openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
436
437 if (verbose)
438 setlogmask(LOG_UPTO(LOG_INFO));
439 else
440 setlogmask(LOG_UPTO(LOG_WARNING));
441#endif
442 }
443
444 if (device != 0) {
445 int fd = open(device, O_RDWR, S_IRUSR | S_IWUSR);
446 if (fd == -1)
447 usage();
448 dup2(fd, 0);
449 dup2(fd, 1);
450 }
451
452 init();
453
454 if (chat_file != NULL) {
455 arg = ARG(argc, argv);
456 if (arg != NULL)
457 usage();
458 else
459 do_file (chat_file);
460 } else {
461 while ((arg = ARG(argc, argv)) != NULL) {
462 chat_expect(arg);
463
464 if ((arg = ARG(argc, argv)) != NULL)
465 chat_send(arg);
466 }
467 }
468
469 terminate(0);
470 return 0;
471}
472
473/*
474 * Process a chat script when read from a file.
475 */
476
477void do_file (chat_file)
478char *chat_file;
479{
480 int linect, sendflg;
481 char *sp, *arg, quote;
482 char buf [STR_LEN];
483 FILE *cfp;
484 //syslog(LOG_ERR,"do_file");
485 cfp = fopen (chat_file, "r");
486 if (cfp == NULL)
487 fatal(1, "%s -- open failed: %m", chat_file);
488
489 linect = 0;
490 sendflg = 0;
491
492 while (fgets(buf, STR_LEN, cfp) != NULL) {
493 sp = strchr (buf, '\n');
494 if (sp)
495 *sp = '\0';
496
497 linect++;
498 sp = buf;
499
500 /* lines starting with '#' are comments. If a real '#'
501 is to be expected, it should be quoted .... */
502 if ( *sp == '#' )
503 continue;
504
505 while (*sp != '\0') {
506 if (*sp == ' ' || *sp == '\t') {
507 ++sp;
508 continue;
509 }
510
511 if (*sp == '"' || *sp == '\'') {
512 quote = *sp++;
513 arg = sp;
514 //syslog(LOG_ERR," arg is %s", arg);
515 while (*sp != quote) {
516 if (*sp == '\0')
517 fatal(1, "unterminated quote (line %d)", linect);
518
519 if (*sp++ == '\\') {
520 if (*sp != '\0')
521 ++sp;
522 }
523 }
524 }
525 else {
526 arg = sp;
527 //syslog(LOG_ERR," arg is %s", arg);
528 while (*sp != '\0' && *sp != ' ' && *sp != '\t')
529 ++sp;
530 }
531
532 if (*sp != '\0')
533 *sp++ = '\0';
534
535 if (sendflg)
536 {
537 //syslog(LOG_ERR,"chat_send arg is %s", arg);
538 chat_send (arg);
539 }
540 else
541 {
542 //syslog(LOG_ERR,"chat_expect arg is %s", arg);
543 chat_expect (arg);
544 }
545 sendflg = !sendflg;
546 }
547 }
548 //syslog(LOG_ERR,"close file done");
549 fclose (cfp);
550}
551
552/*
553 * We got an error parsing the command line.
554 */
555void usage()
556{
557 fprintf(stderr, "\
558Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file] [-R report-file]\n"
559#ifdef ENABLE_NETWORK_SUPPORT
560" [-h hostname] [-p tcp-port]\n"
561#endif /* ENABLE_NETWORK_SUPPORT */
562" [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
563 exit(1);
564}
565
566char line[1024];
567
568/*
569 * Send a message to syslog and/or stderr.
570 */
571void msgf __V((const char *fmt, ...))
572{
573 va_list args;
574
575#ifdef __STDC__
576 va_start(args, fmt);
577#else
578 char *fmt;
579 va_start(args);
580 fmt = va_arg(args, char *);
581#endif
582
583 vfmtmsg(line, sizeof(line), fmt, args);
584 if (to_log)
585 //syslog(LOG_INFO, "%s", line);
586 if (to_stderr)
587 fprintf(stderr, "%s\n", line);
588}
589
590/*
591 * Print an error message and terminate.
592 */
593
594void fatal __V((int code, const char *fmt, ...))
595{
596 va_list args;
597
598#ifdef __STDC__
599 va_start(args, fmt);
600#else
601 int code;
602 char *fmt;
603 va_start(args);
604 code = va_arg(args, int);
605 fmt = va_arg(args, char *);
606#endif
607
608 vfmtmsg(line, sizeof(line), fmt, args);
609 if (to_log)
610 //syslog(LOG_ERR, "%s", line);
611 if (to_stderr)
612 fprintf(stderr, "%s\n", line);
613 terminate(code);
614}
615
616int alarmed = 0;
617
618SIGTYPE sigalrm(signo)
619int signo;
620{
621 int flags;
622
623 alarm(1);
624 alarmed = 1; /* Reset alarm to avoid race window */
625 signal(SIGALRM, sigalrm); /* that can cause hanging in read() */
626
627 if ((flags = fcntl(0, F_GETFL, 0)) == -1)
628 fatal(2, "Can't get file mode flags on stdin: %m");
629
630 if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
631 fatal(2, "Can't set file mode flags on stdin: %m");
632
633 if (verbose)
634 msgf("alarm");
635}
636
637void unalarm()
638{
639 int flags;
640
641 if ((flags = fcntl(0, F_GETFL, 0)) == -1)
642 fatal(2, "Can't get file mode flags on stdin: %m");
643
644 if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
645 fatal(2, "Can't set file mode flags on stdin: %m");
646}
647
648SIGTYPE sigint(signo)
649int signo;
650{
651 fatal(2, "SIGINT");
652}
653
654SIGTYPE sigterm(signo)
655int signo;
656{
657 fatal(2, "SIGTERM");
658}
659
660SIGTYPE sighup(signo)
661int signo;
662{
663 fatal(2, "SIGHUP");
664}
665
666void init()
667{
668 signal(SIGINT, sigint);
669 signal(SIGTERM, sigterm);
670 signal(SIGHUP, sighup);
671
672#ifdef ENABLE_NETWORK_SUPPORT
673 if (network)
674 net_open();
675 else
676#endif
677 set_tty_parameters();
678 signal(SIGALRM, sigalrm);
679 alarm(0);
680 alarmed = 0;
681}
682
683void set_tty_parameters()
684{
685#if defined(get_term_param)
686 term_parms t;
687
688 if (get_term_param (&t) < 0)
689#ifndef EMBED
690 fatal(2, "Can't get terminal parameters: %m");
691#else
692 syslog(LOG_ERR, "Can't get terminal parameters: %m");
693#endif
694
695 saved_tty_parameters = t;
696 have_tty_parameters = 1;
697
698 t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
699 t.c_oflag = 0;
700 t.c_lflag = 0;
701 t.c_cc[VERASE] =
702 t.c_cc[VKILL] = 0;
703 t.c_cc[VMIN] = 1;
704 t.c_cc[VTIME] = 0;
705
706 if (set_term_param (&t) < 0)
707#ifndef EMBED
708 fatal(2, "Can't set terminal parameters: %m");
709#else
710 syslog(LOG_ERR, "Can't set terminal parameters: %m");
711#endif
712#endif
713}
714
715void break_sequence()
716{
717#ifdef TERMIOS
718 tcsendbreak (0, 0);
719#endif
720}
721
722void terminate(status)
723int status;
724{
725 static int terminating = 0;
726
727 if (terminating)
728 exit(status);
729 terminating = 1;
730 echo_stderr(-1);
731/*
732 * Allow the last of the report string to be gathered before we terminate.
733 */
734 if (report_gathering &&
735 report_file != (char *) 0 && report_fp != (FILE *) NULL) {
736 int c, rep_len;
737
738 rep_len = strlen(report_buffer);
739 while (rep_len + 1 <= sizeof(report_buffer)) {
740 alarm(1);
741 c = get_char();
742 alarm(0);
743 if (c < 0 || iscntrl(c))
744 break;
745 report_buffer[rep_len] = c;
746 ++rep_len;
747 }
748 report_buffer[rep_len] = 0;
749 fprintf (report_fp, "chat: %s\n", report_buffer);
750 }
751 if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
752 if (verbose)
753 fprintf (report_fp, "Closing \"%s\".\n", report_file);
754 fclose (report_fp);
755 report_fp = (FILE *) NULL;
756 }
757
758#if defined(get_term_param)
759 if (have_tty_parameters) {
760 if (set_term_param (&saved_tty_parameters) < 0)
761#ifndef EMBED
762 fatal(2, "Can't restore terminal parameters: %m");
763#else
764 syslog(LOG_ERR, "Can't restore terminal parameters: %m");
765#endif
766 }
767#endif
768 syslog(LOG_ERR,"terminate done");
769 exit(status);
770}
771
772/*
773 * 'Clean up' this string.
774 */
775char *clean(s, sending)
776register char *s;
777int sending; /* set to 1 when sending (putting) this string. */
778{
779 char cur_chr;
780 char *s1, *p, *phchar;
781 int add_return = sending;
782 size_t len = strlen(s) + 3; /* see len comments below */
783
784#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
785#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
786 || (((chr) >= 'a') && ((chr) <= 'z')) \
787 || (((chr) >= 'A') && ((chr) <= 'Z')) \
788 || (chr) == '_')
789
790 p = s1 = malloc(len);
791 if (!p)
792 fatal(2, "memory error!");
793 while (*s) {
794 cur_chr = *s++;
795 if (cur_chr == '^') {
796 cur_chr = *s++;
797 if (cur_chr == '\0') {
798 *p++ = '^';
799 break;
800 }
801 cur_chr &= 0x1F;
802 if (cur_chr != 0) {
803 *p++ = cur_chr;
804 }
805 continue;
806 }
807
808 if (use_env && cur_chr == '$') { /* ARI */
809 char c;
810
811 phchar = s;
812 while (isalnumx(*s))
813 s++;
814 c = *s; /* save */
815 *s = '\0';
816 phchar = getenv(phchar);
817 *s = c; /* restore */
818 if (phchar) {
819 len += strlen(phchar);
820 s1 = grow(s1, &p, len);
821 while (*phchar)
822 *p++ = *phchar++;
823 }
824 continue;
825 }
826
827 if (cur_chr != '\\') {
828 *p++ = cur_chr;
829 continue;
830 }
831
832 cur_chr = *s++;
833 if (cur_chr == '\0') {
834 if (sending) {
835 *p++ = '\\';
836 *p++ = '\\'; /* +1 for len */
837 }
838 break;
839 }
840
841 switch (cur_chr) {
842 case 'b':
843 *p++ = '\b';
844 break;
845
846 case 'c':
847 if (sending && *s == '\0')
848 add_return = 0;
849 else
850 *p++ = cur_chr;
851 break;
852
853 case '\\':
854 case 'K':
855 case 'p':
856 case 'd':
857 if (sending)
858 *p++ = '\\';
859 *p++ = cur_chr;
860 break;
861
862 case 'T':
863 if (sending && phone_num) {
864 len += strlen(phone_num);
865 s1 = grow(s1, &p, len);
866 for (phchar = phone_num; *phchar != '\0'; phchar++)
867 *p++ = *phchar;
868 }
869 else {
870 *p++ = '\\';
871 *p++ = 'T';
872 }
873 break;
874
875 case 'U':
876 if (sending && phone_num2) {
877 len += strlen(phone_num2);
878 s1 = grow(s1, &p, len);
879 for (phchar = phone_num2; *phchar != '\0'; phchar++)
880 *p++ = *phchar;
881 }
882 else {
883 *p++ = '\\';
884 *p++ = 'U';
885 }
886 break;
887
888 case 'q':
889 quiet = 1;
890 break;
891
892 case 'r':
893 *p++ = '\r';
894 break;
895
896 case 'n':
897 *p++ = '\n';
898 break;
899
900 case 's':
901 *p++ = ' ';
902 break;
903
904 case 't':
905 *p++ = '\t';
906 break;
907
908 case 'N':
909 if (sending) {
910 *p++ = '\\';
911 *p++ = '\0';
912 }
913 else
914 *p++ = 'N';
915 break;
916
917 case '$': /* ARI */
918 if (use_env) {
919 *p++ = cur_chr;
920 break;
921 }
922 /* FALL THROUGH */
923
924 default:
925 if (isoctal (cur_chr)) {
926 cur_chr &= 0x07;
927 if (isoctal (*s)) {
928 cur_chr <<= 3;
929 cur_chr |= *s++ - '0';
930 if (isoctal (*s)) {
931 cur_chr <<= 3;
932 cur_chr |= *s++ - '0';
933 }
934 }
935
936 if (cur_chr != 0 || sending) {
937 if (sending && (cur_chr == '\\' || cur_chr == 0))
938 *p++ = '\\';
939 *p++ = cur_chr;
940 }
941 break;
942 }
943
944 if (sending)
945 *p++ = '\\';
946 *p++ = cur_chr;
947 break;
948 }
949 }
950
951 if (add_return)
952 *p++ = '\r'; /* +2 for len */
953
954 *p = '\0'; /* +3 for len */
955 return s1;
956}
957
958/*
959 * A modified version of 'strtok'. This version skips \ sequences.
960 */
961
962char *expect_strtok (s, term)
963 char *s, *term;
964{
965 static char *str = "";
966 int escape_flag = 0;
967 char *result;
968
969/*
970 * If a string was specified then do initial processing.
971 */
972 if (s)
973 str = s;
974
975/*
976 * If this is the escape flag then reset it and ignore the character.
977 */
978 if (*str)
979 result = str;
980 else
981 result = (char *) 0;
982
983 while (*str) {
984 if (escape_flag) {
985 escape_flag = 0;
986 ++str;
987 continue;
988 }
989
990 if (*str == '\\') {
991 ++str;
992 escape_flag = 1;
993 continue;
994 }
995
996/*
997 * If this is not in the termination string, continue.
998 */
999 if (strchr (term, *str) == (char *) 0) {
1000 ++str;
1001 continue;
1002 }
1003
1004/*
1005 * This is the terminator. Mark the end of the string and stop.
1006 */
1007 *str++ = '\0';
1008 break;
1009 }
1010 return (result);
1011}
1012
1013/*
1014 * Process the expect string
1015 */
1016
1017void chat_expect (s)
1018char *s;
1019{
1020 char *expect;
1021 char *reply;
1022
1023 if (strcmp(s, "HANGUP") == 0) {
1024 ++hup_next;
1025 return;
1026 }
1027
1028 if (strcmp(s, "ABORT") == 0) {
1029 ++abort_next;
1030 return;
1031 }
1032
1033 if (strcmp(s, "CLR_ABORT") == 0) {
1034 ++clear_abort_next;
1035 return;
1036 }
1037
1038 if (strcmp(s, "REPORT") == 0) {
1039 ++report_next;
1040 return;
1041 }
1042
1043 if (strcmp(s, "CLR_REPORT") == 0) {
1044 ++clear_report_next;
1045 return;
1046 }
1047
1048 if (strcmp(s, "TIMEOUT") == 0) {
1049 ++timeout_next;
1050 return;
1051 }
1052
1053 if (strcmp(s, "ECHO") == 0) {
1054 ++echo_next;
1055 return;
1056 }
1057
1058 if (strcmp(s, "SAY") == 0) {
1059 ++say_next;
1060 return;
1061 }
1062
1063/*
1064 * Fetch the expect and reply string.
1065 */
1066 for (;;) {
1067 expect = expect_strtok (s, "-");
1068 s = (char *) 0;
1069
1070 if (expect == (char *) 0)
1071 return;
1072
1073 reply = expect_strtok (s, "-");
1074
1075/*
1076 * Handle the expect string. If successful then exit.
1077 */
1078 if (get_string (expect))
1079 return;
1080
1081/*
1082 * If there is a sub-reply string then send it. Otherwise any condition
1083 * is terminal.
1084 */
1085 if (reply == (char *) 0 || exit_code != 3)
1086 break;
1087
1088 chat_send (reply);
1089 }
1090
1091/*
1092 * The expectation did not occur. This is terminal.
1093 */
1094 if (fail_reason)
1095 msgf("Failed (%s)", fail_reason);
1096 else
1097 msgf("Failed");
1098 terminate(exit_code);
1099}
1100
1101/*
1102 * Translate the input character to the appropriate string for printing
1103 * the data.
1104 */
1105
1106char *character(c)
1107int c;
1108{
1109 static char string[10];
1110 char *meta;
1111
1112 meta = (c & 0x80) ? "M-" : "";
1113 c &= 0x7F;
1114
1115 if (c < 32)
1116 sprintf(string, "%s^%c", meta, (int)c + '@');
1117 else if (c == 127)
1118 sprintf(string, "%s^?", meta);
1119 else
1120 sprintf(string, "%s%c", meta, c);
1121
1122 return (string);
1123}
1124
1125/*
1126 * process the reply string
1127 */
1128void chat_send (s)
1129register char *s;
1130{
1131 char file_data[STR_LEN];
1132
1133 if (say_next) {
1134 say_next = 0;
1135 s = clean(s, 1);
1136 write(2, s, strlen(s));
1137 free(s);
1138 return;
1139 }
1140
1141 if (hup_next) {
1142 hup_next = 0;
1143 if (strcmp(s, "OFF") == 0)
1144 signal(SIGHUP, SIG_IGN);
1145 else
1146 signal(SIGHUP, sighup);
1147 return;
1148 }
1149
1150 if (echo_next) {
1151 echo_next = 0;
1152 echo = (strcmp(s, "ON") == 0);
1153 return;
1154 }
1155
1156 if (abort_next) {
1157 char *s1;
1158
1159 abort_next = 0;
1160
1161 if (n_aborts >= MAX_ABORTS)
1162 fatal(2, "Too many ABORT strings");
1163
1164 s1 = clean(s, 0);
1165
1166 if (strlen(s1) > strlen(s)
1167 || strlen(s1) + 1 > sizeof(fail_buffer))
1168 fatal(1, "Illegal or too-long ABORT string ('%v')", s);
1169
1170 abort_string[n_aborts++] = s1;
1171
1172 if (verbose)
1173 msgf("abort on (%v)", s);
1174 return;
1175 }
1176
1177 if (clear_abort_next) {
1178 char *s1;
1179 int i;
1180 int old_max;
1181 int pack = 0;
1182
1183 clear_abort_next = 0;
1184
1185 s1 = clean(s, 0);
1186
1187 if (strlen(s1) > strlen(s)
1188 || strlen(s1) + 1 > sizeof(fail_buffer))
1189 fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
1190
1191 old_max = n_aborts;
1192 for (i=0; i < n_aborts; i++) {
1193 if ( strcmp(s1,abort_string[i]) == 0 ) {
1194 free(abort_string[i]);
1195 abort_string[i] = NULL;
1196 pack++;
1197 n_aborts--;
1198 if (verbose)
1199 msgf("clear abort on (%v)", s);
1200 }
1201 }
1202 free(s1);
1203 if (pack)
1204 pack_array(abort_string,old_max);
1205 return;
1206 }
1207
1208 if (report_next) {
1209 char *s1;
1210
1211 report_next = 0;
1212 if (n_reports >= MAX_REPORTS)
1213 fatal(2, "Too many REPORT strings");
1214
1215 s1 = clean(s, 0);
1216
1217 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof(fail_buffer) - 1)
1218 fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1219
1220 report_string[n_reports++] = s1;
1221
1222 if (verbose)
1223 msgf("report (%v)", s);
1224 return;
1225 }
1226
1227 if (clear_report_next) {
1228 char *s1;
1229 int i;
1230 int old_max;
1231 int pack = 0;
1232
1233 clear_report_next = 0;
1234
1235 s1 = clean(s, 0);
1236
1237 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof(fail_buffer) - 1)
1238 fatal(1, "Illegal or too-long REPORT string ('%v')", s);
1239
1240 old_max = n_reports;
1241 for (i=0; i < n_reports; i++) {
1242 if ( strcmp(s1,report_string[i]) == 0 ) {
1243 free(report_string[i]);
1244 report_string[i] = NULL;
1245 pack++;
1246 n_reports--;
1247 if (verbose)
1248 msgf("clear report (%v)", s);
1249 }
1250 }
1251 free(s1);
1252 if (pack)
1253 pack_array(report_string,old_max);
1254
1255 return;
1256 }
1257
1258 if (timeout_next) {
1259 timeout_next = 0;
1260 timeout = atoi(s);
1261
1262 if (timeout <= 0)
1263 timeout = DEFAULT_CHAT_TIMEOUT;
1264
1265 if (verbose)
1266 msgf("timeout set to %d seconds", timeout);
1267
1268 return;
1269 }
1270
1271 /*
1272 * The syntax @filename means read the string to send from the
1273 * file `filename'.
1274 */
1275 if (s[0] == '@') {
1276 /* skip the @ and any following white-space */
1277 char *fn = s;
1278 while (*++fn == ' ' || *fn == '\t')
1279 ;
1280
1281 if (*fn != 0) {
1282 FILE *f;
1283 int n = 0;
1284
1285 /* open the file and read until STR_LEN-1 bytes or end-of-file */
1286 f = fopen(fn, "r");
1287 if (f == NULL)
1288 fatal(1, "%s -- open failed: %m", fn);
1289 while (n < STR_LEN - 1) {
1290 int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
1291 if (nr < 0)
1292 fatal(1, "%s -- read error", fn);
1293 if (nr == 0)
1294 break;
1295 n += nr;
1296 }
1297 fclose(f);
1298
1299 /* use the string we got as the string to send,
1300 but trim off the final newline if any. */
1301 if (n > 0 && file_data[n-1] == '\n')
1302 --n;
1303 file_data[n] = 0;
1304 s = file_data;
1305 }
1306 }
1307
1308 if (strcmp(s, "EOT") == 0)
1309 s = "^D\\c";
1310 else if (strcmp(s, "BREAK") == 0)
1311 s = "\\K\\c";
1312
1313 if (!put_string(s))
1314 fatal(1, "Failed");
1315}
1316
1317int get_char()
1318{
1319 int status;
1320 unsigned char c;
1321
1322 status = read(0, &c, 1);
1323 switch (status) {
1324 case 1:
1325#ifdef ENABLE_NETWORK_SUPPORT
1326 if (network && c == IAC) {
1327 if (net_get_char(&c) != -1)
1328 return ((int)c & 0x7F);
1329 /* drop through to error below */
1330 } else
1331#endif
1332 return ((int)c & 0x7F);
1333
1334 default:
1335 msgf("warning: read() on stdin returned %d", status);
1336
1337 case -1:
1338 if ((status = fcntl(0, F_GETFL, 0)) == -1)
1339 fatal(2, "Can't get file mode flags on stdin: %m");
1340
1341 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1342 fatal(2, "Can't set file mode flags on stdin: %m");
1343
1344 return (-1);
1345 }
1346}
1347
1348int put_char(c)
1349int c;
1350{
1351 int status;
1352 char ch = c;
1353
1354 usleep(10000); /* inter-character typing delay (?) */
1355
1356 status = write(1, &ch, 1);/*wangming*/
1357
1358 switch (status) {
1359 case 1:
1360 return (0);
1361
1362 default:
1363 msgf("warning: write() on stdout returned %d", status);
1364
1365 case -1:
1366 if ((status = fcntl(0, F_GETFL, 0)) == -1)
1367 fatal(2, "Can't get file mode flags on stdin, %m");
1368
1369 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1370 fatal(2, "Can't set file mode flags on stdin: %m");
1371
1372 return (-1);
1373 }
1374}
1375
1376int write_char (c)
1377int c;
1378{
1379 if (alarmed || put_char(c) < 0) {
1380 alarm(0);
1381 alarmed = 0;
1382
1383 if (verbose) {
1384 if (errno == EINTR || errno == EWOULDBLOCK)
1385 msgf(" -- write timed out");
1386 else
1387 msgf(" -- write failed: %m");
1388 }
1389 return (0);
1390 }
1391 return (1);
1392}
1393
1394int put_string (s)
1395register char *s;
1396{
1397 quiet = 0;
1398 s = clean(s, 1);
1399
1400 if (verbose) {
1401 if (quiet)
1402 msgf("send (?????\?)");
1403 else
1404 msgf("send (%v)", s);
1405 }
1406
1407 alarm(timeout); alarmed = 0;
1408
1409 while (*s) {
1410 register char c = *s++;
1411
1412 if (c != '\\') {
1413 if (!write_char (c))
1414 return 0;
1415 continue;
1416 }
1417
1418 c = *s++;
1419 switch (c) {
1420 case 'd':
1421 sleep(1);
1422 break;
1423
1424 case 'K':
1425 break_sequence();
1426 break;
1427
1428 case 'p':
1429 usleep(10000); /* 1/100th of a second (arg is microseconds) */
1430 break;
1431
1432 default:
1433 if (!write_char (c))
1434 return 0;
1435 break;
1436 }
1437 }
1438
1439 alarm(0);
1440 alarmed = 0;
1441 return (1);
1442}
1443
1444/*
1445 * Echo a character to stderr.
1446 * When called with -1, a '\n' character is generated when
1447 * the cursor is not at the beginning of a line.
1448 */
1449void echo_stderr(n)
1450int n;
1451{
1452 static int need_lf;
1453 char *s;
1454
1455 switch (n) {
1456 case '\r': /* ignore '\r' */
1457 break;
1458 case -1:
1459 if (need_lf == 0)
1460 break;
1461 /* fall through */
1462 case '\n':
1463 write(2, "\n", 1);
1464 need_lf = 0;
1465 break;
1466 default:
1467 s = character(n);
1468 write(2, s, strlen(s));
1469 need_lf = 1;
1470 break;
1471 }
1472}
1473
1474/*
1475 * 'Wait for' this string to appear on this file descriptor.
1476 */
1477int get_string(string)
1478register char *string;
1479{
1480 char temp[STR_LEN];
1481 int c, printed = 0, len, minlen;
1482 register char *s = temp, *end = s + STR_LEN;
1483 char *logged = temp;
1484
1485 fail_reason = (char *)0;
1486 string = clean(string, 0);
1487 len = strlen(string);
1488 minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1489 if (verbose)
1490 msgf("expect (%v)", string);
1491
1492 if (len > STR_LEN) {
1493 msgf("expect string is too long");
1494 exit_code = 1;
1495 return 0;
1496 }
1497
1498 if (len == 0) {
1499 if (verbose)
1500 msgf("got it");
1501 return (1);
1502 }
1503 alarm(timeout);
1504 alarmed = 0;
1505
1506 while ( ! alarmed && (c = get_char()) >= 0) {
1507 int n, abort_len, report_len;
1508
1509 if (echo)
1510 echo_stderr(c);
1511 if (verbose && c == '\n') {
1512 if (s == logged)
1513 msgf(""); /* blank line */
1514 else
1515 msgf("%0.*v", s - logged, logged);
1516 logged = s + 1;
1517 }
1518
1519 *s++ = c;
1520
1521 if (verbose && s >= logged + 80) {
1522 msgf("%0.*v", s - logged, logged);
1523 logged = s;
1524 }
1525
1526 if (Verbose) {
1527 if (c == '\n')
1528 fputc( '\n', stderr );
1529 else if (c != '\r')
1530 fprintf( stderr, "%s", character(c) );
1531 }
1532
1533 if (!report_gathering) {
1534 for (n = 0; n < n_reports; ++n) {
1535 if ((report_string[n] != (char*) NULL) &&
1536 s - temp >= (report_len = strlen(report_string[n])) &&
1537 strncmp(s - report_len, report_string[n], report_len) == 0) {
1538 time_t time_now = time ((time_t*) NULL);
1539 struct tm* tm_now = localtime (&time_now);
1540
1541 strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1542 strcat (report_buffer, report_string[n]);
1543
1544 report_string[n] = (char *) NULL;
1545 report_gathering = 1;
1546 break;
1547 }
1548 }
1549 }
1550 else {
1551 if (!iscntrl (c)) {
1552 int rep_len = strlen (report_buffer);
1553 report_buffer[rep_len] = c;
1554 report_buffer[rep_len + 1] = '\0';
1555 }
1556 else {
1557 report_gathering = 0;
1558 fprintf (report_fp, "chat: %s\n", report_buffer);
1559 }
1560 }
1561
1562 if (s - temp >= len &&
1563 c == string[len - 1] &&
1564 strncmp(s - len, string, len) == 0) {
1565 if (verbose) {
1566 if (s > logged)
1567 msgf("%0.*v", s - logged, logged);
1568 msgf(" -- got it\n");
1569 }
1570
1571 alarm(0);
1572 alarmed = 0;
1573 return (1);
1574 }
1575
1576 for (n = 0; n < n_aborts; ++n) {
1577 if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1578 strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
1579 if (verbose) {
1580 if (s > logged)
1581 msgf("%0.*v", s - logged, logged);
1582 msgf(" -- failed");
1583 }
1584
1585 alarm(0);
1586 alarmed = 0;
1587 exit_code = n + 4;
1588 strcpy(fail_reason = fail_buffer, abort_string[n]);
1589 return (0);
1590 }
1591 }
1592
1593 if (s >= end) {
1594 if (logged < s - minlen) {
1595 if (verbose)
1596 msgf("%0.*v", s - logged, logged);
1597 logged = s;
1598 }
1599 s -= minlen;
1600 memmove(temp, s, minlen);
1601 logged = temp + (logged - s);
1602 s = temp + minlen;
1603 }
1604
1605 if (alarmed && verbose)
1606 msgf("warning: alarm synchronization problem");
1607 }
1608
1609 alarm(0);
1610
1611 if (verbose && printed) {
1612 if (alarmed)
1613 msgf(" -- read timed out");
1614 else
1615 msgf(" -- read failed: %m");
1616 }
1617
1618 exit_code = 3;
1619 alarmed = 0;
1620 return (0);
1621}
1622
1623/*
1624 * Gross kludge to handle Solaris versions >= 2.6 having usleep.
1625 */
1626#ifdef SOL2
1627#include <sys/param.h>
1628#if MAXUID > 65536 /* then this is Solaris 2.6 or later */
1629#undef NO_USLEEP
1630#endif
1631#endif /* SOL2 */
1632
1633#ifdef NO_USLEEP
1634#include <sys/types.h>
1635#include <sys/time.h>
1636
1637/*
1638 usleep -- support routine for 4.2BSD system call emulations
1639 last edit: 29-Oct-1984 D A Gwyn
1640 */
1641
1642extern int select();
1643
1644int
1645usleep( usec ) /* returns 0 if ok, else -1 */
1646 long usec; /* delay in microseconds */
1647{
1648 static struct { /* `timeval' */
1649 long tv_sec; /* seconds */
1650 long tv_usec; /* microsecs */
1651 } delay; /* _select() timeout */
1652
1653 delay.tv_sec = usec / 1000000L;
1654 delay.tv_usec = usec % 1000000L;
1655
1656 return select(0, (long *)0, (long *)0, (long *)0, &delay);
1657}
1658#endif
1659
1660void
1661pack_array (array, end)
1662 char **array; /* The address of the array of string pointers */
1663 int end; /* The index of the next free entry before CLR_ */
1664{
1665 int i, j;
1666
1667 for (i = 0; i < end; i++) {
1668 if (array[i] == NULL) {
1669 for (j = i+1; j < end; ++j)
1670 if (array[j] != NULL)
1671 array[i++] = array[j];
1672 for (; i < end; ++i)
1673 array[i] = NULL;
1674 break;
1675 }
1676 }
1677}
1678
1679/*
1680 * vfmtmsg - format a message into a buffer. Like vsprintf except we
1681 * also specify the length of the output buffer, and we handle the
1682 * %m (error message) format.
1683 * Doesn't do floating-point formats.
1684 * Returns the number of chars put into buf.
1685 */
1686#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
1687
1688int
1689vfmtmsg(buf, buflen, fmt, args)
1690 char *buf;
1691 int buflen;
1692 const char *fmt;
1693 va_list args;
1694{
1695 int c, i, n;
1696 int width, prec, fillch;
1697 int base, len, neg, quoted;
1698 unsigned long val = 0;
1699 char *str, *buf0;
1700 const char *f;
1701 unsigned char *p;
1702 char num[32];
1703 static char hexchars[] = "0123456789abcdef";
1704
1705 buf0 = buf;
1706 --buflen;
1707 while (buflen > 0) {
1708 for (f = fmt; *f != '%' && *f != 0; ++f)
1709 ;
1710 if (f > fmt) {
1711 len = f - fmt;
1712 if (len > buflen)
1713 len = buflen;
1714 memcpy(buf, fmt, len);
1715 buf += len;
1716 buflen -= len;
1717 fmt = f;
1718 }
1719 if (*fmt == 0)
1720 break;
1721 c = *++fmt;
1722 width = prec = 0;
1723 fillch = ' ';
1724 if (c == '0') {
1725 fillch = '0';
1726 c = *++fmt;
1727 }
1728 if (c == '*') {
1729 width = va_arg(args, int);
1730 c = *++fmt;
1731 } else {
1732 while (isdigit(c)) {
1733 width = width * 10 + c - '0';
1734 c = *++fmt;
1735 }
1736 }
1737 if (c == '.') {
1738 c = *++fmt;
1739 if (c == '*') {
1740 prec = va_arg(args, int);
1741 c = *++fmt;
1742 } else {
1743 while (isdigit(c)) {
1744 prec = prec * 10 + c - '0';
1745 c = *++fmt;
1746 }
1747 }
1748 }
1749 str = 0;
1750 base = 0;
1751 neg = 0;
1752 ++fmt;
1753 switch (c) {
1754 case 'd':
1755 i = va_arg(args, int);
1756 if (i < 0) {
1757 neg = 1;
1758 val = -i;
1759 } else
1760 val = i;
1761 base = 10;
1762 break;
1763 case 'o':
1764 val = va_arg(args, unsigned int);
1765 base = 8;
1766 break;
1767 case 'x':
1768 val = va_arg(args, unsigned int);
1769 base = 16;
1770 break;
1771 case 'p':
1772 val = (unsigned long) va_arg(args, void *);
1773 base = 16;
1774 neg = 2;
1775 break;
1776 case 's':
1777 str = va_arg(args, char *);
1778 break;
1779 case 'c':
1780 num[0] = va_arg(args, int);
1781 num[1] = 0;
1782 str = num;
1783 break;
1784 case 'm':
1785 str = strerror(errno);
1786 break;
1787 case 'v': /* "visible" string */
1788 case 'q': /* quoted string */
1789 quoted = c == 'q';
1790 p = va_arg(args, unsigned char *);
1791 if (fillch == '0' && prec > 0) {
1792 n = prec;
1793 } else {
1794 n = strlen((char *)p);
1795 if (prec > 0 && prec < n)
1796 n = prec;
1797 }
1798 while (n > 0 && buflen > 0) {
1799 c = *p++;
1800 --n;
1801 if (!quoted && c >= 0x80) {
1802 OUTCHAR('M');
1803 OUTCHAR('-');
1804 c -= 0x80;
1805 }
1806 if (quoted && (c == '"' || c == '\\'))
1807 OUTCHAR('\\');
1808 if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
1809 if (quoted) {
1810 OUTCHAR('\\');
1811 switch (c) {
1812 case '\t': OUTCHAR('t'); break;
1813 case '\n': OUTCHAR('n'); break;
1814 case '\b': OUTCHAR('b'); break;
1815 case '\f': OUTCHAR('f'); break;
1816 default:
1817 OUTCHAR('x');
1818 OUTCHAR(hexchars[c >> 4]);
1819 OUTCHAR(hexchars[c & 0xf]);
1820 }
1821 } else {
1822 if (c == '\t')
1823 OUTCHAR(c);
1824 else {
1825 OUTCHAR('^');
1826 OUTCHAR(c ^ 0x40);
1827 }
1828 }
1829 } else
1830 OUTCHAR(c);
1831 }
1832 continue;
1833 default:
1834 *buf++ = '%';
1835 if (c != '%')
1836 --fmt; /* so %z outputs %z etc. */
1837 --buflen;
1838 continue;
1839 }
1840 if (base != 0) {
1841 str = num + sizeof(num);
1842 *--str = 0;
1843 while (str > num + neg) {
1844 *--str = hexchars[val % base];
1845 val = val / base;
1846 if (--prec <= 0 && val == 0)
1847 break;
1848 }
1849 switch (neg) {
1850 case 1:
1851 *--str = '-';
1852 break;
1853 case 2:
1854 *--str = 'x';
1855 *--str = '0';
1856 break;
1857 }
1858 len = num + sizeof(num) - 1 - str;
1859 } else {
1860 len = strlen(str);
1861 if (prec > 0 && len > prec)
1862 len = prec;
1863 }
1864 if (width > 0) {
1865 if (width > buflen)
1866 width = buflen;
1867 if ((n = width - len) > 0) {
1868 buflen -= n;
1869 for (; n > 0; --n)
1870 *buf++ = fillch;
1871 }
1872 }
1873 if (len > buflen)
1874 len = buflen;
1875 memcpy(buf, str, len);
1876 buf += len;
1877 buflen -= len;
1878 }
1879 *buf = 0;
1880 return buf - buf0;
1881}
1882
1883
1884#ifdef ENABLE_NETWORK_SUPPORT
1885/*
1886 * Allow chat to chat to a network connection easily
1887 */
1888
1889#include <netdb.h>
1890#include <netinet/in.h>
1891#include <arpa/inet.h>
1892#include <sys/socket.h>
1893
1894void
1895net_open()
1896{
1897 struct sockaddr_in sock_in;
1898 struct hostent *host;
1899 int s;
1900 aaaaaaaaaaaaa
1901 bzero((char *)&sock_in, sizeof (sock_in));
1902 sock_in.sin_family = AF_INET;
1903 s = socket(AF_INET, SOCK_STREAM, 0);
1904 if (s == -1) {
1905 perror("socket");
1906 exit(1);
1907 }
1908
1909 if (bind(s, (struct sockaddr*)&sock_in, sizeof (sock_in)) == -1) {
1910 perror("bind");
1911 exit(2);
1912 }
1913
1914 host = gethostbyname(net_host);
1915 if (host) {
1916 sock_in.sin_family = host->h_addrtype;
1917 bcopy(host->h_addr, &sock_in.sin_addr, host->h_length);
1918 } else {
1919 sock_in.sin_family = AF_INET;
1920 sock_in.sin_addr.s_addr = inet_addr(net_host);
1921 if (sock_in.sin_addr.s_addr == -1) {
1922 fprintf(stderr, "%s: %s unknown host\n", program_name, net_host);
1923 exit(3);
1924 }
1925 }
1926
1927 sock_in.sin_port = htons(net_port);
1928
1929 if (connect(s, (struct sockaddr*)&sock_in, sizeof(sock_in)) == -1) {
1930 perror("connect:");
1931 exit(4);
1932 }
1933
1934 /*
1935 * make the socket stdin/stdout
1936 */
1937 if (s != 0) {
1938 dup2(s, 0);
1939 close(s);
1940 }
1941 dup2(0, 1);
1942}
1943
1944
1945/* we arrive here having already receive an IAC */
1946int
1947net_get_char(unsigned char *cp)
1948{
1949 int status;
1950 unsigned char cmd, option;
1951
1952 do {
1953 status = read(0, &cmd, 1);
1954 if (status <= 0)
1955 return(IAC);
1956
1957 if (cmd == IAC) /* escaped IAC */
1958 return(IAC);
1959
1960 status = read(0, &option, 1);
1961 if (status <= 0) {
1962 put_char(IAC);
1963 return(cmd);
1964 }
1965
1966 switch (cmd) {
1967 case WILL:
1968 put_char(IAC);
1969 put_char(DONT);
1970 put_char(option);
1971 break;
1972 case WONT:
1973 break;
1974 case DO:
1975 put_char(IAC);
1976 put_char(WONT);
1977 put_char(option);
1978 break;
1979 case DONT:
1980 break;
1981 default:
1982 put_char(IAC);
1983 put_char(cmd);
1984 return(option);
1985 }
1986
1987 /*
1988 * get next char
1989 */
1990 status = read(0, cp, 1);
1991 if (status <= 0)
1992 return(-1);
1993 } while (*cp == IAC);
1994
1995 return(*cp);
1996}
1997
1998#endif /* ENABLE_NETWORK_SUPPORT */