blob: e9a3503b436370fc7c32e6077815d00eb53a447a [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
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 warn("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 warn("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
316 program_name = *argv;
317#ifndef EMBED
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 device = arg;
381 else
382 usage();
383 break;
384
385 case 'r':
386 case 'R':
387 arg = OPTARG (argc, argv);
388 if (arg) {
389 if (report_fp != NULL)
390 fclose (report_fp);
391 report_file = copy_of (arg);
392 report_fp = fopen (report_file, option == 'r' ? "a" : "w");
393 if (report_fp != NULL) {
394 if (verbose)
395 fprintf (report_fp, "Opening \"%s\"...\n",
396 report_file);
397 report = 1;
398 }
399 }
400 break;
401
402 case 'T':
403 if ((arg = OPTARG(argc, argv)) != NULL)
404 phone_num = copy_of(arg);
405 else
406 usage();
407 break;
408
409 case 'U':
410 if ((arg = OPTARG(argc, argv)) != NULL)
411 phone_num2 = copy_of(arg);
412 else
413 usage();
414 break;
415
416 default:
417 usage();
418 break;
419 }
420 }
421
422/*
423 * Default the report file to the stderr location
424 */
425 if (report_fp == NULL)
426 report_fp = stderr;
427
428 if (to_log) {
429#ifdef ultrix
430 openlog("chat", LOG_PID);
431#else
432 openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
433
434 if (verbose)
435 setlogmask(LOG_UPTO(LOG_INFO));
436 else
437 setlogmask(LOG_UPTO(LOG_WARNING));
438#endif
439 }
440
441 if (device != 0) {
442 int fd = open(device, O_RDWR, S_IRUSR | S_IWUSR);
443 if (fd == -1)
444 usage();
445 dup2(fd, 0);
446 dup2(fd, 1);
447 }
448
449 init();
450
451 if (chat_file != NULL) {
452 arg = ARG(argc, argv);
453 if (arg != NULL)
454 usage();
455 else
456 do_file (chat_file);
457 } else {
458 while ((arg = ARG(argc, argv)) != NULL) {
459 chat_expect(arg);
460
461 if ((arg = ARG(argc, argv)) != NULL)
462 chat_send(arg);
463 }
464 }
465
466 terminate(0);
467 return 0;
468}
469
470/*
471 * Process a chat script when read from a file.
472 */
473
474void do_file (chat_file)
475char *chat_file;
476{
477 int linect, sendflg;
478 char *sp, *arg, quote;
479 char buf [STR_LEN];
480 FILE *cfp;
481
482 cfp = fopen (chat_file, "r");
483 if (cfp == NULL)
484 {
485 syslog(LOG_ERR, "Can't set terminal parameters");
486 warn("Warning: disabling multilink");
487 warn("%s -- open failed: %m", chat_file);
488 //syslog(LOG_ERR, "Can't set terminal parameters");
489 }
490 linect = 0;
491 sendflg = 0;
492
493 while (fgets(buf, STR_LEN, cfp) != NULL) {
494 sp = strchr (buf, '\n');
495 if (sp)
496 *sp = '\0';
497
498 linect++;
499 sp = buf;
500
501 /* lines starting with '#' are comments. If a real '#'
502 is to be expected, it should be quoted .... */
503 if ( *sp == '#' )
504 continue;
505
506 while (*sp != '\0') {
507 if (*sp == ' ' || *sp == '\t') {
508 ++sp;
509 continue;
510 }
511
512 if (*sp == '"' || *sp == '\'') {
513 quote = *sp++;
514 arg = sp;
515 while (*sp != quote) {
516 if (*sp == '\0')
517 warn("unterminated quote (line %d)", linect);
518
519 if (*sp++ == '\\') {
520 if (*sp != '\0')
521 ++sp;
522 }
523 }
524 }
525 else {
526 arg = sp;
527 while (*sp != '\0' && *sp != ' ' && *sp != '\t')
528 ++sp;
529 }
530
531 if (*sp != '\0')
532 *sp++ = '\0';
533
534 if (sendflg)
535 {
536 chat_send (arg);
537 }
538 else
539 {
540 warn("wangming: %s---->%d", __FILE__, __LINE__);
541 chat_expect (arg);
542 }
543 sendflg = !sendflg;
544 }
545 }
546 fclose (cfp);
547}
548
549/*
550 * We got an error parsing the command line.
551 */
552void usage()
553{
554 fprintf(stderr, "\
555Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file] [-R report-file]\n"
556#ifdef ENABLE_NETWORK_SUPPORT
557" [-h hostname] [-p tcp-port]\n"
558#endif /* ENABLE_NETWORK_SUPPORT */
559" [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name);
560 exit(1);
561}
562
563char line[1024];
564
565/*
566 * Send a message to syslog and/or stderr.
567 */
568void msgf __V((const char *fmt, ...))
569{
570 va_list args;
571
572#ifdef __STDC__
573 va_start(args, fmt);
574#else
575 char *fmt;
576 va_start(args);
577 fmt = va_arg(args, char *);
578#endif
579
580 vfmtmsg(line, sizeof(line), fmt, args);
581 if (to_log)
582 syslog(LOG_INFO, "%s", line);
583 if (to_stderr)
584 fprintf(stderr, "%s\n", line);
585}
586
587/*
588 * Print an error message and terminate.
589 */
590
591void fatal __V((int code, const char *fmt, ...))
592{
593 va_list args;
594
595#ifdef __STDC__
596 va_start(args, fmt);
597#else
598 int code;
599 char *fmt;
600 va_start(args);
601 code = va_arg(args, int);
602 fmt = va_arg(args, char *);
603#endif
604
605 vfmtmsg(line, sizeof(line), fmt, args);
606 if (to_log)
607 syslog(LOG_ERR, "%s", line);
608 if (to_stderr)
609 fprintf(stderr, "%s\n", line);
610 terminate(code);
611}
612
613int alarmed = 0;
614
615SIGTYPE sigalrm(signo)
616int signo;
617{
618 int flags;
619
620 alarm(1);
621 alarmed = 1; /* Reset alarm to avoid race window */
622 signal(SIGALRM, sigalrm); /* that can cause hanging in read() */
623
624 if ((flags = fcntl(0, F_GETFL, 0)) == -1)
625 warn("Can't get file mode flags on stdin: %m");
626
627 if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
628 warn("Can't set file mode flags on stdin: %m");
629
630 if (verbose)
631 warn("alarm");
632}
633
634void unalarm()
635{
636 int flags;
637
638 if ((flags = fcntl(0, F_GETFL, 0)) == -1)
639 warn("Can't get file mode flags on stdin: %m");
640
641 if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
642 warn("Can't set file mode flags on stdin: %m");
643}
644
645SIGTYPE sigint(signo)
646int signo;
647{
648 warn("SIGINT");
649}
650
651SIGTYPE sigterm(signo)
652int signo;
653{
654 warn("SIGTERM");
655}
656
657SIGTYPE sighup(signo)
658int signo;
659{
660 warn("SIGHUP");
661}
662
663void init()
664{
665 signal(SIGINT, sigint);
666 signal(SIGTERM, sigterm);
667 signal(SIGHUP, sighup);
668
669#ifdef ENABLE_NETWORK_SUPPORT
670 if (network)
671 net_open();
672 else
673#endif
674 set_tty_parameters();
675 signal(SIGALRM, sigalrm);
676 alarm(0);
677 alarmed = 0;
678}
679
680void set_tty_parameters()
681{
682#if defined(get_term_param)
683 term_parms t;
684
685 if (get_term_param (&t) < 0)
686#ifndef EMBED
687 warn("Can't get terminal parameters: %m");
688#else
689 syslog(LOG_ERR, "Can't get terminal parameters: %m");
690#endif
691
692 saved_tty_parameters = t;
693 have_tty_parameters = 1;
694
695 t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
696 t.c_oflag = 0;
697 t.c_lflag = 0;
698 t.c_cc[VERASE] =
699 t.c_cc[VKILL] = 0;
700 t.c_cc[VMIN] = 1;
701 t.c_cc[VTIME] = 0;
702
703 if (set_term_param (&t) < 0)
704#ifndef EMBED
705 warn("Can't set terminal parameters: %m");
706#else
707 syslog(LOG_ERR, "Can't set terminal parameters: %m");
708#endif
709#endif
710}
711
712void break_sequence()
713{
714#ifdef TERMIOS
715 tcsendbreak (0, 0);
716#endif
717}
718
719void terminate(status)
720int status;
721{
722 static int terminating = 0;
723
724 if (terminating)
725 exit(status);
726 terminating = 1;
727 echo_stderr(-1);
728/*
729 * Allow the last of the report string to be gathered before we terminate.
730 */
731 if (report_gathering &&
732 report_file != (char *) 0 && report_fp != (FILE *) NULL) {
733 int c, rep_len;
734
735 rep_len = strlen(report_buffer);
736 while (rep_len + 1 <= sizeof(report_buffer)) {
737 alarm(1);
738 c = get_char();
739 alarm(0);
740 if (c < 0 || iscntrl(c))
741 break;
742 report_buffer[rep_len] = c;
743 ++rep_len;
744 }
745 report_buffer[rep_len] = 0;
746 fprintf (report_fp, "chat: %s\n", report_buffer);
747 }
748 if (report_file != (char *) 0 && report_fp != (FILE *) NULL) {
749 if (verbose)
750 fprintf (report_fp, "Closing \"%s\".\n", report_file);
751 fclose (report_fp);
752 report_fp = (FILE *) NULL;
753 }
754
755#if defined(get_term_param)
756 if (have_tty_parameters) {
757 if (set_term_param (&saved_tty_parameters) < 0)
758#ifndef EMBED
759 warn("Can't restore terminal parameters: %m");
760#else
761 syslog(LOG_ERR, "Can't restore terminal parameters: %m");
762#endif
763 }
764#endif
765
766 exit(status);
767}
768
769/*
770 * 'Clean up' this string.
771 */
772char *clean(s, sending)
773register char *s;
774int sending; /* set to 1 when sending (putting) this string. */
775{
776 char cur_chr;
777 char *s1, *p, *phchar;
778 int add_return = sending;
779 size_t len = strlen(s) + 3; /* see len comments below */
780
781#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
782#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
783 || (((chr) >= 'a') && ((chr) <= 'z')) \
784 || (((chr) >= 'A') && ((chr) <= 'Z')) \
785 || (chr) == '_')
786
787 p = s1 = malloc(len);
788 if (!p)
789 warn("memory error!");
790 while (*s) {
791 cur_chr = *s++;
792 if (cur_chr == '^') {
793 cur_chr = *s++;
794 if (cur_chr == '\0') {
795 *p++ = '^';
796 break;
797 }
798 cur_chr &= 0x1F;
799 if (cur_chr != 0) {
800 *p++ = cur_chr;
801 }
802 continue;
803 }
804
805 if (use_env && cur_chr == '$') { /* ARI */
806 char c;
807
808 phchar = s;
809 while (isalnumx(*s))
810 s++;
811 c = *s; /* save */
812 *s = '\0';
813 phchar = getenv(phchar);
814 *s = c; /* restore */
815 if (phchar) {
816 len += strlen(phchar);
817 s1 = grow(s1, &p, len);
818 while (*phchar)
819 *p++ = *phchar++;
820 }
821 continue;
822 }
823
824 if (cur_chr != '\\') {
825 *p++ = cur_chr;
826 continue;
827 }
828
829 cur_chr = *s++;
830 if (cur_chr == '\0') {
831 if (sending) {
832 *p++ = '\\';
833 *p++ = '\\'; /* +1 for len */
834 }
835 break;
836 }
837
838 switch (cur_chr) {
839 case 'b':
840 *p++ = '\b';
841 break;
842
843 case 'c':
844 if (sending && *s == '\0')
845 add_return = 0;
846 else
847 *p++ = cur_chr;
848 break;
849
850 case '\\':
851 case 'K':
852 case 'p':
853 case 'd':
854 if (sending)
855 *p++ = '\\';
856 *p++ = cur_chr;
857 break;
858
859 case 'T':
860 if (sending && phone_num) {
861 len += strlen(phone_num);
862 s1 = grow(s1, &p, len);
863 for (phchar = phone_num; *phchar != '\0'; phchar++)
864 *p++ = *phchar;
865 }
866 else {
867 *p++ = '\\';
868 *p++ = 'T';
869 }
870 break;
871
872 case 'U':
873 if (sending && phone_num2) {
874 len += strlen(phone_num2);
875 s1 = grow(s1, &p, len);
876 for (phchar = phone_num2; *phchar != '\0'; phchar++)
877 *p++ = *phchar;
878 }
879 else {
880 *p++ = '\\';
881 *p++ = 'U';
882 }
883 break;
884
885 case 'q':
886 quiet = 1;
887 break;
888
889 case 'r':
890 *p++ = '\r';
891 break;
892
893 case 'n':
894 *p++ = '\n';
895 break;
896
897 case 's':
898 *p++ = ' ';
899 break;
900
901 case 't':
902 *p++ = '\t';
903 break;
904
905 case 'N':
906 if (sending) {
907 *p++ = '\\';
908 *p++ = '\0';
909 }
910 else
911 *p++ = 'N';
912 break;
913
914 case '$': /* ARI */
915 if (use_env) {
916 *p++ = cur_chr;
917 break;
918 }
919 /* FALL THROUGH */
920
921 default:
922 if (isoctal (cur_chr)) {
923 cur_chr &= 0x07;
924 if (isoctal (*s)) {
925 cur_chr <<= 3;
926 cur_chr |= *s++ - '0';
927 if (isoctal (*s)) {
928 cur_chr <<= 3;
929 cur_chr |= *s++ - '0';
930 }
931 }
932
933 if (cur_chr != 0 || sending) {
934 if (sending && (cur_chr == '\\' || cur_chr == 0))
935 *p++ = '\\';
936 *p++ = cur_chr;
937 }
938 break;
939 }
940
941 if (sending)
942 *p++ = '\\';
943 *p++ = cur_chr;
944 break;
945 }
946 }
947
948 if (add_return)
949 *p++ = '\r'; /* +2 for len */
950
951 *p = '\0'; /* +3 for len */
952 return s1;
953}
954
955/*
956 * A modified version of 'strtok'. This version skips \ sequences.
957 */
958
959char *expect_strtok (s, term)
960 char *s, *term;
961{
962 static char *str = "";
963 int escape_flag = 0;
964 char *result;
965
966/*
967 * If a string was specified then do initial processing.
968 */
969 if (s)
970 str = s;
971
972/*
973 * If this is the escape flag then reset it and ignore the character.
974 */
975 if (*str)
976 result = str;
977 else
978 result = (char *) 0;
979
980 while (*str) {
981 if (escape_flag) {
982 escape_flag = 0;
983 ++str;
984 continue;
985 }
986
987 if (*str == '\\') {
988 ++str;
989 escape_flag = 1;
990 continue;
991 }
992
993/*
994 * If this is not in the termination string, continue.
995 */
996 if (strchr (term, *str) == (char *) 0) {
997 ++str;
998 continue;
999 }
1000
1001/*
1002 * This is the terminator. Mark the end of the string and stop.
1003 */
1004 *str++ = '\0';
1005 break;
1006 }
1007 return (result);
1008}
1009
1010/*
1011 * Process the expect string
1012 */
1013
1014void chat_expect (s)
1015char *s;
1016{
1017 char *expect;
1018 char *reply;
1019 warn("wangming: %s---->%d, s is %s", __FILE__, __LINE__, s);
1020 if (strcmp(s, "HANGUP") == 0) {
1021 ++hup_next;
1022 return;
1023 }
1024
1025 if (strcmp(s, "ABORT") == 0) {
1026 ++abort_next;
1027 return;
1028 }
1029
1030 if (strcmp(s, "CLR_ABORT") == 0) {
1031 ++clear_abort_next;
1032 return;
1033 }
1034
1035 if (strcmp(s, "REPORT") == 0) {
1036 ++report_next;
1037 return;
1038 }
1039
1040 if (strcmp(s, "CLR_REPORT") == 0) {
1041 ++clear_report_next;
1042 return;
1043 }
1044
1045 if (strcmp(s, "TIMEOUT") == 0) {
1046 ++timeout_next;
1047 return;
1048 }
1049
1050 if (strcmp(s, "ECHO") == 0) {
1051 ++echo_next;
1052 return;
1053 }
1054
1055 if (strcmp(s, "SAY") == 0) {
1056 ++say_next;
1057 return;
1058 }
1059
1060/*
1061 * Fetch the expect and reply string.
1062 */
1063 for (;;) {
1064 expect = expect_strtok (s, "-");
1065 warn("wangming: %s---->%d, expect is %s", __FILE__, __LINE__, expect);
1066 s = (char *) 0;
1067
1068 if (expect == (char *) 0)
1069 return;
1070
1071 reply = expect_strtok (s, "-");
1072 warn("wangming: %s---->%d, reply is %s", __FILE__, __LINE__, reply);
1073
1074/*
1075 * Handle the expect string. If successful then exit.
1076 */
1077 if (get_string (expect))
1078 return;
1079
1080/*
1081 * If there is a sub-reply string then send it. Otherwise any condition
1082 * is terminal.
1083 */
1084 if (reply == (char *) 0 || exit_code != 3)
1085 break;
1086
1087 chat_send (reply);
1088 }
1089
1090/*
1091 * The expectation did not occur. This is terminal.
1092 */
1093 if (fail_reason)
1094 warn("Failed (%s)", fail_reason);
1095 else
1096 warn("Failed");
1097 terminate(exit_code);
1098}
1099
1100/*
1101 * Translate the input character to the appropriate string for printing
1102 * the data.
1103 */
1104
1105char *character(c)
1106int c;
1107{
1108 static char string[10];
1109 char *meta;
1110
1111 meta = (c & 0x80) ? "M-" : "";
1112 c &= 0x7F;
1113
1114 if (c < 32)
1115 sprintf(string, "%s^%c", meta, (int)c + '@');
1116 else if (c == 127)
1117 sprintf(string, "%s^?", meta);
1118 else
1119 sprintf(string, "%s%c", meta, c);
1120
1121 return (string);
1122}
1123
1124/*
1125 * process the reply string
1126 */
1127void chat_send (s)
1128register char *s;
1129{
1130 char file_data[STR_LEN];
1131
1132 if (say_next) {
1133 say_next = 0;
1134 s = clean(s, 1);
1135 write(2, s, strlen(s));
1136 free(s);
1137 return;
1138 }
1139
1140 if (hup_next) {
1141 hup_next = 0;
1142 if (strcmp(s, "OFF") == 0)
1143 signal(SIGHUP, SIG_IGN);
1144 else
1145 signal(SIGHUP, sighup);
1146 return;
1147 }
1148
1149 if (echo_next) {
1150 echo_next = 0;
1151 echo = (strcmp(s, "ON") == 0);
1152 return;
1153 }
1154
1155 if (abort_next) {
1156 char *s1;
1157
1158 abort_next = 0;
1159
1160 if (n_aborts >= MAX_ABORTS)
1161 warn("Too many ABORT strings");
1162
1163 s1 = clean(s, 0);
1164
1165 if (strlen(s1) > strlen(s)
1166 || strlen(s1) + 1 > sizeof(fail_buffer))
1167 warn("Illegal or too-long ABORT string ('%v')", s);
1168
1169 abort_string[n_aborts++] = s1;
1170
1171 if (verbose)
1172 warn("abort on (%v)", s);
1173 return;
1174 }
1175
1176 if (clear_abort_next) {
1177 char *s1;
1178 int i;
1179 int old_max;
1180 int pack = 0;
1181
1182 clear_abort_next = 0;
1183
1184 s1 = clean(s, 0);
1185
1186 if (strlen(s1) > strlen(s)
1187 || strlen(s1) + 1 > sizeof(fail_buffer))
1188 warn("Illegal or too-long CLR_ABORT string ('%v')", s);
1189
1190 old_max = n_aborts;
1191 for (i=0; i < n_aborts; i++) {
1192 if ( strcmp(s1,abort_string[i]) == 0 ) {
1193 free(abort_string[i]);
1194 abort_string[i] = NULL;
1195 pack++;
1196 n_aborts--;
1197 if (verbose)
1198 warn("clear abort on (%v)", s);
1199 }
1200 }
1201 free(s1);
1202 if (pack)
1203 pack_array(abort_string,old_max);
1204 return;
1205 }
1206
1207 if (report_next) {
1208 char *s1;
1209
1210 report_next = 0;
1211 if (n_reports >= MAX_REPORTS)
1212 warn("Too many REPORT strings");
1213
1214 s1 = clean(s, 0);
1215
1216 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof(fail_buffer) - 1)
1217 warn("Illegal or too-long REPORT string ('%v')", s);
1218
1219 report_string[n_reports++] = s1;
1220
1221 if (verbose)
1222 warn("report (%v)", s);
1223 return;
1224 }
1225
1226 if (clear_report_next) {
1227 char *s1;
1228 int i;
1229 int old_max;
1230 int pack = 0;
1231
1232 clear_report_next = 0;
1233
1234 s1 = clean(s, 0);
1235
1236 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof(fail_buffer) - 1)
1237 warn("Illegal or too-long REPORT string ('%v')", s);
1238
1239 old_max = n_reports;
1240 for (i=0; i < n_reports; i++) {
1241 if ( strcmp(s1,report_string[i]) == 0 ) {
1242 free(report_string[i]);
1243 report_string[i] = NULL;
1244 pack++;
1245 n_reports--;
1246 if (verbose)
1247 warn("clear report (%v)", s);
1248 }
1249 }
1250 free(s1);
1251 if (pack)
1252 pack_array(report_string,old_max);
1253
1254 return;
1255 }
1256
1257 if (timeout_next) {
1258 timeout_next = 0;
1259 timeout = atoi(s);
1260
1261 if (timeout <= 0)
1262 timeout = DEFAULT_CHAT_TIMEOUT;
1263
1264 if (verbose)
1265 warn("timeout set to %d seconds", timeout);
1266
1267 return;
1268 }
1269
1270 /*
1271 * The syntax @filename means read the string to send from the
1272 * file `filename'.
1273 */
1274 if (s[0] == '@') {
1275 /* skip the @ and any following white-space */
1276 char *fn = s;
1277 while (*++fn == ' ' || *fn == '\t')
1278 ;
1279
1280 if (*fn != 0) {
1281 FILE *f;
1282 int n = 0;
1283
1284 /* open the file and read until STR_LEN-1 bytes or end-of-file */
1285 f = fopen(fn, "r");
1286 if (f == NULL)
1287 warn("%s -- open failed: %m", fn);
1288 while (n < STR_LEN - 1) {
1289 int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f);
1290 if (nr < 0)
1291 warn("%s -- read error", fn);
1292 if (nr == 0)
1293 break;
1294 n += nr;
1295 }
1296 fclose(f);
1297
1298 /* use the string we got as the string to send,
1299 but trim off the final newline if any. */
1300 if (n > 0 && file_data[n-1] == '\n')
1301 --n;
1302 file_data[n] = 0;
1303 s = file_data;
1304 }
1305 }
1306
1307 if (strcmp(s, "EOT") == 0)
1308 s = "^D\\c";
1309 else if (strcmp(s, "BREAK") == 0)
1310 s = "\\K\\c";
1311
1312 if (!put_string(s))
1313 warn("Failed");
1314}
1315
1316int get_char()
1317{
1318 int status;
1319 unsigned char c;
1320
1321 status = read(0, &c, 1);
1322
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 warn("warning: read() on stdin returned %d", status);
1336
1337 case -1:
1338 if ((status = fcntl(0, F_GETFL, 0)) == -1)
1339 warn("Can't get file mode flags on stdin: %m");
1340
1341 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1342 warn("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);
1357
1358 switch (status) {
1359 case 1:
1360 return (0);
1361
1362 default:
1363 warn("warning: write() on stdout returned %d", status);
1364
1365 case -1:
1366 if ((status = fcntl(0, F_GETFL, 0)) == -1)
1367 warn("Can't get file mode flags on stdin, %m");
1368
1369 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1370 warn("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 warn(" -- write timed out");
1386 else
1387 warn(" -- 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 warn("send (?????\?)");
1403 else
1404 warn("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 warn("wangming: %s---->%d, string is %s", __FILE__, __LINE__, string);
1490 if (verbose)
1491 {
1492 warn("expect (%v)", string);
1493 //warn("expect (%v)", string);
1494 }
1495 if (len > STR_LEN) {
1496 warn("expect string is too long");
1497 exit_code = 1;
1498 return 0;
1499 }
1500
1501 if (len == 0) {
1502 if (verbose)
1503 warn("got it");
1504 return (1);
1505 }
1506
1507 alarm(timeout);
1508 alarmed = 0;
1509
1510 while ( ! alarmed && (c = get_char()) >= 0) {
1511 int n, abort_len, report_len;
1512
1513 if (echo)
1514 echo_stderr(c);
1515 if (verbose && c == '\n') {
1516 if (s == logged)
1517 warn(""); /* blank line */
1518 else
1519 warn("%0.*v", s - logged, logged);
1520 logged = s + 1;
1521 }
1522
1523 *s++ = c;
1524
1525 if (verbose && s >= logged + 80) {
1526 warn("%0.*v", s - logged, logged);
1527 logged = s;
1528 }
1529
1530 if (Verbose) {
1531 if (c == '\n')
1532 fputc( '\n', stderr );
1533 else if (c != '\r')
1534 fprintf( stderr, "%s", character(c) );
1535 }
1536
1537 if (!report_gathering) {
1538 for (n = 0; n < n_reports; ++n) {
1539 if ((report_string[n] != (char*) NULL) &&
1540 s - temp >= (report_len = strlen(report_string[n])) &&
1541 strncmp(s - report_len, report_string[n], report_len) == 0) {
1542 time_t time_now = time ((time_t*) NULL);
1543 struct tm* tm_now = localtime (&time_now);
1544
1545 strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1546 strcat (report_buffer, report_string[n]);
1547
1548 report_string[n] = (char *) NULL;
1549 report_gathering = 1;
1550 break;
1551 }
1552 }
1553 }
1554 else {
1555 if (!iscntrl (c)) {
1556 int rep_len = strlen (report_buffer);
1557 report_buffer[rep_len] = c;
1558 report_buffer[rep_len + 1] = '\0';
1559 }
1560 else {
1561 report_gathering = 0;
1562 fprintf (report_fp, "chat: %s\n", report_buffer);
1563 }
1564 }
1565
1566 if (s - temp >= len &&
1567 c == string[len - 1] &&
1568 strncmp(s - len, string, len) == 0) {
1569 if (verbose) {
1570 if (s > logged)
1571 warn("%0.*v", s - logged, logged);
1572 warn(" -- got it\n");
1573 }
1574
1575 alarm(0);
1576 alarmed = 0;
1577 return (1);
1578 }
1579
1580 for (n = 0; n < n_aborts; ++n) {
1581 if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1582 strncmp(s - abort_len, abort_string[n], abort_len) == 0) {
1583 if (verbose) {
1584 if (s > logged)
1585 warn("%0.*v", s - logged, logged);
1586 warn(" -- failed");
1587 }
1588
1589 alarm(0);
1590 alarmed = 0;
1591 exit_code = n + 4;
1592 strcpy(fail_reason = fail_buffer, abort_string[n]);
1593 return (0);
1594 }
1595 }
1596
1597 if (s >= end) {
1598 if (logged < s - minlen) {
1599 if (verbose)
1600 warn("%0.*v", s - logged, logged);
1601 logged = s;
1602 }
1603 s -= minlen;
1604 memmove(temp, s, minlen);
1605 logged = temp + (logged - s);
1606 s = temp + minlen;
1607 }
1608
1609 if (alarmed && verbose)
1610 warn("warning: alarm synchronization problem");
1611 }
1612
1613 alarm(0);
1614
1615 if (verbose && printed) {
1616 if (alarmed)
1617 warn(" -- read timed out");
1618 else
1619 warn(" -- read failed: %m");
1620 }
1621
1622 exit_code = 3;
1623 alarmed = 0;
1624 return (0);
1625}
1626
1627/*
1628 * Gross kludge to handle Solaris versions >= 2.6 having usleep.
1629 */
1630#ifdef SOL2
1631#include <sys/param.h>
1632#if MAXUID > 65536 /* then this is Solaris 2.6 or later */
1633#undef NO_USLEEP
1634#endif
1635#endif /* SOL2 */
1636
1637#ifdef NO_USLEEP
1638#include <sys/types.h>
1639#include <sys/time.h>
1640
1641/*
1642 usleep -- support routine for 4.2BSD system call emulations
1643 last edit: 29-Oct-1984 D A Gwyn
1644 */
1645
1646extern int select();
1647
1648int
1649usleep( usec ) /* returns 0 if ok, else -1 */
1650 long usec; /* delay in microseconds */
1651{
1652 static struct { /* `timeval' */
1653 long tv_sec; /* seconds */
1654 long tv_usec; /* microsecs */
1655 } delay; /* _select() timeout */
1656
1657 delay.tv_sec = usec / 1000000L;
1658 delay.tv_usec = usec % 1000000L;
1659
1660 return select(0, (long *)0, (long *)0, (long *)0, &delay);
1661}
1662#endif
1663
1664void
1665pack_array (array, end)
1666 char **array; /* The address of the array of string pointers */
1667 int end; /* The index of the next free entry before CLR_ */
1668{
1669 int i, j;
1670
1671 for (i = 0; i < end; i++) {
1672 if (array[i] == NULL) {
1673 for (j = i+1; j < end; ++j)
1674 if (array[j] != NULL)
1675 array[i++] = array[j];
1676 for (; i < end; ++i)
1677 array[i] = NULL;
1678 break;
1679 }
1680 }
1681}
1682
1683/*
1684 * vfmtmsg - format a message into a buffer. Like vsprintf except we
1685 * also specify the length of the output buffer, and we handle the
1686 * %m (error message) format.
1687 * Doesn't do floating-point formats.
1688 * Returns the number of chars put into buf.
1689 */
1690#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
1691
1692int
1693vfmtmsg(buf, buflen, fmt, args)
1694 char *buf;
1695 int buflen;
1696 const char *fmt;
1697 va_list args;
1698{
1699 int c, i, n;
1700 int width, prec, fillch;
1701 int base, len, neg, quoted;
1702 unsigned long val = 0;
1703 char *str, *buf0;
1704 const char *f;
1705 unsigned char *p;
1706 char num[32];
1707 static char hexchars[] = "0123456789abcdef";
1708
1709 buf0 = buf;
1710 --buflen;
1711 while (buflen > 0) {
1712 for (f = fmt; *f != '%' && *f != 0; ++f)
1713 ;
1714 if (f > fmt) {
1715 len = f - fmt;
1716 if (len > buflen)
1717 len = buflen;
1718 memcpy(buf, fmt, len);
1719 buf += len;
1720 buflen -= len;
1721 fmt = f;
1722 }
1723 if (*fmt == 0)
1724 break;
1725 c = *++fmt;
1726 width = prec = 0;
1727 fillch = ' ';
1728 if (c == '0') {
1729 fillch = '0';
1730 c = *++fmt;
1731 }
1732 if (c == '*') {
1733 width = va_arg(args, int);
1734 c = *++fmt;
1735 } else {
1736 while (isdigit(c)) {
1737 width = width * 10 + c - '0';
1738 c = *++fmt;
1739 }
1740 }
1741 if (c == '.') {
1742 c = *++fmt;
1743 if (c == '*') {
1744 prec = va_arg(args, int);
1745 c = *++fmt;
1746 } else {
1747 while (isdigit(c)) {
1748 prec = prec * 10 + c - '0';
1749 c = *++fmt;
1750 }
1751 }
1752 }
1753 str = 0;
1754 base = 0;
1755 neg = 0;
1756 ++fmt;
1757 switch (c) {
1758 case 'd':
1759 i = va_arg(args, int);
1760 if (i < 0) {
1761 neg = 1;
1762 val = -i;
1763 } else
1764 val = i;
1765 base = 10;
1766 break;
1767 case 'o':
1768 val = va_arg(args, unsigned int);
1769 base = 8;
1770 break;
1771 case 'x':
1772 val = va_arg(args, unsigned int);
1773 base = 16;
1774 break;
1775 case 'p':
1776 val = (unsigned long) va_arg(args, void *);
1777 base = 16;
1778 neg = 2;
1779 break;
1780 case 's':
1781 str = va_arg(args, char *);
1782 break;
1783 case 'c':
1784 num[0] = va_arg(args, int);
1785 num[1] = 0;
1786 str = num;
1787 break;
1788 case 'm':
1789 str = strerror(errno);
1790 break;
1791 case 'v': /* "visible" string */
1792 case 'q': /* quoted string */
1793 quoted = c == 'q';
1794 p = va_arg(args, unsigned char *);
1795 if (fillch == '0' && prec > 0) {
1796 n = prec;
1797 } else {
1798 n = strlen((char *)p);
1799 if (prec > 0 && prec < n)
1800 n = prec;
1801 }
1802 while (n > 0 && buflen > 0) {
1803 c = *p++;
1804 --n;
1805 if (!quoted && c >= 0x80) {
1806 OUTCHAR('M');
1807 OUTCHAR('-');
1808 c -= 0x80;
1809 }
1810 if (quoted && (c == '"' || c == '\\'))
1811 OUTCHAR('\\');
1812 if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
1813 if (quoted) {
1814 OUTCHAR('\\');
1815 switch (c) {
1816 case '\t': OUTCHAR('t'); break;
1817 case '\n': OUTCHAR('n'); break;
1818 case '\b': OUTCHAR('b'); break;
1819 case '\f': OUTCHAR('f'); break;
1820 default:
1821 OUTCHAR('x');
1822 OUTCHAR(hexchars[c >> 4]);
1823 OUTCHAR(hexchars[c & 0xf]);
1824 }
1825 } else {
1826 if (c == '\t')
1827 OUTCHAR(c);
1828 else {
1829 OUTCHAR('^');
1830 OUTCHAR(c ^ 0x40);
1831 }
1832 }
1833 } else
1834 OUTCHAR(c);
1835 }
1836 continue;
1837 default:
1838 *buf++ = '%';
1839 if (c != '%')
1840 --fmt; /* so %z outputs %z etc. */
1841 --buflen;
1842 continue;
1843 }
1844 if (base != 0) {
1845 str = num + sizeof(num);
1846 *--str = 0;
1847 while (str > num + neg) {
1848 *--str = hexchars[val % base];
1849 val = val / base;
1850 if (--prec <= 0 && val == 0)
1851 break;
1852 }
1853 switch (neg) {
1854 case 1:
1855 *--str = '-';
1856 break;
1857 case 2:
1858 *--str = 'x';
1859 *--str = '0';
1860 break;
1861 }
1862 len = num + sizeof(num) - 1 - str;
1863 } else {
1864 len = strlen(str);
1865 if (prec > 0 && len > prec)
1866 len = prec;
1867 }
1868 if (width > 0) {
1869 if (width > buflen)
1870 width = buflen;
1871 if ((n = width - len) > 0) {
1872 buflen -= n;
1873 for (; n > 0; --n)
1874 *buf++ = fillch;
1875 }
1876 }
1877 if (len > buflen)
1878 len = buflen;
1879 memcpy(buf, str, len);
1880 buf += len;
1881 buflen -= len;
1882 }
1883 *buf = 0;
1884 return buf - buf0;
1885}
1886
1887
1888#ifdef ENABLE_NETWORK_SUPPORT
1889/*
1890 * Allow chat to chat to a network connection easily
1891 */
1892
1893#include <netdb.h>
1894#include <netinet/in.h>
1895#include <arpa/inet.h>
1896#include <sys/socket.h>
1897
1898void
1899net_open()
1900{
1901 struct sockaddr_in sock_in;
1902 struct hostent *host;
1903 int s;
1904
1905 bzero((char *)&sock_in, sizeof (sock_in));
1906 sock_in.sin_family = AF_INET;
1907 s = socket(AF_INET, SOCK_STREAM, 0);
1908 if (s == -1) {
1909 perror("socket");
1910 exit(1);
1911 }
1912
1913 if (bind(s, (struct sockaddr*)&sock_in, sizeof (sock_in)) == -1) {
1914 perror("bind");
1915 exit(2);
1916 }
1917
1918 host = gethostbyname(net_host);
1919 if (host) {
1920 sock_in.sin_family = host->h_addrtype;
1921 bcopy(host->h_addr, &sock_in.sin_addr, host->h_length);
1922 } else {
1923 sock_in.sin_family = AF_INET;
1924 sock_in.sin_addr.s_addr = inet_addr(net_host);
1925 if (sock_in.sin_addr.s_addr == -1) {
1926 fprintf(stderr, "%s: %s unknown host\n", program_name, net_host);
1927 exit(3);
1928 }
1929 }
1930
1931 sock_in.sin_port = htons(net_port);
1932
1933 if (connect(s, (struct sockaddr*)&sock_in, sizeof(sock_in)) == -1) {
1934 perror("connect:");
1935 exit(4);
1936 }
1937
1938 /*
1939 * make the socket stdin/stdout
1940 */
1941 if (s != 0) {
1942 dup2(s, 0);
1943 close(s);
1944 }
1945 dup2(0, 1);
1946}
1947
1948
1949/* we arrive here having already receive an IAC */
1950int
1951net_get_char(unsigned char *cp)
1952{
1953 int status;
1954 unsigned char cmd, option;
1955
1956 do {
1957 status = read(0, &cmd, 1);
1958 if (status <= 0)
1959 return(IAC);
1960
1961 if (cmd == IAC) /* escaped IAC */
1962 return(IAC);
1963
1964 status = read(0, &option, 1);
1965 if (status <= 0) {
1966 put_char(IAC);
1967 return(cmd);
1968 }
1969
1970 switch (cmd) {
1971 case WILL:
1972 put_char(IAC);
1973 put_char(DONT);
1974 put_char(option);
1975 break;
1976 case WONT:
1977 break;
1978 case DO:
1979 put_char(IAC);
1980 put_char(WONT);
1981 put_char(option);
1982 break;
1983 case DONT:
1984 break;
1985 default:
1986 put_char(IAC);
1987 put_char(cmd);
1988 return(option);
1989 }
1990
1991 /*
1992 * get next char
1993 */
1994 status = read(0, cp, 1);
1995 if (status <= 0)
1996 return(-1);
1997 } while (*cp == IAC);
1998
1999 return(*cp);
2000}
2001
2002#endif /* ENABLE_NETWORK_SUPPORT */