]> git.ozlabs.org Git - ppp.git/blobdiff - chat/chat.c
pppd: Fix variable reference syntax in Makefile.linux
[ppp.git] / chat / chat.c
index 409285666644e8cecef28e677a66d46e47de61b7..bf107335c8458fa296a75f171c967b6669fb4a17 100644 (file)
  *     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.19 1998/03/24 23:57:48 paulus Exp $";
+static const char rcsid[] = "$Id: chat.c,v 1.30 2004/01/17 05:47:55 carlsonj Exp $";
 #endif
 
 #include <stdio.h>
@@ -163,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;
@@ -193,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;
 
@@ -201,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));
@@ -250,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]] ...]]]
  *
@@ -274,6 +300,10 @@ main(argc, argv)
            ++echo;
            break;
 
+       case 'E':
+           ++use_env;
+           break;
+
        case 'v':
            ++verbose;
            break;
@@ -455,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);
 }
 
@@ -465,7 +495,7 @@ 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;
 
@@ -482,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);
 }
 
 /*
@@ -507,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);
 }
 
@@ -528,7 +560,7 @@ int signo;
        fatal(2, "Can't set file mode flags on stdin: %m");
 
     if (verbose)
-       logf("alarm");
+       msgf("alarm");
 }
 
 void unalarm()
@@ -606,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);
@@ -650,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 '\\':
@@ -702,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;
 
@@ -734,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;
@@ -772,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;
 }
 
 /*
@@ -930,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);
 }
 
@@ -966,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;
@@ -1006,7 +1081,7 @@ register char *s;
        abort_string[n_aborts++] = s1;
 
        if (verbose)
-           logf("abort on (%v)", s);
+           msgf("abort on (%v)", s);
        return;
     }
 
@@ -1032,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);
@@ -1049,14 +1124,14 @@ 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;
     }
 
@@ -1070,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;
@@ -1081,7 +1157,7 @@ register char *s;
                pack++;
                n_reports--;
                if (verbose)
-                   logf("clear report (%v)", s);
+                   msgf("clear report (%v)", s);
            }
        }
         free(s1);
@@ -1093,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)
@@ -1125,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)
@@ -1153,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)
@@ -1175,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);
     }
@@ -1192,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;
@@ -1281,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);
     }
 
@@ -1305,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;
        }
 
@@ -1359,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);
@@ -1373,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);
@@ -1387,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;
@@ -1397,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;