* This software is in the public domain.
*
* -----------------
+ * 22-May-99 added environment substitutuion, enabled with -E switch.
+ * Andreas Arens <andras@cityweb.de>.
+ *
+ * 12-May-99 added a feature to read data to be sent from a file,
+ * if the send string starts with @. Idea from gpk <gpk@onramp.net>.
+ *
* 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 <kdart@cisco.com>
* 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 <stdio.h>
#include <termios.h>
#endif
-#if __STDC__
-#include <stdarg.h>
-#define __V(x) x
-#else
-#include <varargs.h>
-#define __V(x) (va_alist) va_dcl
-#define const
-#endif
-
#define STR_LEN 1024
#ifndef SIGTYPE
#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)
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;
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;
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));
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]] ...]]]
*
++echo;
break;
+ case 'E':
+ ++use_env;
+ break;
+
case 'v':
++verbose;
break;
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);
}
/*
* 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;
{
va_list args;
-#if __STDC__
+#ifdef __STDC__
va_start(args, fmt);
#else
int code;
fatal(2, "Can't set file mode flags on stdin: %m");
if (verbose)
- logf("alarm");
+ msgf("alarm");
}
void unalarm()
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);
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 '\\':
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;
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;
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;
}
/*
* 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);
}
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;
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;
pack++;
n_aborts--;
if (verbose)
- logf("clear abort on (%v)", s);
+ msgf("clear abort on (%v)", s);
}
}
free(s1);
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;
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;
pack++;
n_reports--;
if (verbose)
- logf("clear report (%v)", s);
+ msgf("clear report (%v)", s);
}
}
free(s1);
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)
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)
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)
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);
}
if (verbose) {
if (quiet)
- logf("send (??????)");
+ msgf("send (??????)");
else
- logf("send (%v)", s);
+ msgf("send (%v)", s);
}
alarm(timeout); alarmed = 0;
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);
}
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;
}
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);
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);
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;
}
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;
return (0);
}
+/*
+ * Gross kludge to handle Solaris versions >= 2.6 having usleep.
+ */
+#ifdef SOL2
+#include <sys/param.h>
+#if MAXUID > 65536 /* then this is Solaris 2.6 or later */
+#undef NO_USLEEP
+#endif
+#endif /* SOL2 */
+
#ifdef NO_USLEEP
#include <sys/types.h>
#include <sys/time.h>
const char *f;
unsigned char *p;
char num[32];
- time_t t;
static char hexchars[] = "0123456789abcdef";
buf0 = buf;