X-Git-Url: http://git.ozlabs.org/?p=ppp.git;a=blobdiff_plain;f=chat%2Fchat.c;h=bf107335c8458fa296a75f171c967b6669fb4a17;hp=006841359ee0b9a1848c9a296ee36c0514485ae2;hb=d00f8a0e184cc73b1ca33a9b22c1b1f87292a72f;hpb=42f98236389c506f0790b56d633bfbea3b12c20e diff --git a/chat/chat.c b/chat/chat.c index 0068413..bf10733 100644 --- a/chat/chat.c +++ b/chat/chat.c @@ -14,6 +14,12 @@ * This software is in the public domain. * * ----------------- + * 22-May-99 added environment substitutuion, enabled with -E switch. + * Andreas Arens . + * + * 12-May-99 added a feature to read data to be sent from a file, + * if the send string starts with @. Idea from gpk . + * * added -T and -U option and \T and \U substitution to pass a phone * number into chat script. Two are needed for some ISDN TA applications. * Keith Dart @@ -74,11 +80,14 @@ * Columbus, OH 43221 * (614)451-1883 * - * */ +#ifndef __STDC__ +#define const +#endif + #ifndef lint -static char rcsid[] = "$Id: chat.c,v 1.16 1997/11/27 06:00:06 paulus Exp $"; +static const char rcsid[] = "$Id: chat.c,v 1.30 2004/01/17 05:47:55 carlsonj Exp $"; #endif #include @@ -106,15 +115,6 @@ static char rcsid[] = "$Id: chat.c,v 1.16 1997/11/27 06:00:06 paulus Exp $"; #include #endif -#if __STDC__ -#include -#define __V(x) x -#else -#include -#define __V(x) (va_alist) va_dcl -#define const -#endif - #define STR_LEN 1024 #ifndef SIGTYPE @@ -139,9 +139,17 @@ static char rcsid[] = "$Id: chat.c,v 1.16 1997/11/27 06:00:06 paulus Exp $"; #define O_NONBLOCK O_NDELAY #endif +#ifdef SUNOS +extern int sys_nerr; +extern char *sys_errlist[]; +#define memmove(to, from, n) bcopy(from, to, n) +#define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\ + "unknown error") +#endif + /*************** Micro getopt() *********************************************/ #define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ - ((--c,++v),_O=4,c)&&**v=='-'&&v[0][1]?*++*v=='-'\ + (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) #define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ (_O=4,(char*)0):(char*)0) @@ -164,6 +172,7 @@ int to_stderr = 0; int Verbose = 0; int quiet = 0; int report = 0; +int use_env = 0; int exit_code = 0; FILE* report_fp = (FILE *) 0; char *report_file = (char *) 0; @@ -194,7 +203,7 @@ int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; int clear_abort_next = 0; char *report_string[MAX_REPORTS] ; -char report_buffer[50] ; +char report_buffer[256] ; int n_reports = 0, report_next = 0, report_gathering = 0 ; int clear_report_next = 0; @@ -202,8 +211,9 @@ int say_next = 0, hup_next = 0; void *dup_mem __P((void *b, size_t c)); void *copy_of __P((char *s)); +char *grow __P((char *s, char **p, size_t len)); void usage __P((void)); -void logf __P((const char *fmt, ...)); +void msgf __P((const char *fmt, ...)); void fatal __P((int code, const char *fmt, ...)); SIGTYPE sigalrm __P((int signo)); SIGTYPE sigint __P((int signo)); @@ -251,8 +261,23 @@ char *s; return dup_mem (s, strlen (s) + 1); } +/* grow a char buffer and keep a pointer offset */ +char *grow(s, p, len) +char *s; +char **p; +size_t len; +{ + size_t l = *p - s; /* save p as distance into s */ + + s = realloc(s, len); + if (!s) + fatal(2, "memory error!"); + *p = s + l; /* restore p */ + return s; +} + /* - * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \ + * chat [ -v ] [ -E ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \ * [ -r report-file ] \ * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] * @@ -275,6 +300,10 @@ main(argc, argv) ++echo; break; + case 'E': + ++use_env; + break; + case 'v': ++verbose; break; @@ -456,8 +485,8 @@ char *chat_file; void usage() { fprintf(stderr, "\ -Usage: %s [-e] [-v] [-t timeout] [-r report-file] [-T phone-number]\n\ - [-U phone-number2] {-f chat-file | chat-script}\n", program_name); +Usage: %s [-e] [-E] [-v] [-V] [-t timeout] [-r report-file]\n\ + [-T phone-number] [-U phone-number2] {-f chat-file | chat-script}\n", program_name); exit(1); } @@ -466,11 +495,11 @@ char line[1024]; /* * Send a message to syslog and/or stderr. */ -void logf __V((const char *fmt, ...)) +void msgf __V((const char *fmt, ...)) { va_list args; -#if __STDC__ +#ifdef __STDC__ va_start(args, fmt); #else char *fmt; @@ -483,6 +512,7 @@ void logf __V((const char *fmt, ...)) syslog(LOG_INFO, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); + va_end(args); } /* @@ -493,7 +523,7 @@ void fatal __V((int code, const char *fmt, ...)) { va_list args; -#if __STDC__ +#ifdef __STDC__ va_start(args, fmt); #else int code; @@ -508,6 +538,7 @@ void fatal __V((int code, const char *fmt, ...)) syslog(LOG_ERR, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); + va_end(args); terminate(code); } @@ -529,7 +560,7 @@ int signo; fatal(2, "Can't set file mode flags on stdin: %m"); if (verbose) - logf("alarm"); + msgf("alarm"); } void unalarm() @@ -607,27 +638,32 @@ void break_sequence() void terminate(status) int status; { + static int terminating = 0; + + if (terminating) + exit(status); + terminating = 1; echo_stderr(-1); - if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { /* * Allow the last of the report string to be gathered before we terminate. */ - if (report_gathering) { - int c, rep_len; + if (report_gathering) { + int c, rep_len; - rep_len = strlen(report_buffer); - while (rep_len + 1 <= sizeof(report_buffer)) { - alarm(1); - c = get_char(); - alarm(0); - if (c < 0 || iscntrl(c)) - break; - report_buffer[rep_len] = c; - ++rep_len; - } - report_buffer[rep_len] = 0; - fprintf (report_fp, "chat: %s\n", report_buffer); + rep_len = strlen(report_buffer); + while (rep_len + 1 <= sizeof(report_buffer)) { + alarm(1); + c = get_char(); + alarm(0); + if (c < 0 || iscntrl(c)) + break; + report_buffer[rep_len] = c; + ++rep_len; } + report_buffer[rep_len] = 0; + fprintf (report_fp, "chat: %s\n", report_buffer); + } + if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { if (verbose) fprintf (report_fp, "Closing \"%s\".\n", report_file); fclose (report_fp); @@ -651,51 +687,78 @@ char *clean(s, sending) register char *s; int sending; /* set to 1 when sending (putting) this string. */ { - char temp[STR_LEN], cur_chr; - register char *s1, *phchar; + char cur_chr; + char *s1, *p, *phchar; int add_return = sending; -#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) + size_t len = strlen(s) + 3; /* see len comments below */ + +#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) +#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \ + || (((chr) >= 'a') && ((chr) <= 'z')) \ + || (((chr) >= 'A') && ((chr) <= 'Z')) \ + || (chr) == '_') - s1 = temp; + p = s1 = malloc(len); + if (!p) + fatal(2, "memory error!"); while (*s) { cur_chr = *s++; if (cur_chr == '^') { cur_chr = *s++; if (cur_chr == '\0') { - *s1++ = '^'; + *p++ = '^'; break; } cur_chr &= 0x1F; if (cur_chr != 0) { - *s1++ = cur_chr; + *p++ = cur_chr; + } + continue; + } + + if (use_env && cur_chr == '$') { /* ARI */ + char c; + + phchar = s; + while (isalnumx(*s)) + s++; + c = *s; /* save */ + *s = '\0'; + phchar = getenv(phchar); + *s = c; /* restore */ + if (phchar) { + len += strlen(phchar); + s1 = grow(s1, &p, len); + while (*phchar) + *p++ = *phchar++; } continue; } if (cur_chr != '\\') { - *s1++ = cur_chr; + *p++ = cur_chr; continue; } cur_chr = *s++; if (cur_chr == '\0') { if (sending) { - *s1++ = '\\'; - *s1++ = '\\'; + *p++ = '\\'; + *p++ = '\\'; /* +1 for len */ } break; } switch (cur_chr) { case 'b': - *s1++ = '\b'; + *p++ = '\b'; break; case 'c': if (sending && *s == '\0') add_return = 0; else - *s1++ = cur_chr; + *p++ = cur_chr; break; case '\\': @@ -703,30 +766,33 @@ int sending; /* set to 1 when sending (putting) this string. */ case 'p': case 'd': if (sending) - *s1++ = '\\'; - - *s1++ = cur_chr; + *p++ = '\\'; + *p++ = cur_chr; break; case 'T': if (sending && phone_num) { - for ( phchar = phone_num; *phchar != '\0'; phchar++) - *s1++ = *phchar; + len += strlen(phone_num); + s1 = grow(s1, &p, len); + for (phchar = phone_num; *phchar != '\0'; phchar++) + *p++ = *phchar; } else { - *s1++ = '\\'; - *s1++ = 'T'; + *p++ = '\\'; + *p++ = 'T'; } break; case 'U': if (sending && phone_num2) { - for ( phchar = phone_num2; *phchar != '\0'; phchar++) - *s1++ = *phchar; + len += strlen(phone_num2); + s1 = grow(s1, &p, len); + for (phchar = phone_num2; *phchar != '\0'; phchar++) + *p++ = *phchar; } else { - *s1++ = '\\'; - *s1++ = 'U'; + *p++ = '\\'; + *p++ = 'U'; } break; @@ -735,30 +801,37 @@ int sending; /* set to 1 when sending (putting) this string. */ break; case 'r': - *s1++ = '\r'; + *p++ = '\r'; break; case 'n': - *s1++ = '\n'; + *p++ = '\n'; break; case 's': - *s1++ = ' '; + *p++ = ' '; break; case 't': - *s1++ = '\t'; + *p++ = '\t'; break; case 'N': if (sending) { - *s1++ = '\\'; - *s1++ = '\0'; + *p++ = '\\'; + *p++ = '\0'; } else - *s1++ = 'N'; + *p++ = 'N'; break; - + + case '$': /* ARI */ + if (use_env) { + *p++ = cur_chr; + break; + } + /* FALL THROUGH */ + default: if (isoctal (cur_chr)) { cur_chr &= 0x07; @@ -773,25 +846,24 @@ int sending; /* set to 1 when sending (putting) this string. */ if (cur_chr != 0 || sending) { if (sending && (cur_chr == '\\' || cur_chr == 0)) - *s1++ = '\\'; - *s1++ = cur_chr; + *p++ = '\\'; + *p++ = cur_chr; } break; } if (sending) - *s1++ = '\\'; - *s1++ = cur_chr; + *p++ = '\\'; + *p++ = cur_chr; break; } } if (add_return) - *s1++ = '\r'; + *p++ = '\r'; /* +2 for len */ - *s1++ = '\0'; /* guarantee closure */ - *s1++ = '\0'; /* terminate the string */ - return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ + *p = '\0'; /* +3 for len */ + return s1; } /* @@ -931,9 +1003,9 @@ char *s; * The expectation did not occur. This is terminal. */ if (fail_reason) - logf("Failed (%s)", fail_reason); + msgf("Failed (%s)", fail_reason); else - logf("Failed"); + msgf("Failed"); terminate(exit_code); } @@ -967,9 +1039,11 @@ int c; void chat_send (s) register char *s; { + char file_data[STR_LEN]; + if (say_next) { say_next = 0; - s = clean(s,0); + s = clean(s, 1); write(2, s, strlen(s)); free(s); return; @@ -1007,13 +1081,12 @@ register char *s; abort_string[n_aborts++] = s1; if (verbose) - logf("abort on (%v)", s); + msgf("abort on (%v)", s); return; } if (clear_abort_next) { char *s1; - char *s2 = s; int i; int old_max; int pack = 0; @@ -1034,7 +1107,7 @@ register char *s; pack++; n_aborts--; if (verbose) - logf("clear abort on (%v)", s); + msgf("clear abort on (%v)", s); } } free(s1); @@ -1051,20 +1124,19 @@ register char *s; fatal(2, "Too many REPORT strings"); s1 = clean(s, 0); - - if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) + if (strlen(s1) > strlen(s) + || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long REPORT string ('%v')", s); report_string[n_reports++] = s1; if (verbose) - logf("report (%v)", s); + msgf("report (%v)", s); return; } if (clear_report_next) { char *s1; - char *s2 = s; int i; int old_max; int pack = 0; @@ -1073,7 +1145,8 @@ register char *s; s1 = clean(s, 0); - if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) + if (strlen(s1) > strlen(s) + || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long REPORT string ('%v')", s); old_max = n_reports; @@ -1084,7 +1157,7 @@ register char *s; pack++; n_reports--; if (verbose) - logf("clear report (%v)", s); + msgf("clear report (%v)", s); } } free(s1); @@ -1096,17 +1169,55 @@ register char *s; if (timeout_next) { timeout_next = 0; + s = clean(s, 0); timeout = atoi(s); if (timeout <= 0) timeout = DEFAULT_CHAT_TIMEOUT; if (verbose) - logf("timeout set to %d seconds", timeout); + msgf("timeout set to %d seconds", timeout); return; } + /* + * The syntax @filename means read the string to send from the + * file `filename'. + */ + if (s[0] == '@') { + /* skip the @ and any following white-space */ + char *fn = s; + while (*++fn == ' ' || *fn == '\t') + ; + + if (*fn != 0) { + FILE *f; + int n = 0; + + /* open the file and read until STR_LEN-1 bytes or end-of-file */ + f = fopen(fn, "r"); + if (f == NULL) + fatal(1, "%s -- open failed: %m", fn); + while (n < STR_LEN - 1) { + int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f); + if (nr < 0) + fatal(1, "%s -- read error", fn); + if (nr == 0) + break; + n += nr; + } + fclose(f); + + /* use the string we got as the string to send, + but trim off the final newline if any. */ + if (n > 0 && file_data[n-1] == '\n') + --n; + file_data[n] = 0; + s = file_data; + } + } + if (strcmp(s, "EOT") == 0) s = "^D\\c"; else if (strcmp(s, "BREAK") == 0) @@ -1128,7 +1239,7 @@ int get_char() return ((int)c & 0x7F); default: - logf("warning: read() on stdin returned %d", status); + msgf("warning: read() on stdin returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) @@ -1156,7 +1267,7 @@ int c; return (0); default: - logf("warning: write() on stdout returned %d", status); + msgf("warning: write() on stdout returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) @@ -1178,9 +1289,9 @@ int c; if (verbose) { if (errno == EINTR || errno == EWOULDBLOCK) - logf(" -- write timed out"); + msgf(" -- write timed out"); else - logf(" -- write failed: %m"); + msgf(" -- write failed: %m"); } return (0); } @@ -1195,9 +1306,9 @@ register char *s; if (verbose) { if (quiet) - logf("send (??????)"); + msgf("send (?????\?)"); else - logf("send (%v)", s); + msgf("send (%v)", s); } alarm(timeout); alarmed = 0; @@ -1284,17 +1395,17 @@ register char *string; minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; if (verbose) - logf("expect (%v)", string); + msgf("expect (%v)", string); if (len > STR_LEN) { - logf("expect string is too long"); + msgf("expect string is too long"); exit_code = 1; return 0; } if (len == 0) { if (verbose) - logf("got it"); + msgf("got it"); return (1); } @@ -1308,16 +1419,16 @@ register char *string; echo_stderr(c); if (verbose && c == '\n') { if (s == logged) - logf(""); /* blank line */ + msgf(""); /* blank line */ else - logf("%0.*v", s - logged, logged); + msgf("%0.*v", s - logged, logged); logged = s + 1; } *s++ = c; if (verbose && s >= logged + 80) { - logf("%0.*v", s - logged, logged); + msgf("%0.*v", s - logged, logged); logged = s; } @@ -1362,8 +1473,8 @@ register char *string; strncmp(s - len, string, len) == 0) { if (verbose) { if (s > logged) - logf("%0.*v", s - logged, logged); - logf(" -- got it\n"); + msgf("%0.*v", s - logged, logged); + msgf(" -- got it\n"); } alarm(0); @@ -1376,8 +1487,8 @@ register char *string; strncmp(s - abort_len, abort_string[n], abort_len) == 0) { if (verbose) { if (s > logged) - logf("%0.*v", s - logged, logged); - logf(" -- failed"); + msgf("%0.*v", s - logged, logged); + msgf(" -- failed"); } alarm(0); @@ -1390,7 +1501,8 @@ register char *string; if (s >= end) { if (logged < s - minlen) { - logf("%0.*v", s - logged, logged); + if (verbose) + msgf("%0.*v", s - logged, logged); logged = s; } s -= minlen; @@ -1400,16 +1512,16 @@ register char *string; } if (alarmed && verbose) - logf("warning: alarm synchronization problem"); + msgf("warning: alarm synchronization problem"); } alarm(0); if (verbose && printed) { if (alarmed) - logf(" -- read timed out"); + msgf(" -- read timed out"); else - logf(" -- read failed: %m"); + msgf(" -- read failed: %m"); } exit_code = 3; @@ -1417,6 +1529,16 @@ register char *string; return (0); } +/* + * Gross kludge to handle Solaris versions >= 2.6 having usleep. + */ +#ifdef SOL2 +#include +#if MAXUID > 65536 /* then this is Solaris 2.6 or later */ +#undef NO_USLEEP +#endif +#endif /* SOL2 */ + #ifdef NO_USLEEP #include #include @@ -1487,7 +1609,6 @@ vfmtmsg(buf, buflen, fmt, args) const char *f; unsigned char *p; char num[32]; - time_t t; static char hexchars[] = "0123456789abcdef"; buf0 = buf;