summaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/contrib/xchat.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/libexec/uucp/contrib/xchat.c')
-rw-r--r--gnu/libexec/uucp/contrib/xchat.c1473
1 files changed, 1473 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/contrib/xchat.c b/gnu/libexec/uucp/contrib/xchat.c
new file mode 100644
index 00000000000..b44549e53ed
--- /dev/null
+++ b/gnu/libexec/uucp/contrib/xchat.c
@@ -0,0 +1,1473 @@
+/*
+ * ***********
+ * * XCHAT.C *
+ * ***********
+ *
+ * Extended chat processor for Taylor UUCP. See accompanying documentation.
+ *
+ * Written by:
+ * Bob Denny (denny@alisa.com)
+ * Based on code in DECUS UUCP (for VAX/VMS)
+ *
+ * Small modification by:
+ * Daniel Hagerty (hag@eddie.mit.edu)
+ *
+ * History:
+ * Version 1.0 shipped with Taylor 1.03. No configuration info inside.
+ *
+ * Bob Denny - Sun Aug 30 18:41:30 1992
+ * V1.1 - long overdue changes for other systems. Rip out interval
+ * timer code, use timer code from Taylor UUCP, use select()
+ * for timed reads. Use Taylor UUCP "conf.h" file to set
+ * configuration for this program. Add defaulting of script
+ * and log file paths.
+ *
+ * Daniel Hagerty - Mon Nov 22 18:17:38 1993
+ * V1.2 - Added a new opcode to xchat. "expectstr" is a cross between
+ * sendstr and expect, looking for a parameter supplied string.
+ * Useful where a prompt could change for different dial in
+ * lines and such.
+ *
+ * Bugs:
+ * Does not support BSD terminal I/O. Anyone care to add it?
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/termio.h>
+
+#include "xc-conf.h"
+
+/*
+ * Pick a timing routine to use, as done in Taylor UUCP.
+ */
+#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS || HAVE_POLL
+#define USE_SELECT_TIMER 0
+#else
+#define USE_SELECT_TIMER HAVE_SELECT
+#if USE_SELECT_TIMER
+#include <sys/time.h>
+#endif
+#endif
+
+#if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS
+#undef HAVE_POLL
+#define HAVE_POLL 0
+#endif
+
+#if HAVE_USLEEP || HAVE_NAP
+#undef HAVE_NAPMS
+#define HAVE_NAPMS 0
+#endif
+
+#if HAVE_USLEEP
+#undef HAVE_NAP
+#define HAVE_NAP 0
+#endif
+
+static int ttblind();
+static int ttcd();
+
+/* script entry -- "compiled" form of dial, hangup, or login script */
+
+struct script {
+ struct script *next; /* pointer to next entry, or null */
+ int opcode; /* numeric opcode */
+ char *strprm; /* pointer to string param */
+ long intprm; /* integer parameter */
+ char *newstate; /* new state name */
+};
+
+/* opcode definition array element -- one for each possible opcode */
+
+struct script_opdef {
+ char *opname;
+ int opcode; /* numeric opcode -- same as array index */
+ int prmtype; /* one of SC_NONE, SC_STR, SC_XSTR, SC_INT */
+ int newstate; /* one of SC_NONE, SC_NWST */
+};
+
+ /* values for opcode */
+
+#define SC_LABEL 0 /* "label" (state name) */
+#define SC_CDLY 1 /* set char output delay in msec */
+#define SC_PCHR 2 /* pause char for dial string (from P in input) */
+#define SC_PTIM 3 /* seconds to allow for pause char */
+#define SC_WCHR 4 /* wait char for dial string (from W in input) */
+#define SC_WTIM 5 /* seconds to allow for wait char */
+#define SC_ZERO 6 /* zero counter */
+#define SC_INCR 7 /* increment counter */
+#define SC_IFGT 8 /* change state if counter > int param */
+#define SC_WAIT 9 /* wait for int param seconds */
+#define SC_GOTO 10 /* unconditional change to new state */
+#define SC_SEND 11 /* send strparam (after sprintf substitutions) */
+#define SC_BRK 12 /* send a break */
+#define SC_HANG 13 /* drop DTR */
+#define SC_DIAL 14 /* send telno string (after subst PCHR & WCHR) */
+#define SC_DTIM 15 /* time in msec per digit (for timeout calculations) */
+ /* default = 100 (one tenth second) */
+#define SC_CTIM 16 /* additional time (in seconds) to wait for carrier */
+ /* default = 45 seconds */
+#define SC_EXIT 17 /* script done, success */
+#define SC_FAIL 18 /* script done, failure */
+#define SC_LOG 19 /* write strparam to uucp.log */
+#define SC_LOGE 20 /* write strparam to uucp.log w/error ind */
+#define SC_DBG 21 /* write strparam to debug log if debug lvl = LGI */
+#define SC_DBGE 22 /* write strparam to debug log if debug lvl = LGIE */
+#define SC_DBST 23 /* 'or' intparam into debug mask */
+#define SC_DBCL 24 /* 'bicl' intparam into debug mask */
+#define SC_TIMO 25 /* newstate if no match in intparam secs */
+ /* (uses calculated dial time if intparam is 0) */
+#define SC_XPCT 26 /* wait for strparam, goto _newstate if found */
+#define SC_CARR 27 /* goto _newstate if carrier detected */
+#define SC_FLSH 28 /* flush typeahead buffer */
+#define SC_IFBL 29 /* change state if controller is blind w/o CD */
+#define SC_IFBG 30 /* chg state if ctlr is blind and counter > intprm */
+#define SC_SNDP 31 /* send parameter n */
+#define SC_IF1P 32 /* if parameter n present */
+#define SC_IF0P 33 /* if parameter n absent */
+#define SC_DBOF 34 /* open debugging file */
+#define SC_TELN 35 /* Set telno from parameter n */
+#define SC_7BIT 36 /* Set port to 7-bit stripping */
+#define SC_8BIT 37 /* Set port for 8-bit characters */
+#define SC_PNON 38 /* Set port for 8-bit, no parity */
+#define SC_PEVN 39 /* Set port for 7-bit, even parity */
+#define SC_PODD 40 /* Set port for 7-bit, odd parity */
+#define SC_HUPS 41 /* Change state on HUP signal */
+#define SC_XPST 42 /* Expect a param string */
+#define SC_END 43 /* end of array */
+
+ /* values for prmtype, prm2type */
+
+#define SC_NONE 0 /* no parameter */
+#define SC_STR 1 /* simple string */
+#define SC_INT 2 /* integer */
+#define SC_NWST 3 /* new state name */
+#define SC_XSTR 4 /* translated string */
+
+/* opcode definition table for dial/login/hangup scripts */
+
+static struct script_opdef sc_opdef[] =
+ {
+ {"label", SC_LABEL, SC_NONE, SC_NONE},
+ {"chrdly", SC_CDLY, SC_INT, SC_NONE},
+ {"pchar", SC_PCHR, SC_STR, SC_NONE},
+ {"ptime", SC_PTIM, SC_INT, SC_NONE},
+ {"wchar", SC_WCHR, SC_STR, SC_NONE},
+ {"wtime", SC_WTIM, SC_INT, SC_NONE},
+ {"zero", SC_ZERO, SC_NONE, SC_NONE},
+ {"count", SC_INCR, SC_NONE, SC_NONE},
+ {"ifgtr", SC_IFGT, SC_INT, SC_NWST},
+ {"sleep", SC_WAIT, SC_INT, SC_NONE},
+ {"goto", SC_GOTO, SC_NONE, SC_NWST},
+ {"send", SC_SEND, SC_XSTR, SC_NONE},
+ {"break", SC_BRK, SC_NONE, SC_NONE},
+ {"hangup", SC_HANG, SC_NONE, SC_NONE},
+ {"7bit", SC_7BIT, SC_NONE, SC_NONE},
+ {"8bit", SC_8BIT, SC_NONE, SC_NONE},
+ {"nopar", SC_PNON, SC_NONE, SC_NONE},
+ {"evenpar", SC_PEVN, SC_NONE, SC_NONE},
+ {"oddpar", SC_PODD, SC_NONE, SC_NONE},
+ {"telno", SC_TELN, SC_INT, SC_NONE},
+ {"dial", SC_DIAL, SC_NONE, SC_NONE},
+ {"dgttime", SC_DTIM, SC_INT, SC_NONE},
+ {"ctime", SC_CTIM, SC_INT, SC_NONE},
+ {"success", SC_EXIT, SC_NONE, SC_NONE},
+ {"failed", SC_FAIL, SC_NONE, SC_NONE},
+ {"log", SC_LOG, SC_XSTR, SC_NONE},
+ {"logerr", SC_LOGE, SC_XSTR, SC_NONE},
+ {"debug", SC_DBG, SC_XSTR, SC_NONE},
+ {"debuge", SC_DBGE, SC_XSTR, SC_NONE},
+ {"dbgset", SC_DBST, SC_INT, SC_NONE},
+ {"dbgclr", SC_DBCL, SC_INT, SC_NONE},
+ {"dbgfile", SC_DBOF, SC_XSTR, SC_NONE},
+ {"timeout", SC_TIMO, SC_INT, SC_NWST},
+ {"expect", SC_XPCT, SC_XSTR, SC_NWST},
+ {"ifcarr", SC_CARR, SC_NONE, SC_NWST},
+ {"ifhang", SC_HUPS, SC_NONE, SC_NWST},
+ {"flush", SC_FLSH, SC_NONE, SC_NONE},
+ {"ifblind", SC_IFBL, SC_NONE, SC_NWST},
+ {"ifblgtr", SC_IFBG, SC_INT, SC_NWST},
+ {"sendstr", SC_SNDP, SC_INT, SC_NONE},
+ {"ifstr", SC_IF1P, SC_INT, SC_NWST},
+ {"ifnstr", SC_IF0P, SC_INT, SC_NWST},
+ {"expectstr", SC_XPST, SC_INT, SC_NWST},
+ {"table end", SC_END, SC_NONE, SC_NONE}
+ };
+
+#define SUCCESS 0
+#define FAIL 1
+#define ERROR -1
+#define MAX_SCLINE 255 /* max length of a line in a script file */
+#define MAX_EXPCT 127 /* max length of an expect string */
+#define CTL_DELIM " \t\n\r" /* Delimiters for tokens */
+#define SAME 0 /* if (strcmp(a,b) == SAME) ... */
+#define SLOP 10 /* Slop space on arrays */
+#define MAX_STRING 200 /* Max length string to send/expect */
+
+#define DEBUG_LEVEL(level) \
+ (Debug & (1 << level))
+
+#define DB_LOG 0 /* error messages and a copy of the LOGFILE output */
+#define DB_LGIE 1 /* dial,login,init trace -- errors only */
+#define DB_LGI 2 /* dial,login,init trace -- nonerrors (incl chr I/O) */
+#define DB_LGII 3 /* script processing internals */
+
+#define TRUE 1
+#define FALSE 0
+
+#define NONE 0
+#define EVEN 1
+#define ODD 2
+
+#define logit(m, p1) fprintf(stderr, "%s %s\n", m, p1)
+
+static char **paramv; /* Parameter vector */
+static int paramc; /* Parameter count */
+static char telno[64]; /* Telephone number w/meta-chars */
+static int Debug;
+static int fShangup = FALSE; /* TRUE if HUP signal received */
+static FILE *dbf = NULL;
+static struct termio old, new;
+
+extern int usignal();
+extern int uhup();
+
+static struct siglist
+{
+ int signal;
+ int (*o_catcher) ();
+ int (*n_catcher) ();
+} sigtbl[] = {
+ { SIGHUP, NULL, uhup },
+ { SIGINT, NULL, usignal },
+ { SIGIOT, NULL, usignal },
+ { SIGQUIT, NULL, usignal },
+ { SIGTERM, NULL, usignal },
+ { SIGALRM, NULL, usignal },
+ { 0, NULL, NULL } /* Table end */
+ };
+
+extern struct script *read_script();
+extern void msleep();
+extern char xgetc();
+extern void charlog();
+extern void setup_tty();
+extern void restore_tty();
+extern void ttoslow();
+extern void ttflui();
+extern void tthang();
+extern void ttbreak();
+extern void tt7bit();
+extern void ttpar();
+extern void DEBUG();
+
+extern void *malloc();
+
+
+/*
+ * **********************************
+ * * BEGIN EXECUTION - MAIN PROGRAM *
+ * **********************************
+ *
+ * This program is called by Taylor UUCP with a list of
+ * arguments in argc/argv, and stdin/stdout mapped to the
+ * tty device, and stderr mapped to the Taylor logfile, where
+ * anything written to stdout will be logged as an error.
+ *
+ */
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+ int i, stat;
+ FILE *sf;
+ char sfname[256];
+ struct script *script;
+ struct siglist *sigs;
+
+ /*
+ * The following is needed because my cpp does not have the
+ * #error directive...
+ */
+#if ! HAVE_SELECT
+ no_select_sorry(); /* Sad way to fail make */
+#endif
+
+ paramv = &argv[2]; /* Parameters start at 2nd arg */
+ paramc = argc - 2; /* Number of live parameters */
+
+ telno[0] = '\0';
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "%s: no script file supplied\n", argv[0]);
+ exit(FAIL);
+ }
+
+ /*
+ * If the script file argument begins with '/', then we assume
+ * it is an absolute pathname, otherwise, we prepend the
+ * SCRIPT_DIR path.
+ */
+ *sfname = '\0'; /* Empty name string */
+ if(argv[1][0] != '/') /* If relative path */
+ strcat(sfname, SCRIPT_DIR); /* Prepend the default dir. */
+ strcat(sfname, argv[1]); /* Add the script file name */
+
+ /*
+ * Now open the script file.
+ */
+ if ((sf = fopen(sfname, "r")) == NULL)
+ {
+ fprintf(stderr, "%s: Failed to open script %s\n", argv[0], sfname);
+ perror(" ");
+ exit(FAIL);
+ }
+
+ /*
+ * COMPILE SCRIPT
+ */
+ if ((script = read_script(sf)) == NULL)
+ {
+ fprintf(stderr, "%s: script error in \"%s\"\n", argv[0], argv[1]);
+ exit(FAIL);
+ }
+
+ /*
+ * Set up a signal catcher so the line can be returned to
+ * it's current state if something nasty happens.
+ */
+ sigs = &sigtbl[0];
+ while(sigs->signal)
+ {
+ sigs->o_catcher = (int (*) ())signal(sigs->signal, sigs->n_catcher);
+ sigs += 1;
+ }
+
+ /*
+ * Save current tty settings, then set up raw, single
+ * character input processing, with 7-bit stripping.
+ */
+ setup_tty();
+
+ /*
+ * EXECUTE SCRIPT
+ */
+ if ((stat = do_script(script)) != SUCCESS)
+ fprintf(stderr, "%s: script %s failed.\n", argv[0], argv[1]);
+
+ /*
+ * Clean up and exit.
+ */
+ restore_tty();
+#ifdef FIXSIGS
+ sigs = &sigtbl[0];
+ while(sigs->signal)
+ if(sigs->o_catcher != -1)
+ signal(sigs->signal, sigs->o_catcher);
+#endif
+ exit(stat);
+}
+
+/*
+ * deal_script - deallocate a script and all strings it points to
+ */
+int deal_script(loc)
+struct script *loc;
+{
+ /*
+ * If pointer is null, just exit
+ */
+ if (loc == (struct script *)NULL)
+ return SUCCESS;
+
+ /*
+ * Deallocate the rest of the script
+ */
+ deal_script(loc->next);
+
+ /*
+ * Deallocate the string parameter, if any
+ */
+ if (loc->strprm != (char *)NULL)
+ free(loc->strprm);
+
+ /*
+ * Deallocate the new state name parameter, if any
+ */
+ if (loc->newstate != (char *)NULL)
+ free(loc->newstate);
+
+ /*
+ * Deallocate this entry
+ */
+ free(loc);
+
+ return SUCCESS;
+}
+
+
+/*
+ * read_script
+ *
+ * Read & compile a script, return pointer to first entry, or null if bad
+ */
+struct script *read_script(fd)
+ FILE *fd;
+{
+ struct script *this = NULL;
+ struct script *prev = NULL;
+ struct script *first = NULL;
+ long len, i;
+ char inpline[MAX_SCLINE];
+ char inpcopy[MAX_SCLINE];
+ char *c, *cln, *opc, *cp;
+
+ /*
+ * MAIN COMPILATION LOOP
+ */
+ while ((c = fgets(inpline, (sizeof inpline - 1), fd)) != (char *)NULL)
+ {
+ /*
+ * Skip comments and blank lines
+ */
+ if (*c == '#' || *c == '\n')
+ continue;
+
+ /*
+ * Get rid of the trailing newline, and copy the string
+ */
+ inpline[strlen(inpline)-1] = '\0';
+ strcpy(inpcopy, inpline);
+
+ /*
+ * Look for text starting in the first col (a label)
+ */
+ if ((!isspace(inpline[0])) &&
+ (cln = strchr (inpline, ':')) != (char *)NULL) {
+ this = (struct script *)malloc (sizeof (struct script));
+ if (prev != (struct script *)NULL)
+ prev->next = this;
+ prev = this;
+ if (first == (struct script *)NULL)
+ first = this;
+ this->next = (struct script *)NULL;
+ this->opcode = SC_LABEL;
+ len = cln - c;
+ this->strprm = (char *)malloc(len+1);
+ strncpy(this->strprm, c, len);
+ (this->strprm)[len] = '\0';
+ this->intprm = 0;
+ this->newstate = (char *)NULL;
+ c = cln + 1;
+ }
+
+ /*
+ * Now handle the opcode. Fold it to lower case.
+ */
+ opc = strtok(c, CTL_DELIM);
+ if (opc == (char *)NULL) /* If no opcode... */
+ continue; /* ...read the next line */
+ cp = opc;
+ while(*cp)
+ tolower(*cp++);
+
+ /*
+ * If we have an opcode but we haven't seen anything
+ * else (like a label) yet, i.e., this is the first
+ * entry, and there was no label. We need to
+ * cobble up a label so that read_script is happy
+ */
+ if (first == (struct script *)NULL)
+ {
+ this = (struct script *)malloc (sizeof (struct script));
+ prev = this;
+ first = this;
+ this->next = (struct script *)NULL;
+ this->opcode = SC_LABEL;
+ this->strprm = (char *)malloc(2);
+ strcpy(this->strprm, ":");
+ this->intprm = 0;
+ this->newstate = (char *)NULL;
+ }
+
+ /*
+ * Find opcode - ndex through the opcode definition table
+ */
+ for (i=1; sc_opdef[i].opcode != SC_END; i++)
+ if (strcmp(opc, sc_opdef[i].opname) == SAME)
+ break;
+ if ((sc_opdef[i].opcode) == SC_END)
+ {
+ logit ("Bad opcode in script", opc);
+ deal_script(first);
+ return (struct script *)NULL;
+ }
+
+ /*
+ * Found opcode. Allocate a new command node and initialize
+ */
+ this = (struct script *)malloc(sizeof (struct script));
+ prev->next = this;
+ prev = this;
+ this->next = (struct script *)NULL;
+ this->opcode = sc_opdef[i].opcode;
+ this->strprm = (char *)NULL;
+ this->intprm = 0;
+ this->newstate = (char *)NULL;
+
+ /*
+ * Pick up new state parameter, if any
+ */
+ if (sc_opdef[i].newstate == SC_NWST)
+ {
+ c = strtok((char *)NULL, CTL_DELIM);
+ if (c == (char *)NULL)
+ {
+ logit("Missing new state", opc);
+ deal_script(first);
+ return (struct script *)NULL;
+ }
+ else
+ {
+ this->newstate = (char *)malloc(strlen(c)+1);
+ strcpy(this->newstate, c);
+ }
+ }
+
+ /*
+ * Pick up the string or integer parameter. Handle missing
+ * parameter gracefully.
+ */
+ switch (sc_opdef[i].prmtype)
+ {
+ /*
+ * INT parameter - convert and store in node
+ */
+ case SC_INT:
+ c = strtok((char *)NULL, CTL_DELIM);
+ if (c == (char *)NULL)
+ {
+ logit("Missing script param", opc);
+ deal_script(first);
+ return (struct script *)NULL;
+ }
+ /*
+ * If this is the parameter to DBST or DBCL, force
+ * base-10 conversion, else convert per parameter.
+ */
+ if (sc_opdef[i].opcode == SC_DBST ||
+ sc_opdef[i].opcode == SC_DBCL)
+ this->intprm = strtol(c, (char **)NULL, 0);
+ else
+ this->intprm = strtol(c, (char **)NULL, 10);
+ break;
+
+ /*
+ * STR/XSTR strings.
+ */
+ case SC_STR:
+ case SC_XSTR:
+ c = strtok((char *)NULL, CTL_DELIM);
+ if (c == (char *)NULL)
+ {
+ logit("Missing script param", opc);
+ deal_script(first);
+ return (struct script *)NULL;
+ }
+ /*
+ * For XSTR opcode, use c to find out where
+ * the string param begins in the copy of the
+ * input line, and pick up all that's left of
+ * the line (to allow imbedded blanks, etc.).
+ */
+ if (sc_opdef[i].prmtype == SC_XSTR)
+ c = &inpcopy[0] + (c - &inpline[0]);
+
+ /*
+ * Allocate a buffer for the string parameter
+ */
+ this->strprm = (char *)malloc(strlen(c)+1);
+
+ /*
+ * For XSTR, Translate the string and store its
+ * length. Note that, after escape sequences are
+ * compressed, the resulting string may well be a
+ * few bytes shorter than the input string (whose
+ * length was the basis for the malloc above),
+ * but it will never be longer.
+ */
+ if (sc_opdef[i].prmtype == SC_XSTR)
+ {
+ this->intprm = xlat_str(this->strprm, c);
+ this->strprm[this->intprm] = '\0';
+ }
+ else
+ strcpy(this->strprm, c);
+ break;
+
+ }
+ }
+
+ /*
+ * EOF
+ */
+ return first;
+}
+
+
+/*
+ * xlat_str
+ *
+ * Translate embedded escape characters in a "send" or "expect" string.
+ *
+ * Called by read_script(), above.
+ *
+ * Returns the actual length of the resulting string. Note that imbedded
+ * nulls (specified by \000 in the input) ARE allowed in the result.
+ */
+xlat_str(out, in)
+ char *out, *in;
+{
+ register int i = 0, j = 0;
+ int byte, k;
+
+ while (in[i])
+ {
+ if (in[i] != '\\')
+ {
+ out[j++] = in[i++];
+ }
+ else
+ {
+ switch (in[++i])
+ {
+ case 'd': /* EOT */
+ out[j++] = 0x04;
+ break;
+ case 'N': /* null */
+ out[j++] = 0x00;
+ break;
+ case 'n': /* line feed */
+ out[j++] = 0x0a;
+ break;
+ case 'r': /* carriage return */
+ out[j++] = 0x0d;
+ break;
+ case 's': /* space */
+ out[j++] = ' ';
+ break;
+ case 't': /* tab */
+ out[j++] = '\t';
+ break;
+ case '-': /* hyphen */
+ out[j++] = '-';
+ break;
+ case '\\': /* back slash */
+ out[j++] = '\\';
+ break;
+ case '0': /* '\nnn' format */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ byte = in[i] - '0';
+ k = 0;
+
+ while (3 > ++k)
+ if ((in[i+1] < '0') || (in[i+1] > '7'))
+ break;
+ else
+ {
+ byte = (byte<<3) + in[i+1] - '0';
+ ++i;
+ }
+ out[j++] = byte;
+ break;
+ default: /* don't know so skip it */
+ break;
+ }
+ ++i;
+ }
+ }
+ return j;
+}
+
+
+/* find a state within a script */
+
+struct script *
+ find_state(begin, newstate)
+struct script *begin;
+char *newstate;
+{
+ struct script *here;
+
+ for (here=begin; here != (struct script *)NULL; here=here->next) {
+ if (here->opcode == SC_LABEL &&
+ strcmp(here->strprm, newstate) == SAME)
+ return here;
+ }
+ return (struct script *)NULL;
+}
+
+
+/*
+ * do_script() - execute a script
+ */
+int do_script(begin)
+ struct script *begin;
+{
+ struct script *curstate, *newstate, *curscr;
+ int dbgsave;
+ char tempstr[MAX_SCLINE];
+ char dfname[256];
+ char *c, chr;
+ int prmlen;
+ int dbfd;
+
+ time_t sc_carrtime = 45000; /* time to wf carr after dial */
+ time_t sc_chrdly = 100; /* delay time for ttoslow */
+ time_t sc_ptime = 2000; /* time to allow for pause char */
+ time_t sc_wtime = 10000; /* time to allow for wait char */
+ time_t sc_dtime = 100; /* time to allow for each digit */
+ time_t sc_dtmo; /* total time to dial number */
+ int sc_counter; /* random counter */
+ char sc_pchar = ','; /* modem pause character */
+ char sc_wchar = 'W'; /* modem wait-for-dialtone character */
+ time_t sc_begwait; /* time at beg of wait */
+ time_t sc_secs; /* timeout period */
+
+ int expcnt;
+ int expin;
+ static char expbuf[MAX_EXPCT];
+
+ dbgsave = Debug;
+ curstate = begin;
+
+ if (curstate == (struct script *)NULL)
+ return SUCCESS;
+
+ _newstate:
+ /*
+ * do all of curstate's actions. Enter with curstate pointing
+ * to a label entry
+ */
+ expin = 0;
+
+ for (curscr = curstate->next; /* point to 1st scr after label */
+ (curscr != (struct script *)NULL) && /* do until end of scr */
+ (curscr->opcode != SC_LABEL); /* or next label */
+ curscr = curscr->next)
+ {
+ expcnt = 0;
+ switch (curscr->opcode)
+ {
+ case SC_LABEL:
+ logit("Script proc err", curstate->strprm);
+ return FAIL;
+
+ case SC_FLSH:
+ DEBUG(DB_LGII, "Flushing typeahead buffer\n", 0);
+ ttflui();
+ break;
+
+ case SC_CDLY:
+ sc_chrdly = curscr->intprm;
+ DEBUG(DB_LGII, "Set chrdly to %d\n", sc_chrdly);
+ break;
+
+ case SC_PCHR:
+ sc_pchar = *(curscr->strprm);
+ DEBUG(DB_LGII, "Set pause char to %c\n", sc_pchar);
+ break;
+
+ case SC_PTIM:
+ sc_ptime = curscr->intprm;
+ DEBUG(DB_LGII, "Set pause time to %d\n", sc_ptime);
+ break;
+
+ case SC_WCHR:
+ sc_wchar = *(curscr->strprm);
+ DEBUG(DB_LGII, "Set wait char to %c\n", sc_wchar);
+ break;
+
+ case SC_WTIM:
+ sc_wtime = curscr->intprm;
+ DEBUG(DB_LGII, "Set wait time to %d\n", sc_wtime);
+ break;
+
+ case SC_ZERO:
+ sc_counter = 0;
+ DEBUG(DB_LGII, "Set counter to %d\n", sc_counter);
+ break;
+
+ case SC_INCR:
+ sc_counter++;
+ DEBUG(DB_LGII, "Incr counter to %d\n", sc_counter);
+ break;
+
+ case SC_WAIT:
+ DEBUG(DB_LGII, "Sleeping %d tenth-secs\n", curscr->intprm);
+ msleep(curscr->intprm);
+ break;
+
+ case SC_DTIM:
+ sc_dtime = curscr->intprm;
+ DEBUG(DB_LGII, "Digit time is %d\n", sc_dtime);
+ break;
+
+ case SC_CTIM:
+ sc_carrtime = curscr->intprm;
+ DEBUG(DB_LGII, "Carrier time is %d\n", sc_carrtime);
+ break;
+
+ case SC_EXIT:
+ Debug = dbgsave;
+ DEBUG(DB_LGI, "Script ended successfully\n", 0);
+ return SUCCESS;
+
+ case SC_FAIL:
+ Debug = dbgsave;
+ if (DEBUG_LEVEL(DB_LGI) && dbf != NULL)
+ fprintf(dbf, "Script failed\n");
+ else if (expin)
+ charlog(expbuf, expin, DB_LOG,
+ "Script failed. Last received data");
+ return FAIL;
+
+ case SC_LOG:
+ logit(curscr->strprm, "");
+ break;
+
+ case SC_LOGE:
+ logit("ERROR: ", curscr->strprm);
+ break;
+
+ case SC_DBOF:
+ /*
+ * If the debug file name does not begin with "/", then
+ * we prepend the LOG_DIR to the string. Then CREATE the
+ * file. This WIPES OUT previous logs.
+ */
+ *dfname = '\0'; /* Zero name string */
+ if(curscr->strprm[0] != '/')
+ strcat(dfname, LOG_DIR); /* Prepend default directory */
+ strcat(dfname, curscr->strprm); /* Add given string */
+ DEBUG(DB_LGII, "Open debug file %s\n", dfname);
+ if ((dbfd = creat (dfname, 0600)) <= 0)
+ {
+ logit("Failed to create debug log %s", dfname);
+ perror("");
+ return FAIL;
+ }
+ if ((dbf = fdopen(dbfd, "w")) == NULL)
+ {
+ logit("Failed to open debug log fildes.", "");
+ perror("");
+ return FAIL;
+ }
+ break;
+
+ case SC_DBG:
+ DEBUG(DB_LGI, "<%s>\n", curscr->strprm);
+ break;
+
+ case SC_DBGE:
+ DEBUG(DB_LGIE, "ERROR: <%s>\n", curscr->strprm);
+ break;
+
+ case SC_DBST:
+ Debug |= curscr->intprm;
+ DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug);
+ break;
+
+ case SC_DBCL:
+ Debug &= ~(curscr->intprm);
+ DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug);
+ break;
+
+ case SC_BRK:
+ DEBUG(DB_LGI, "Sending break\n", 0);
+ ttbreak();
+ break;
+
+ case SC_HANG:
+ DEBUG(DB_LGI, "Dropping DTR\n", 0);
+ tthang();
+ break;
+
+ case SC_7BIT:
+ DEBUG(DB_LGI, "Enabling 7-bit stripping\n", 0);
+ tt7bit(TRUE);
+ break;
+
+ case SC_8BIT:
+ DEBUG(DB_LGI, "Disabling 7-bit stripping\n", 0);
+ tt7bit(FALSE);
+ break;
+
+ case SC_PNON:
+ DEBUG(DB_LGI, "Setting 8-bit, no parity\n", 0);
+ ttpar(NONE);
+ break;
+
+ case SC_PEVN:
+ DEBUG(DB_LGI, "Setting 7-bit, even parity\n", 0);
+ ttpar(EVEN);
+ break;
+
+ case SC_PODD:
+ DEBUG(DB_LGI, "Setting 7-bit, odd parity\n", 0);
+ ttpar(ODD);
+ break;
+
+ case SC_IFBL:
+ if (ttblind())
+ {
+ DEBUG(DB_LGI, "Blind mux,\n", 0);
+ goto _chgstate;
+ }
+ break;
+
+ case SC_IFBG:
+ if (ttblind() && sc_counter > curscr->intprm)
+ {
+ DEBUG(DB_LGI, "Blind mux & ctr > %d\n",
+ curscr->intprm);
+ goto _chgstate;
+ }
+ break;
+
+ case SC_IFGT:
+ if (sc_counter > curscr->intprm)
+ {
+ DEBUG(DB_LGI, "Counter > %d\n", curscr->intprm);
+ goto _chgstate;
+ }
+ break;
+
+ case SC_GOTO:
+ _chgstate:
+ DEBUG(DB_LGI, "Changing to state %s\n",
+ curscr->newstate);
+ curstate = find_state(begin, curscr->newstate);
+ if (curstate == NULL)
+ {
+ logit("New state not found",
+ curscr->newstate);
+ return FAIL;
+ }
+ goto _newstate;
+
+ case SC_SEND:
+ ttoslow(curscr->strprm, curscr->intprm, sc_chrdly);
+ break;
+
+ case SC_TELN:
+ if (curscr->intprm > paramc - 1)
+ {
+ sprintf(tempstr, "telno - param #%d", curscr->intprm);
+ logit(tempstr, " not present");
+ return FAIL;
+ }
+ strcpy(telno, paramv[curscr->intprm]);
+ DEBUG(DB_LGII, "telno set to %s\n", telno);
+ break;
+
+ case SC_SNDP:
+ if (curscr->intprm > paramc - 1)
+ {
+ sprintf(tempstr, "sendstr - param #%d", curscr->intprm);
+ logit(tempstr, " not present");
+ return FAIL;
+ }
+ prmlen = xlat_str(tempstr, paramv[curscr->intprm]);
+ ttoslow(tempstr, prmlen, sc_chrdly);
+ break;
+
+ case SC_IF1P:
+ if (curscr->intprm < paramc)
+ goto _chgstate;
+ break;
+
+ case SC_IF0P:
+ if (curscr->intprm >= paramc)
+ goto _chgstate;
+ break;
+
+ case SC_DIAL:
+ if(telno[0] == '\0')
+ {
+ logit("telno not set", "");
+ return(FAIL);
+ }
+ /*
+ * Compute and set a default timeout for the 'timeout'
+ * command. Some parameters in this computation may be
+ * changed by the script. See the man page xchat(8) for
+ * details.
+ */
+ sc_dtmo = (sc_dtime+sc_chrdly)*strlen(telno)
+ + sc_carrtime;
+ c=strcpy(tempstr, telno);
+ for (; *c!='\0'; c++)
+ {
+ if (*c == 'W')
+ {
+ *c = sc_wchar;
+ sc_dtmo += sc_wtime;
+ }
+ else if (*c == 'P')
+ {
+ *c = sc_pchar;
+ sc_dtmo += sc_ptime;
+ }
+ }
+ DEBUG(DB_LGI, "Dialing, default timeout is %d millisecs\n", sc_dtmo);
+ ttoslow(tempstr, 0, sc_chrdly);
+ break;
+
+ case SC_TIMO: /* these are "expects", don't bother */
+ case SC_XPCT: /* with them yet, other than noting that */
+ case SC_CARR: /* they exist */
+ case SC_XPST:
+ expcnt++;
+ break;
+ }
+
+ }
+
+ /* we've done the current state's actions, now do its expects, if any */
+
+ if (expcnt == 0)
+ {
+ if (curscr != (struct script *)NULL &&
+ (curscr->opcode == SC_LABEL))
+ {
+ curstate = curscr;
+ DEBUG(DB_LGI, "Fell through to state %s\n",
+ curstate->strprm);
+ goto _newstate;
+ }
+ else
+ {
+ logit("No way out of state", curstate->strprm);
+ return FAIL;
+ }
+ }
+
+ time(&sc_begwait); /* log time at beg of expect */
+ DEBUG(DB_LGI, "Doing expects for state %s\n", curstate->strprm);
+ charlog((char *)NULL, 0, DB_LGI, "Received");
+
+ while (1)
+ {
+ chr = xgetc(1); /* Returns upon char input or 1 sec. tmo */
+
+ charlog(&chr, 1, DB_LGI, (char *)NULL);
+
+ if (chr != EOF)
+ {
+ if (expin < MAX_EXPCT)
+ {
+ expbuf[expin++] = chr & 0x7f;
+ }
+ else
+ {
+ strncpy(expbuf, &expbuf[1], MAX_EXPCT-1);
+ expbuf[MAX_EXPCT-1] = chr & 0x7f;
+ }
+ }
+
+ /* for each entry in the current state... */
+
+ for (curscr = curstate->next;
+ (curscr != (struct script *)NULL) &&
+ (curscr->opcode != SC_LABEL);
+ curscr = curscr->next)
+ {
+
+ switch (curscr->opcode)
+ {
+ case SC_TIMO:
+ sc_secs = curscr->intprm;
+ if (sc_secs == 0)
+ sc_secs = sc_dtmo;
+ sc_secs /= 1000;
+ if (time(NULL)-sc_begwait > sc_secs)
+ {
+ DEBUG(DB_LGI,
+ "\nTimed out (%d secs)\n", sc_secs);
+ goto _chgstate;
+ }
+ break;
+
+ case SC_CARR:
+ if (ttcd())
+ {
+ DEBUG(DB_LGI, "\nGot carrier\n", 0);
+ goto _chgstate;
+ }
+ break;
+
+ case SC_HUPS:
+ if (fShangup)
+ {
+ DEBUG(DB_LGI, "\nGot data set hangup\n", 0);
+ goto _chgstate;
+ }
+ break;
+
+ case SC_XPCT:
+ if ((expin >= curscr->intprm) &&
+ (strncmp(curscr->strprm,
+ &expbuf[expin - curscr->intprm],
+ curscr->intprm) == SAME))
+ {
+ charlog(curscr->strprm, curscr->intprm,
+ DB_LGI, "Matched");
+ goto _chgstate;
+ }
+ break;
+
+ }
+ }
+ }
+}
+ /* New opcode added by hag@eddie.mit.edu for expecting a
+ parameter supplied string */
+ case SC_XPST:
+ if(curscr->intprm >paramc-1)
+ {
+ sprintf(tempstr,"expectstr - param#%d",curscr->intprm);
+ logit(tempstr, " not present");
+ return(FAIL);
+ }
+ prmlen=xlat_str(tempstr,paramv[curscr->intprm]);
+ if((expin >= prmlen) &&
+ (strncmp(tempstr,&expbuf[expin-prmlen],
+ prmlen) == SAME))
+ {
+ charlog(tempstr,prmlen,DB_LGI, "Matched");
+ goto _chgstate;
+ }
+ break;
+/*
+ * SIGNAL HANDLERS
+ */
+
+/*
+ * usignal - generic signal catcher
+ */
+static int usignal(isig)
+ int isig;
+{
+ DEBUG(DB_LOG, "Caught signal %d. Exiting...\n", isig);
+ restore_tty();
+ exit(FAIL);
+}
+
+/*
+ * uhup - HUP catcher
+ */
+static int uhup(isig)
+ int isig;
+{
+ DEBUG(DB_LOG, "Data set hangup.\n");
+ fShangup = TRUE;
+}
+
+/*
+ * TERMINAL I/O ROUTINES
+ */
+
+/*
+ * xgetc - get a character with timeout
+ *
+ * Assumes that stdin is opened on a terminal or TCP socket
+ * with O_NONBLOCK.
+ */
+static char xgetc(tmo)
+int tmo; /* Timeout, seconds */
+{
+ char c;
+ struct timeval s;
+ int f = 1; /* Select on stdin */
+ int result;
+
+ if(read(0, &c, 1) <= 0) /* If no data available */
+ {
+ s.tv_sec = (long)tmo;
+ s.tv_usec = 0L;
+ if(select (1, &f, (int *) NULL, &f, &s) == 1)
+ read(0, &c, 1);
+ else
+ c = '\377';
+ }
+
+ return(c);
+}
+
+/*
+ * Pause for an interval in milliseconds
+ */
+void msleep(msec)
+long msec;
+{
+
+#if HAVE_USLEEP
+ if(msec == 0) /* Skip all of this if delay = 0 */
+ return;
+ usleep (msec * (long)1000);
+#endif /* HAVE_USLEEP */
+
+#if HAVE_NAPMS
+ if(msec == 0) /* Skip all of this if delay = 0 */
+ return;
+ napms (msec);
+#endif /* HAVE_NAPMS */
+
+#if HAVE_NAP
+ if(msec == 0) /* Skip all of this if delay = 0 */
+ return;
+ nap (msec);
+#endif /* HAVE_NAP */
+
+#if HAVE_POLL
+ struct pollfd sdummy;
+
+ if(msec == 0)
+ return;
+ /*
+ * We need to pass an unused pollfd structure because poll checks
+ * the address before checking the number of elements.
+ */
+ poll (&sdummy, 0, msec);
+#endif /* HAVE_POLL */
+
+#if USE_SELECT_TIMER
+ struct timeval s;
+
+ if(msec == 0)
+ return;
+ s.tv_sec = msec / 1000L;
+ s.tv_usec = (msec % 1000L) * 1000L;
+ select (0, (int *) NULL, (int *) NULL, (int *) NULL, &s);
+#endif /* USE_SELECT_TIMER */
+
+#if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP && \
+ ! HAVE_POLL && ! USE_SELECT_TIMER
+ if(msec == 0)
+ return;
+ sleep (1); /* Sleep for a whole second (UGH!) */
+#endif /* HAVE_ and USE_ nothing */
+}
+
+/*
+ * Debugging output
+ */
+static void DEBUG(level, msg1, msg2)
+int level;
+char *msg1, *msg2;
+{
+ if ((dbf != NULL) && DEBUG_LEVEL(level))
+ fprintf(dbf, msg1, msg2);
+}
+
+/*
+ * charlog - log a string of characters
+ *
+ * SPECIAL CASE: msg=NULL, len=1 and msg[0]='\377' gets logged
+ * when read does its 1 sec. timeout. Log "<1 sec.>"
+ * so user can see elapsed time
+ */
+static void charlog(buf, len, mask, msg)
+char *buf;
+int len, mask;
+char *msg;
+{
+ char tbuf[256];
+
+ if (DEBUG_LEVEL(mask) && dbf != NULL)
+ {
+ if(msg == (char *)NULL)
+ msg = "";
+ strncpy(tbuf, buf, len);
+ tbuf[len] = '\0';
+ if(len == 1 && tbuf[0] == '\377')
+ strcpy(tbuf, "<1 sec.>");
+ fprintf(dbf, "%s %s\n", msg, tbuf);
+ }
+}
+
+/*
+ * setup_tty()
+ *
+ * Save current tty settings, then set up raw, single
+ * character input processing, with 7-bit stripping.
+ */
+static void setup_tty()
+{
+ register int i;
+
+ ioctl(0, TCGETA, &old);
+
+ new = old;
+
+ for(i = 0; i < 7; i++)
+ new.c_cc[i] = '\0';
+ new.c_cc[VMIN] = 0; /* MIN = 0, use requested count */
+ new.c_cc[VTIME] = 10; /* TIME = 1 sec. */
+ new.c_iflag = ISTRIP; /* Raw mode, 7-bit stripping */
+ new.c_lflag = 0; /* No special line discipline */
+
+ ioctl(0, TCSETA, &new);
+}
+
+/*
+ * restore_tty() - restore signal handlers and tty modes on exit.
+ */
+static void restore_tty(sig)
+int sig;
+{
+ ioctl(0, TCSETA, &old);
+ return;
+}
+
+/*
+ * ttoslow() - Send characters with pacing delays
+ */
+static void ttoslow(s, len, delay)
+ char *s;
+ int len;
+ time_t delay;
+{
+ int i;
+
+ if (len == 0)
+ len = strlen(s);
+
+ charlog (s, len, DB_LGI, "Sending slowly");
+
+ for (i = 0; i < len; i++, s++)
+ {
+ write(1, s, 1);
+ msleep(delay);
+ }
+}
+
+/*
+ * ttflui - flush input buffer
+ */
+static void ttflui()
+{
+ if(isatty(0))
+ (void) ioctl ( 0, TCFLSH, 0);
+}
+
+/*
+ * ttcd - Test if carrier is present
+ *
+ * NOT IMPLEMENTED. I don't know how!!!
+ */
+static int ttcd()
+{
+ return TRUE;
+}
+
+/*
+ * tthang - Force DTR low for 1-2 sec.
+ */
+static void tthang()
+{
+ if(!isatty())
+ return;
+
+#ifdef TCCLRDTR
+ (void) ioctl (1, TCCLRDTR, 0);
+ sleep (2);
+ (void) ioctl (1, TCSETDTR, 0);
+#endif
+
+ return;
+}
+
+/*
+ * ttbreak - Send a "break" on the line
+ */
+static void ttbreak()
+{
+ (void) ioctl (1, TCSBRK, 0);
+}
+
+/*
+ * ttblind - return TRUE if tty is "blind"
+ *
+ * NOT IMPLEMENTED - Don't know how!!!
+ */
+static int ttblind()
+{
+ return FALSE;
+}
+
+/*
+ * tt7bit - enable/disable 7-bit stripping on line
+ */
+static void tt7bit(enable)
+ int enable;
+{
+ if(enable)
+ new.c_iflag |= ISTRIP;
+ else
+ new.c_iflag &= ~ISTRIP;
+
+ ioctl(0, TCSETA, &new);
+}
+
+/*
+ * ttpar - Set parity mode on line. Ignore parity errors on input.
+ */
+static void ttpar(mode)
+ int mode;
+{
+ switch(mode)
+ {
+ case NONE:
+ new.c_iflag &= ~(INPCK | IGNPAR);
+ new.c_cflag &= ~(CSIZE | PARENB | PARODD);
+ new.c_cflag |= CS8;
+ break;
+
+ case EVEN:
+ new.c_iflag |= (INPCK | IGNPAR);
+ new.c_cflag &= ~(CSIZE | PARODD);
+ new.c_cflag |= (CS7 | PARENB);
+
+ break;
+
+ case ODD:
+ new.c_iflag |= (INPCK | IGNPAR);
+ new.c_cflag &= ~(CSIZE);
+ new.c_cflag |= (CS7 | PARENB | PARODD);
+ break;
+ }
+
+ ioctl(0, TCSETA, &new);
+}
+
+
+
+
+
+
+