* 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>.
*
* Columbus, OH 43221
* (614)451-1883
*
- *
*/
-#ifndef lint
-static const char rcsid[] = "$Id: chat.c,v 1.23 1999/08/13 01:54:32 paulus Exp $";
-#endif
-
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
+#include <stdarg.h>
#ifndef TERMIO
#undef TERMIOS
#define SIGTYPE void
#endif
-#undef __P
-#undef __V
-
-#ifdef __STDC__
-#include <stdarg.h>
-#define __V(x) x
-#define __P(x) x
-#else
-#include <varargs.h>
-#define __V(x) (va_alist) va_dcl
-#define __P(x) ()
-#define const
-#endif
-
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
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;
int say_next = 0, hup_next = 0;
-void *dup_mem __P((void *b, size_t c));
-void *copy_of __P((char *s));
-void usage __P((void));
-void logf __P((const char *fmt, ...));
-void fatal __P((int code, const char *fmt, ...));
-SIGTYPE sigalrm __P((int signo));
-SIGTYPE sigint __P((int signo));
-SIGTYPE sigterm __P((int signo));
-SIGTYPE sighup __P((int signo));
-void unalarm __P((void));
-void init __P((void));
-void set_tty_parameters __P((void));
-void echo_stderr __P((int));
-void break_sequence __P((void));
-void terminate __P((int status));
-void do_file __P((char *chat_file));
-int get_string __P((register char *string));
-int put_string __P((register char *s));
-int write_char __P((int c));
-int put_char __P((int c));
-int get_char __P((void));
-void chat_send __P((register char *s));
-char *character __P((int c));
-void chat_expect __P((register char *s));
-char *clean __P((register char *s, int sending));
-void break_sequence __P((void));
-void terminate __P((int status));
-void pack_array __P((char **array, int end));
-char *expect_strtok __P((char *, char *));
-int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */
-
-int main __P((int, char *[]));
-
-void *dup_mem(b, c)
-void *b;
-size_t c;
+void *dup_mem (void *b, size_t c);
+void *copy_of (char *s);
+char *grow (char *s, char **p, size_t len);
+void usage (void);
+void msgf (const char *fmt, ...);
+void fatal (int code, const char *fmt, ...);
+SIGTYPE sigalrm (int signo);
+SIGTYPE sigint (int signo);
+SIGTYPE sigterm (int signo);
+SIGTYPE sighup (int signo);
+void unalarm (void);
+void init (void);
+void set_tty_parameters (void);
+int echo_stderr (int);
+void break_sequence (void);
+void terminate (int status);
+void do_file (char *chat_file);
+int get_string (register char *string);
+int put_string (register char *s);
+int write_char (int c);
+int put_char (int c);
+int get_char (void);
+int chat_send (register char *s);
+char *character (int c);
+void chat_expect (register char *s);
+char *clean (register char *s, int sending);
+void break_sequence (void);
+void terminate (int status);
+void pack_array (char **array, int end);
+char *expect_strtok (char *, char *);
+int vfmtmsg (char *, int, const char *, va_list); /* vsprintf++ */
+
+int main (int, char *[]);
+
+void *dup_mem(void *b, size_t c)
{
void *ans = malloc (c);
if (!ans)
return ans;
}
-void *copy_of (s)
-char *s;
+void *copy_of (char *s)
{
return dup_mem (s, strlen (s) + 1);
}
+/* grow a char buffer and keep a pointer offset */
+char *grow(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]] ...]]]
*
* Perform a UUCP-dialer-like chat script on stdin and stdout.
*/
int
-main(argc, argv)
- int argc;
- char **argv;
+main(int argc, char **argv)
{
int option;
char *arg;
++echo;
break;
+ case 'E':
+ ++use_env;
+ break;
+
case 'v':
++verbose;
break;
* Process a chat script when read from a file.
*/
-void do_file (chat_file)
-char *chat_file;
+void do_file (char *chat_file)
{
int linect, sendflg;
char *sp, *arg, quote;
/*
* We got an error parsing the command line.
*/
-void usage()
+void usage(void)
{
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(const char *fmt, ...)
{
va_list args;
-#ifdef __STDC__
va_start(args, fmt);
-#else
- char *fmt;
- va_start(args);
- fmt = va_arg(args, char *);
-#endif
vfmtmsg(line, sizeof(line), fmt, args);
if (to_log)
syslog(LOG_INFO, "%s", line);
if (to_stderr)
fprintf(stderr, "%s\n", line);
+ va_end(args);
}
/*
* Print an error message and terminate.
*/
-void fatal __V((int code, const char *fmt, ...))
+void fatal(int code, const char *fmt, ...)
{
va_list args;
-#ifdef __STDC__
va_start(args, fmt);
-#else
- int code;
- char *fmt;
- va_start(args);
- code = va_arg(args, int);
- fmt = va_arg(args, char *);
-#endif
vfmtmsg(line, sizeof(line), fmt, args);
if (to_log)
syslog(LOG_ERR, "%s", line);
if (to_stderr)
fprintf(stderr, "%s\n", line);
+ va_end(args);
terminate(code);
}
int alarmed = 0;
-SIGTYPE sigalrm(signo)
-int signo;
+SIGTYPE sigalrm(int signo)
{
int flags;
fatal(2, "Can't set file mode flags on stdin: %m");
if (verbose)
- logf("alarm");
+ msgf("alarm");
}
-void unalarm()
+void unalarm(void)
{
int flags;
fatal(2, "Can't set file mode flags on stdin: %m");
}
-SIGTYPE sigint(signo)
-int signo;
+SIGTYPE sigint(int signo)
{
fatal(2, "SIGINT");
}
-SIGTYPE sigterm(signo)
-int signo;
+SIGTYPE sigterm(int signo)
{
fatal(2, "SIGTERM");
}
-SIGTYPE sighup(signo)
-int signo;
+SIGTYPE sighup(int signo)
{
fatal(2, "SIGHUP");
}
-void init()
+void init(void)
{
signal(SIGINT, sigint);
signal(SIGTERM, sigterm);
alarmed = 0;
}
-void set_tty_parameters()
+void set_tty_parameters(void)
{
#if defined(get_term_param)
term_parms t;
#endif
}
-void break_sequence()
+void break_sequence(void)
{
#ifdef TERMIOS
tcsendbreak (0, 0);
#endif
}
-void terminate(status)
-int status;
+void terminate(int status)
{
static int terminating = 0;
/*
* 'Clean up' this string.
*/
-char *clean(s, sending)
-register char *s;
-int sending; /* set to 1 when sending (putting) this string. */
+char *clean(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 */
- s1 = temp;
+#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
+#define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \
+ || (((chr) >= 'a') && ((chr) <= 'z')) \
+ || (((chr) >= 'A') && ((chr) <= 'Z')) \
+ || (chr) == '_')
+
+ 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;
}
/*
* A modified version of 'strtok'. This version skips \ sequences.
*/
-char *expect_strtok (s, term)
- char *s, *term;
+char *expect_strtok (char *s, char *term)
{
static char *str = "";
int escape_flag = 0;
* Process the expect string
*/
-void chat_expect (s)
-char *s;
+void chat_expect (char *s)
{
char *expect;
char *reply;
* 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);
}
* the data.
*/
-char *character(c)
-int c;
+char *character(int c)
{
static char string[10];
char *meta;
/*
* process the reply string
*/
-void chat_send (s)
-register char *s;
+int chat_send (register char *s)
{
char file_data[STR_LEN];
+ int len, ret = 0;
if (say_next) {
say_next = 0;
- s = clean(s,0);
- write(2, s, strlen(s));
+ s = clean(s, 1);
+ len = strlen(s);
+ ret = write(2, s, len) != len;
free(s);
- return;
+ return ret;
}
if (hup_next) {
signal(SIGHUP, SIG_IGN);
else
signal(SIGHUP, sighup);
- return;
+ return 0;
}
if (echo_next) {
echo_next = 0;
echo = (strcmp(s, "ON") == 0);
- return;
+ return 0;
}
if (abort_next) {
s1 = clean(s, 0);
- if (strlen(s1) > strlen(s)
- || strlen(s1) + 1 > sizeof(fail_buffer))
+ if (strlen(s1) + 1 > sizeof(fail_buffer))
fatal(1, "Illegal or too-long ABORT string ('%v')", s);
abort_string[n_aborts++] = s1;
if (verbose)
- logf("abort on (%v)", s);
- return;
+ msgf("abort on (%v)", s1);
+ return 0;
}
if (clear_abort_next) {
s1 = clean(s, 0);
- if (strlen(s1) > strlen(s)
- || strlen(s1) + 1 > sizeof(fail_buffer))
+ if (strlen(s1) + 1 > sizeof(fail_buffer))
fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s);
old_max = n_aborts;
pack++;
n_aborts--;
if (verbose)
- logf("clear abort on (%v)", s);
+ msgf("clear abort on (%v)", s1);
}
}
free(s1);
if (pack)
pack_array(abort_string,old_max);
- return;
+ return 0;
}
if (report_next) {
fatal(2, "Too many REPORT strings");
s1 = clean(s, 0);
-
- if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ if (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);
- return;
+ msgf("report (%v)", s1);
+ return 0;
}
if (clear_report_next) {
s1 = clean(s, 0);
- if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ if (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)", s1);
}
}
free(s1);
if (pack)
pack_array(report_string,old_max);
- return;
+ return 0;
}
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;
+ return 0;
}
/*
if (!put_string(s))
fatal(1, "Failed");
+
+ return 0;
}
-int get_char()
+int get_char(void)
{
int status;
char c;
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)
}
}
-int put_char(c)
-int c;
+int put_char(int c)
{
int status;
char ch = 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)
}
}
-int write_char (c)
-int c;
+int write_char(int c)
{
if (alarmed || put_char(c) < 0) {
alarm(0);
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);
}
return (1);
}
-int put_string (s)
-register char *s;
+int put_string(register char *s)
{
quiet = 0;
s = clean(s, 1);
if (verbose) {
if (quiet)
- logf("send (??????)");
+ msgf("send (?????\?)");
else
- logf("send (%v)", s);
+ msgf("send (%v)", s);
}
alarm(timeout); alarmed = 0;
* When called with -1, a '\n' character is generated when
* the cursor is not at the beginning of a line.
*/
-void echo_stderr(n)
-int n;
+int echo_stderr(int n)
{
static int need_lf;
char *s;
+ int len, ret = 0;
switch (n) {
case '\r': /* ignore '\r' */
break;
/* fall through */
case '\n':
- write(2, "\n", 1);
+ ret = write(2, "\n", 1) != 1;
need_lf = 0;
break;
default:
s = character(n);
- write(2, s, strlen(s));
+ len = strlen(s);
+ ret = write(2, s, len) != len;
need_lf = 1;
break;
}
+ return ret;
}
/*
* 'Wait for' this string to appear on this file descriptor.
*/
-int get_string(string)
-register char *string;
+int get_string(register char *string)
{
char temp[STR_LEN];
int c, printed = 0, len, minlen;
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);
}
while ( ! alarmed && (c = get_char()) >= 0) {
int n, abort_len, report_len;
- if (echo)
- echo_stderr(c);
+ if (echo) {
+ if (echo_stderr(c) != 0) {
+ fatal(2, "Could not write to stderr, %m");
+ }
+ }
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;
extern int select();
-int
-usleep( usec ) /* returns 0 if ok, else -1 */
- long usec; /* delay in microseconds */
+/* returns 0 if ok, else -1 */
+int usleep(long usec) /* delay in microseconds */
{
static struct { /* `timeval' */
long tv_sec; /* seconds */
}
#endif
-void
-pack_array (array, end)
- char **array; /* The address of the array of string pointers */
- int end; /* The index of the next free entry before CLR_ */
+void pack_array (
+ char **array, /* The address of the array of string pointers */
+ int end) /* The index of the next free entry before CLR_ */
{
int i, j;
#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
int
-vfmtmsg(buf, buflen, fmt, args)
- char *buf;
- int buflen;
- const char *fmt;
- va_list args;
+vfmtmsg(char *buf, int buflen, const char *fmt, va_list args)
{
int c, i, n;
int width, prec, fillch;