home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / ip / ppp / dp-2.3 / dpd / runscript.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-25  |  25.1 KB  |  1,172 lines

  1. /*
  2. **  Parse and execute dialing script.  Ancient history says this was
  3. **  based on MMDF, but it wouldn't be recognized now...
  4. **  Copyright (c) 1991 Bolt Beranek and Newman, Inc.
  5. **  All rights reserved.
  6. **
  7. **  Redistribution and use in source and binary forms are permitted
  8. **  provided that: (1) source distributions retain this entire copyright
  9. **  notice and comment, and (2) distributions including binaries display
  10. **  the following acknowledgement:  ``This product includes software
  11. **  developed by Bolt Beranek and Newman, Inc. and CREN/CSNET'' in the
  12. **  documentation or other materials provided with the distribution and in
  13. **  all advertising materials mentioning features or use of this software.
  14. **  Neither the name of Bolt Beranek and Newman nor CREN/CSNET may be used
  15. **  to endorse or promote products derived from this software without
  16. **  specific prior written permission.
  17. **
  18. **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  19. **  WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  20. **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  21. */
  22. /*
  23.  * Copyright (c) 1992 Purdue University
  24.  * All rights reserved.
  25.  *
  26.  * Redistribution and use in source and binary forms are permitted
  27.  * provided that the above copyright notice and this paragraph are
  28.  * duplicated in all such forms and that any documentation,
  29.  * advertising materials, and other materials related to such
  30.  * distribution and use acknowledge that the software was developed
  31.  * by Purdue University.  The name of the University may not be used
  32.  * to endorse or promote products derived * from this software without
  33.  * specific prior written permission.
  34.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  35.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  36.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  37.  *
  38.  * Note: this copyright applies to portions of this software developed
  39.  * at Purdue beyond the software covered by the original copyright.
  40.  */
  41. #include <stdio.h>
  42. #include <signal.h>
  43. #include <errno.h>
  44. #include <setjmp.h>
  45. #include <fcntl.h>
  46. #include <ctype.h>
  47. #include <errno.h>
  48. #include <sys/types.h>
  49. #include <sys/wait.h>
  50. #include <sys/ioctl.h>
  51. #include <sys/time.h>
  52. #include <sys/file.h>
  53. #include <sys/param.h>
  54. #include <sys/stream.h>
  55. #include <netinet/in.h>
  56. #include <netinet/in_systm.h>
  57. #include <netinet/ip.h>
  58. #include <sys/socket.h>
  59. #include <net/if.h>
  60. #include "dp_str.h"
  61. #include "dp.h"
  62. #include "dpd.h"
  63.  
  64.     /* Various constants. */
  65. #define MAXFILES    15
  66. #define MAXLINE        256
  67. #define MAXFIELDS    32    /* Maximum number of parameters        */
  68. #define    MAXRECVSTR    MAXFIELDS - 2    /* Maximum number of recieve strings */
  69. #define MATCHLEN    128    /* Size of text to match in recv    */
  70. #define XMITWAIT    60    /* How long to wait on a write        */
  71. #define REPBUFSIZE    256    /* Size of replay buffer        */
  72.  
  73. #define SC_SLEEP    0377    /* Char for \x, sleep            */
  74. #define SC_BREAK    0376    /* Char for \#, break            */
  75.  
  76.     /* Command codes; must not overlap with D_xxx codes */
  77. #define S_ABORT        101    /* Abort the script            */
  78. #define S_ALT        102    /* Alternate                */
  79. #define S_COMMENT    103    /* Comment                */
  80. #define S_GO        104    /* Start the protocol            */
  81. #define S_LOG        105    /* Log a message            */
  82. #define S_MARK        106    /* Mark the place to start a replay    */
  83. #define S_RECV        107    /* Watch for a string            */
  84. #define S_REPLAY    108    /* Reuse old input            */
  85. #define S_SELEND    109    /* Select end                */
  86. #define S_SELST        110    /* Select begin                */
  87. #define S_USEFILE    111    /* Read script commands from another file */
  88. #define S_XMIT        112    /* Transmit a string            */
  89. #define S_EOF        113    /* EOF in file                */
  90.  
  91.     /* Error codes return by commands */
  92. #define D_CONTIN     2    /* Continue with process        */
  93. #define D_OK         1    /* Everything is fine            */
  94. #define D_NOMATCH     0    /* Input did not match            */
  95. #define D_FATAL        -1    /* Fatal error                */
  96. #define D_NONFATAL    -2    /* Possibly-recoverable error        */
  97. #define D_INTRPT    -3    /* Interrupt during system call        */
  98.  
  99.     /* Error codes returned by parsing routines */
  100. #define ERR_NOWORD    -1
  101. #define ERR_QUOTE    -2
  102. #define ERR_FIELDS    -3
  103. #define ERR_BADVAR    -4
  104. #define ERR_TOOLONG    -5
  105.  
  106.     /* Structure that keeps track of open files. */
  107. typedef struct _OPENFILE {
  108.     char    *scriptname;
  109.     FILE    *scriptfile;
  110.     int        linenumber;
  111.     int        nfields;
  112.     char    *fields[MAXFIELDS];
  113. } OPENFILE;
  114.  
  115.     /* Structure that defines a command */
  116. typedef struct _COMMAND {
  117.     char    *Name;
  118.     int        Code;
  119.     int        MinFields;
  120.     int        MaxFields;
  121. } COMMAND;
  122.  
  123.  
  124. static char    scriptname[128];    /* Script filename        */
  125. static char    pushbuffer[MATCHLEN + 2]; /* Pushback buffer for matcher */
  126. static char    *pushptr = pushbuffer;    /* Pointer into buffer        */
  127. static jmp_buf    timerest;
  128. static int    linenumber;        /* Line number in script file    */
  129. static int    use_nfields;        /* Number of "use" fields    */
  130. static char    *use_fields[MAXFIELDS];    /* Current "use" fields        */
  131. static OPENFILE    *openfiles[MAXFILES];    /* Stack of open files        */
  132. static int    nopenfiles;        /* Number of open files        */
  133. static FILE    *scriptfile;        /* Script file            */
  134. static FILE    *portfile;        /* Modem file            */
  135. static FILE    *tranfile;        /* Transcript file descriptor    */
  136. static int    transtyle;        /* Transcript style        */
  137. static int    repcount;        /* Chars left in replay        */
  138. static int    repcurr;        /* Current replay char        */
  139. static int    repnext;        /* Where to insert next char    */
  140. static int    repsize;        /* Replay buffer size; constant    */
  141. static int    repfirst;        /* First char in replay        */
  142. static int    replay;            /* Doing a replay?        */
  143. static int    repbuff[REPBUFSIZE];    /* Replay buffer        */
  144. static char    WHERE[] = "dialscript";    /* Where we are, for logging    */
  145. static COMMAND    commands[] = {
  146.     {    "abort",    S_ABORT,    0,    0    },
  147.     {    "alternate",    S_ALT,        0,    0    },
  148.     {    "go",        S_GO,        0,    0    },
  149.     {    "log",        S_LOG,        1,    1    },
  150.     {    "mark",        S_MARK,        0,    0    },
  151.     {    "recv",        S_RECV,        2,    10    },
  152.     {    "replay",    S_REPLAY,    0,    0    },
  153.     {    "use",        S_USEFILE,    1,    10    },
  154.     {    "xmit",        S_XMIT,        1,    1    },
  155.     {    "{",        S_SELST,    0,    0    },
  156.     {    "}",        S_SELEND,    0,    0    },
  157.     { NULL }
  158. };
  159.  
  160. extern char    *strerror();
  161.  
  162.  
  163. /*
  164. **  Report a syntax error.
  165. */
  166. static void
  167. syntax_error(problem)
  168.     char    *problem;
  169. {
  170.     d_log(DLOG_GENERAL, WHERE, "Syntax error in \"%s\", line %d:\n\t%s\n",
  171.     scriptname, linenumber, problem);
  172. }
  173.  
  174.  
  175. /*
  176. **  Report an I/O error.
  177. */
  178. static void
  179. io_error(problem)
  180.     char    *problem;
  181. {
  182.     d_log(DLOG_GENERAL, WHERE, "I/O error in \"%s\", line %d:\n\t%s\n",
  183.     scriptname, linenumber, problem);
  184. }
  185.  
  186.  
  187. /*
  188. **  Report a miscellaneous error with errno.
  189. */
  190. static void
  191. misc_error(format, arg)
  192.     char    *format;
  193.     char    *arg;
  194. {
  195.     char    buff[256];
  196.  
  197.     (void)sprintf(buff, "Error in \"%s\", line %d:\n\t%s, %s",
  198.     scriptname, linenumber, format, strerror(errno));
  199.     d_log(DLOG_GENERAL, WHERE, buff, arg);
  200. }
  201.  
  202.  
  203. /*
  204. **  Return printable representation of char.
  205. */
  206. static char *
  207. seechar(c)
  208.     char    c;
  209. {
  210.     static char    buff[8];
  211.  
  212.     if (isprint(c) || c == '\n') {
  213.     buff[0] = c;
  214.     buff[1] = '\0';
  215.     }
  216.     else
  217.     (void)sprintf(buff, "\\%03o", c);
  218.     return buff;
  219. }
  220.  
  221.  
  222. /*
  223. **  Parse a C-style escape string.  Returns the length of the string
  224. **  or -1 on error.
  225. */
  226. static int
  227. canonstring(in, out)
  228.     register char    *in;
  229.     register char    *out;
  230. {
  231.     int            count;
  232.     char        c;
  233.     char        *save;
  234.  
  235.     for (save = out; c = *in++; )
  236.     if (c != '\\')
  237.         *out++ = c;
  238.     else {
  239.         switch (c = *in++) {
  240.         default:    *out++ = c;        break;
  241.         case 'b':    *out++ = '\b';        break;
  242.         case 'n':    *out++ = '\n';        break;
  243.         case 'r':    *out++ = '\r';        break;
  244.         case 't':    *out++ = '\t';        break;
  245.         case 'x':    *out++ = SC_SLEEP;    break;
  246.         case '#':    *out++ = SC_BREAK;    break;
  247.         case '\0':
  248.         return -1;
  249.  
  250.         case '0': case '1': case '2': case '3':
  251.         case '4': case '5': case '6': case '7':
  252.         for (c -= '0', count = 1; count < 3; count++, in++) {
  253.             if (*in < '0' || *in > '7')
  254.             break;
  255.             c = (c << 3) | (*in - '0');
  256.         }
  257.         *out++ = c;
  258.         }
  259.     }
  260.     *out = '\0';
  261.     return out - save;
  262. }
  263.  
  264.  
  265. /*
  266. **  Snip the next work out of the buffer.
  267. */
  268. static int
  269. getword(p, start, next)
  270.     register char    *p;
  271.     char        **start;
  272.     char        **next;
  273. {
  274.     /* Skip leading whitespace, see if there's anything left. */
  275.     while (*p == ' ' || *p == '\t')
  276.     p++;
  277.     if (*p == '\0')
  278.     return ERR_NOWORD;
  279.  
  280.     if (*p == '"') {
  281.     for (*start = ++p; *p != '"'; p++)
  282.         if (*p == '\0')
  283.         return ERR_QUOTE;
  284.     *p++ = '\0';
  285.     }
  286.     else {
  287.     for (*start = p; *p && *p != ' ' && *p != '\t'; )
  288.         p++;
  289.     if (*p)
  290.         *p++ = '\0';
  291.     }
  292.     *next = p;
  293.     return D_OK;
  294. }
  295.  
  296.  
  297. #define END    (&temp[MAXLINE - 2])
  298.  
  299.  
  300. /*
  301. **  Handle $-expansion
  302. */
  303. static int
  304. expanddollars(buff)
  305.     char    *buff;
  306. {
  307.     char    temp[MAXLINE];
  308.     char    *out;
  309.     char    *p;
  310.     int        i;
  311.  
  312.     for (out = temp, p = buff; *p; p++)
  313.     if (*p != '$') {
  314.         if (out >= END)
  315.         return ERR_TOOLONG;
  316.         *out++ = *p;
  317.     }
  318.     else if (*++p == '$') {
  319.         if (out >= END)
  320.         return ERR_TOOLONG;
  321.         *out++ = *p;
  322.     }
  323.     else {
  324.         if (!isdigit(*p) || (i = *p - '0') > use_nfields)
  325.         return ERR_BADVAR;
  326.         if (out + strlen(use_fields[i]) >= END)
  327.         return ERR_TOOLONG;
  328.         out += strlen(strcpy(out, use_fields[i]));
  329.     }
  330.  
  331.     *out++ = '\0';
  332.     (void)strcpy(buff, temp);
  333.     return D_OK;
  334. }
  335.  
  336.  
  337. /*
  338. **  Parse a line and break it into fields.  Return the number of fields
  339. **  or an error.
  340. */
  341. static int
  342. getfields(buff, maxfields, fields)
  343.     char    *buff;
  344.     int         maxfields;
  345.     char    *fields[];
  346. {
  347.     char    *p;
  348.     char    *next;
  349.     int        i;
  350.  
  351.     if (strchr(buff, '$') != NULL && (i = expanddollars(buff)) != D_OK)
  352.     return i;
  353.  
  354.     for (i = 0, p = buff; i < maxfields; i++, p = next)
  355.     switch (getword(p, &fields[i], &next)) {
  356.     case ERR_NOWORD:
  357.         return i;
  358.     case ERR_QUOTE:
  359.         return ERR_QUOTE;
  360.     }
  361.     return ERR_FIELDS;
  362. }
  363.  
  364.  
  365. /*
  366. **  Get a line of text and parse it into a command and fields.  Return
  367. **  the command number or an error.
  368. */
  369. static int
  370. getcommand(buff, nfields, fields)
  371.     char        *buff;
  372.     int            *nfields;
  373.     char        *fields[];
  374. {
  375.     char        *p;
  376.     register COMMAND    *cp;
  377.  
  378.     do {
  379.     linenumber++;
  380.     if (fgets(buff, MAXLINE, scriptfile) == NULL)
  381.         return S_EOF;
  382.     if ((p = strchr(buff, '\n')) == NULL) {
  383.         syntax_error("Line too long");
  384.         return D_FATAL;
  385.     }
  386.     *p = '\0';
  387.     } while (buff[0] == '\0' || buff[0] == '#');
  388.  
  389.     /* Get the first word. */
  390.     switch (getword(buff, &fields[0], &p)) {
  391.     default:
  392.     return D_FATAL;
  393.     case ERR_QUOTE:
  394.     syntax_error("Missing end quote");
  395.     return D_FATAL;
  396.     case D_OK:
  397.     break;
  398.     }
  399.  
  400.     /* Find the command word. */
  401.     for (cp = commands; cp->Name; cp++)
  402.     if (strcmp(fields[0], cp->Name) == 0)
  403.         break;
  404.     if (cp->Name == NULL) {
  405.     syntax_error("Bad command");
  406.     return D_FATAL;
  407.     }
  408.  
  409.     /* Parse the rest of the line. */
  410.     *nfields = getfields(p, MAXFIELDS - 1, &fields[1]);
  411.     if (*nfields < 0) {
  412.     switch (*nfields) {
  413.     default:
  414.         syntax_error("Internal error in parser");
  415.         break;
  416.     case ERR_FIELDS:
  417.         syntax_error("Too many fields");
  418.         break;
  419.     case ERR_QUOTE:
  420.         syntax_error("Missing end quote");
  421.         break;
  422.     case ERR_BADVAR:
  423.         syntax_error("Bad \"$\" variable");
  424.         break;
  425.     case ERR_TOOLONG:
  426.         syntax_error("Line too long");
  427.         break;
  428.     }
  429.     return D_FATAL;
  430.     }
  431.  
  432.     /* Make sure the right number of fields are present. */
  433.     if (*nfields >= cp->MinFields && *nfields <= cp->MaxFields)
  434.     return cp->Code;
  435.     syntax_error("Wrong number of fields");
  436.     return D_FATAL;
  437. }
  438.  
  439.  
  440. /*
  441. **  Command dispatcher.
  442. */
  443. static int
  444. dispatch(c, nfields, fields)
  445.     int        c;
  446.     int        nfields;
  447.     char    *fields[];
  448. {
  449.     int        i;
  450.  
  451.     switch (c) {
  452.     default:
  453.     syntax_error("Unknown command in dispatch");
  454.     return D_FATAL;
  455.     case S_MARK:
  456.     do_mark();
  457.     break;
  458.     case S_REPLAY:
  459.     do_replay();
  460.     break;
  461.     case S_LOG:
  462.     d_log(DLOG_GENERAL, WHERE, "%s", fields[1]);
  463.     break;
  464.     case S_COMMENT:
  465.     break;
  466.     case S_ABORT:
  467.     return D_FATAL;
  468.     case S_GO:
  469.     return D_OK;
  470.     case S_ALT:
  471.     case S_SELST:
  472.     case S_SELEND:
  473.     return c;
  474.     case S_XMIT:
  475.     return (i = do_xmit(fields[1])) < 0 ? i : D_CONTIN;
  476.     case S_RECV:
  477.     return (i = do_recv(nfields, fields)) < 0 ? i : D_CONTIN;
  478.     case S_USEFILE:
  479.     return do_use(fields[1], nfields, fields) < 0 ? D_FATAL : D_CONTIN;
  480.     case S_EOF:
  481.     /* Unexpected EOF.  Revert to previous script if possible. */
  482.     return closescript() > 0 ? D_CONTIN : D_FATAL;
  483.     }
  484.     return D_CONTIN;
  485. }
  486.  
  487.  
  488. /*
  489. **  Execute all commands in a block.
  490. */
  491. static int
  492. do_block()
  493. {
  494.     char    buff[MAXLINE + 2];
  495.     char    *fields[MAXFIELDS];
  496.     int        nfields;
  497.     int        c;
  498.     int        i;
  499.  
  500.     for ( ; ; ) {
  501.     if ((c = getcommand(buff, &nfields, fields)) < 0)
  502.         return c;
  503.     if ((i = dispatch(c, nfields, fields)) != D_CONTIN)
  504.         return i;
  505.     }
  506. }
  507.  
  508.  
  509. /*
  510. **  Skip forward until we hit the end of the current select block.
  511. */
  512. static int
  513. findblockend()
  514. {
  515.     char    buff[MAXLINE + 2];
  516.     char    *fields[MAXFIELDS];
  517.     int        nfields;
  518.     int        i;
  519.  
  520.     for ( ; ; ) {
  521.     if ((i = getcommand(buff, &nfields, fields)) < 0)
  522.         return i;
  523.     switch (i) {
  524.     case S_SELEND:
  525.         return S_SELEND;
  526.     case S_SELST:
  527.         if ((i = findblockend()) < 0)
  528.         return i;
  529.         break;
  530.     }
  531.     }
  532. }
  533.  
  534.  
  535. /*
  536. **  Find next alternative in current select block.
  537. */
  538. static int
  539. findnextalt()
  540. {
  541.     char    buff[MAXLINE + 2];
  542.     char    *fields[MAXFIELDS];
  543.     int        nfields;
  544.     int        i;
  545.  
  546.     for ( ; ; ) {
  547.     if ((i = getcommand(buff, &nfields, fields)) < 0)
  548.         return i;
  549.     switch (i) {
  550.     case S_SELEND:
  551.     case S_ALT:
  552.         return i;
  553.     case S_SELST:
  554.         if ((findblockend()) < 0)
  555.         return i;
  556.         break;
  557.     }
  558.     }
  559. }
  560.  
  561.  
  562. static int
  563. runloop()
  564. {
  565.     char    buff[MAXLINE + 2];
  566.     char    *fields[MAXFIELDS];
  567.     int        nfields;
  568.     int        nselect;
  569.     int        i;
  570.  
  571.     /* Go for it. */
  572.     for (nselect = 0; ; )
  573.     switch (i = do_block()) {
  574.     default:
  575.         if (i < 0)
  576.         return i;
  577.         syntax_error("Internal error in runloop");
  578.         return -1;
  579.  
  580.     case D_OK:
  581.         return 0;
  582.     case D_FATAL:
  583.         return -1;
  584.  
  585.     case D_NONFATAL:
  586.         /* Possibly-recoverable error.  If in a select block, try the
  587.          * next alternate. */
  588.         if (nselect <= 0 || (i = findnextalt()) < 0)
  589.         return -1;
  590.         switch (i) {
  591.         default:
  592.         syntax_error("Bad syntax in script");
  593.         return -1;
  594.         case S_ALT:
  595.         continue;
  596.         case S_SELEND:
  597.         return -1;
  598.         }
  599.  
  600.     case S_SELST:
  601.         /* The next line had better be an alternate  */
  602.         if (getcommand(buff, &nfields, fields) != S_ALT) {
  603.         syntax_error("Missing \"alternate\" after \"{\"");
  604.         return -1;
  605.         }
  606.         nselect++;
  607.         continue;
  608.  
  609.     case S_SELEND:
  610.         /* verify that there was a select block being looked at.  If so,
  611.          * then the last alternate was the successful one.  Continue
  612.          * normally. */
  613.         if (nselect-- <= 0) {
  614.         syntax_error("Bad \"}\"");
  615.         return -1;
  616.         }
  617.         continue;
  618.  
  619.     case S_ALT:
  620.         /* First, make sure the context was correct  */
  621.         if (nselect <= 0) {
  622.         syntax_error("Bad alternate");
  623.         return -1;
  624.         }
  625.         /* If we get here, then an alternate within a select was
  626.          * completed successfully.  Move on. */
  627.         if ((i = findblockend()) < 0)
  628.         return -1;
  629.         nselect--;
  630.         switch (i) {
  631.         default:
  632.         syntax_error("Syntax error in alternate");
  633.         return -1;
  634.         case S_SELEND:
  635.         continue;
  636.         }
  637.     }
  638. }
  639.  
  640. static char *
  641. copystring(p)
  642.     char    *p;
  643. {
  644.     char    *new;
  645.  
  646.     if ((new = malloc((unsigned int)strlen(p) + 1)) == NULL) {
  647.     misc_error("Malloc failed; can't copy \"%s\"", p);
  648.     return NULL;
  649.     }
  650.     return strcpy(new, p);
  651. }
  652.  
  653. char *
  654. tr_dial(str, map)
  655. char *str;
  656. char *map;
  657. {
  658.     char *s, *m;
  659.     int nm, n;
  660.     if (!map || !*map)
  661.     return str;
  662.  
  663.     nm = strlen(map)/2;
  664.     for (s = str ; *s ; s++)
  665.     for (n = nm, m = map ; n-- ; m += 2)
  666.         if (*s == *m) {
  667.         *s = *(m+1);
  668.         break;
  669.         }
  670.  
  671.     return str;
  672. }
  673.  
  674. /*
  675. **  Read lines from the script, parse them, do the actions.
  676. */
  677. int
  678. runscript(rp, fp, ms, tname, charmap)
  679.     REMOTE    *rp;
  680.     FILE    *fp;
  681.     char    *ms;
  682.     char    *tname;
  683.     char    *charmap;
  684. {
  685.     int        i;
  686.     char    *fields[MAXFIELDS];
  687.     char    **ap;
  688.  
  689.     /* Set up globals. */
  690.     portfile = fp;
  691.     if (tname == NULL)
  692.     tranfile = NULL;
  693.     else {
  694.     tranfile = *tname == '+' ? fopen(++tname, "a") : fopen(tname, "w");
  695.     if (tranfile == NULL) {
  696.         misc_error("Can't create transcript \"%s\"", tname);
  697.         return -1;
  698.     }
  699.     }
  700.     transtyle = rp->Transtyle;
  701.  
  702.     /*
  703.      * Set up the modem dialing script first..
  704.      */
  705.     fields[0] = use_fields[0] = copystring(ms);
  706.     fields[1] = use_fields[1] = tr_dial(copystring(rp->Phone), charmap);
  707.     i = do_use(ms, use_nfields = 2, fields) < 0 ? -1 : runloop();
  708.     while (closescript())
  709.     ;
  710.     if (i < 0)
  711.     i = -1;
  712.  
  713.     if (i >= 0) {
  714.     /*
  715.      * If call is successful, then do the login script.
  716.      */
  717.     fields[0] = use_fields[0] = copystring(rp->Script);
  718.     use_nfields = 1;
  719.     if (ap = rp->ScriptArgs)
  720.         for ( ; *ap ; ap++, use_nfields++ )
  721.         fields[use_nfields] = use_fields[use_nfields] = copystring(*ap);
  722.  
  723.     i = do_use(rp->Script, use_nfields, fields) < 0 ? -1 : runloop();
  724.     while (closescript())
  725.         ;
  726.     if (i < 0)
  727.         i = -2;
  728.     }
  729.  
  730.     if (tranfile)
  731.     (void)fclose(tranfile);
  732.     return i;
  733. }
  734.  
  735.  
  736. /*
  737. **   Wait for a given string, with optional timeout.
  738. */
  739. static void
  740. scripttimeout()
  741. {
  742.     longjmp(timerest, 1);
  743. }
  744.  
  745.  
  746. /*
  747. **  Write to the port, with a timeout.
  748. */
  749. static int
  750. writechar(p)
  751.     char    *p;
  752. {
  753.     int        i;
  754.  
  755.     if (tranfile && transtyle != TS_LOW) {
  756.     if (isprint(*p))
  757.         (void)fprintf(tranfile, "\tPUT    %c (0%03o)\n", *p, *p);
  758.     else
  759.         (void)fprintf(tranfile, "\tPUT 0x%02x (0%03o)\n", *p, *p);
  760.     (void)fflush(tranfile);
  761.     }
  762.  
  763.     (void)signal(SIGALRM, scripttimeout);
  764.     if (setjmp(timerest) == 0) {
  765.     (void)alarm(XMITWAIT);
  766.     i = write(fileno(portfile), p, 1);
  767.     (void)alarm(0);
  768.  
  769.     /* Process the return codes. */
  770.     if (i == 1)
  771.         return D_OK;
  772.     if (i >= 0)
  773.         return D_NONFATAL;
  774.     if (errno != EINTR)
  775.         return D_FATAL;
  776.     }
  777.     io_error("Write time-out");
  778.     return D_INTRPT;
  779. }
  780.  
  781.  
  782. /*
  783. **  Transmit a string.
  784. */
  785. do_xmit(string)
  786.     char        *string;
  787. {
  788.     register int    i;
  789.     register char    *p;
  790.     char        buff[MAXLINE];
  791.  
  792.     if (tranfile && transtyle != TS_LOW) {
  793.     (void)fprintf(tranfile, "XMIT %s\n", string);
  794.     (void)fflush(tranfile);
  795.     }
  796.  
  797.     /* Make canonical. */
  798.     if ((i = canonstring(string, buff)) < 0) {
  799.     syntax_error("Bad transmit string format");
  800.     return D_FATAL;
  801.     }
  802.  
  803.     for (p = buff; --i >= 0; p++) {
  804.     if ((unsigned char)*p == (unsigned char)SC_SLEEP) {
  805.         if (tranfile && transtyle != TS_LOW) {
  806.         (void)fprintf(tranfile, "\tSLEEP\n");
  807.         (void)fflush(tranfile);
  808.         }
  809.         (void)sleep(1);
  810.     }
  811.     else if ((unsigned char)*p == (unsigned char)SC_BREAK) {
  812.         if (tranfile && transtyle != TS_LOW) {
  813.         (void)fprintf(tranfile, "\tBREAK\n");
  814.         (void)fflush(tranfile);
  815.         }
  816.         (void)ioctl(fileno(portfile), TIOCSBRK, (caddr_t)0);
  817.         (void)sleep(1);
  818.         (void)ioctl(fileno(portfile), TIOCCBRK, (caddr_t)0);
  819.     }
  820.     else if (writechar(p) < 0)
  821.         return D_FATAL;
  822.     }
  823.     return D_OK;
  824. }
  825.  
  826.  
  827. do_recv(nrecv_str, recv_str)
  828. int nrecv_str;
  829. char *recv_str[];
  830. {
  831.     char    *timestr = recv_str[nrecv_str];
  832.     int        length;
  833.     int        i;
  834.     int        err;
  835.     int        timeout;
  836.  
  837.     if (tranfile && transtyle != TS_LOW) {
  838.     (void)fprintf(tranfile, "RECV ");
  839.     for (i = 1 ; i <= nrecv_str ; i++)
  840.         (void)fprintf(tranfile, "%s%c", recv_str[i],
  841.               (i == nrecv_str) ? '\n' : ' ');
  842.     (void)fflush(tranfile);
  843.     }
  844.  
  845.     recv_str++;
  846.     nrecv_str--;
  847.     /* Convert the strings. */
  848.     for (i = 0 ; i < nrecv_str ; i++) {
  849.     if ((length = canonstring(recv_str[i], recv_str[i])) < 0) {
  850.         syntax_error("Bad receive string format");
  851.         return D_FATAL;
  852.     }
  853.     if (length > MATCHLEN) {
  854.         syntax_error("Receive string too long");
  855.         return D_FATAL;
  856.     }
  857.     }
  858.  
  859.     /* Set up the timer */
  860.     if ((timeout = atoi(timestr)) < 0) {
  861.     syntax_error("Bad timeout value");
  862.     return D_FATAL;
  863.     }
  864.     if (timeout > 0)
  865.     (void)signal(SIGALRM, scripttimeout);
  866.  
  867.     if (setjmp(timerest) == 0) {
  868.     /* Do the matching. */
  869.     if (timeout > 0)
  870.         (void)alarm((unsigned int)timeout);
  871.     for ( ; ; ) {
  872.         for (i = 0 ; i < nrecv_str ; i++)
  873.         if ((err = matchtext(recv_str[i], timeout)) == D_OK)
  874.             break;
  875.         if (i < nrecv_str)
  876.         break;
  877.         if (getnextchar(timeout) == D_CONTIN)
  878.         longjmp(timerest, 2);
  879.     }
  880.     if (timeout > 0)
  881.         (void)alarm(0);
  882.  
  883.     switch (err) {
  884.     default:
  885.         return D_FATAL;
  886.     case D_OK:
  887.         return D_OK;
  888.     case D_NONFATAL:
  889.         io_error("EOF while trying match");
  890.         return D_NONFATAL;
  891.     case D_FATAL:
  892.         io_error("Read error while trying match");
  893.         return D_FATAL;
  894.     case D_INTRPT:
  895.         break;
  896.     }
  897.     }
  898.  
  899.     d_log(DLOG_INFO, WHERE, "No match for \"%s\"%s after %d seconds",
  900.     recv_str[0], nrecv_str > 1 ? " ..." : "", timeout);
  901.     return D_NONFATAL;
  902. }
  903.  
  904.  
  905. #define pushbackchar(c)        (*pushptr++ = (c))
  906.  
  907.  
  908. /*
  909. **  Check for a match between the string and the input stream.
  910. */
  911. int
  912. matchtext(p, timeout)
  913.     char    *p;
  914.     int        timeout;
  915. {
  916.     int        c;
  917.     int        i;
  918.  
  919.     if (*p == '\0')
  920.     return D_OK;
  921.     if ((c = getnextchar(timeout)) < D_OK)
  922.     return c;
  923.     
  924.     if (c == D_CONTIN)
  925.     return D_NOMATCH;
  926.  
  927.     if (c == *p) {
  928.     i = matchtext(++p, timeout);
  929.     if (i < D_OK)
  930.         pushbackchar(c);
  931.     return i;
  932.     }
  933.     pushbackchar(c);
  934.     return D_NOMATCH;
  935. }
  936.  
  937.  
  938. /*
  939. **  Read characters from the port in raw mode, or the pushback buffer.
  940. */
  941. int
  942. getnextchar(timeout)
  943. int timeout;
  944. {
  945.     int        i;
  946.     char    c;
  947.  
  948.     /* Use pushback if there is any. */
  949.     if (pushptr > pushbuffer) {
  950.     c = *--pushptr;
  951.     if (tranfile && transtyle != TS_LOW) {
  952.         if (isprint(c))
  953.         (void)fprintf(tranfile, "\t\tGET %4c (0%03o)\n", c, c);
  954.         else
  955.         (void)fprintf(tranfile, "\t\tGET 0x%02x (0%03o)\n", c, c);
  956.         (void)fflush(tranfile);
  957.     }
  958.     return c;
  959.     }
  960.  
  961.  
  962.     while ((i = readchar(timeout)) != EOF) {
  963.     /* filter out some of the junk characters */
  964.     c = toascii(i);
  965.     if (tranfile && transtyle != TS_LOW) {
  966.         if (isprint(c))
  967.         (void)fprintf(tranfile, "\t\tGET %4c (0%03o)\n", c, c);
  968.         else
  969.         (void)fprintf(tranfile, "\t\tGET 0x%02x (0%03o)\n", c, c);
  970.         (void)fflush(tranfile);
  971.     }
  972.     if (c != '\0' && c != '\177')
  973.         return c;
  974.     /* Ignore NUL and DEL because they are almost always just noise */
  975.     }
  976.  
  977.     if (i == EOF)
  978.     return D_CONTIN;    /* End of replay only... */
  979.     if (feof(portfile))
  980.     return D_NONFATAL;
  981.     if (errno == EINTR)
  982.     return D_INTRPT;
  983.     return D_FATAL;
  984. }
  985.  
  986.  
  987. /*
  988. **  Open a script.
  989. */
  990. int
  991. do_use(sname, nfields, fields)
  992.     char *sname;
  993.     int nfields;
  994.     char *fields[];
  995. {
  996.     register int    i;
  997.     OPENFILE        *op;
  998.     FILE        *F;
  999.  
  1000.     if (nopenfiles > MAXFILES) {
  1001.     syntax_error("Too many \"use\" commands");
  1002.     return D_FATAL;
  1003.     }
  1004.  
  1005.     /* Get the full filename. */
  1006.     (void)strcpy(scriptname,
  1007.          expand_dirs_file("$DPCONF_DIR:$DPSCRIPT_DIR", sname));
  1008.  
  1009.     /* Open it. */
  1010.     if ((F = fopen(scriptname, "r")) == NULL) {
  1011.     misc_error("Can't open script file \"%s\"", scriptname);
  1012.     return D_FATAL;
  1013.     }
  1014.     if (tranfile && transtyle != TS_LOW) {
  1015.     (void)fprintf(tranfile, "USING %s", scriptname);
  1016.     for (i = 0; i < nfields; i++)
  1017.         (void)fprintf(tranfile, " %s", fields[i]);
  1018.     (void)fprintf(tranfile, "\n");
  1019.     (void)fflush(tranfile);
  1020.     }
  1021.  
  1022.     /* If this isn't the first file, save the current state. */
  1023.     if (nopenfiles) {
  1024.     if ((op = (OPENFILE *)malloc(sizeof (OPENFILE))) == NULL) {
  1025.         misc_error("No space for openfile \"%s\"", scriptname);
  1026.         return D_FATAL;
  1027.     }
  1028.     op->linenumber = linenumber;
  1029.     op->scriptfile = scriptfile;
  1030.     op->scriptname = copystring(scriptname);
  1031.     for (op->nfields = use_nfields, i = 0; i < use_nfields; i++)
  1032.         op->fields[i] = use_fields[i];
  1033.     for (use_nfields = nfields - 1, i = 0; i < nfields; i++)
  1034.         use_fields[i] = copystring(fields[i + 1]);
  1035.     openfiles[nopenfiles - 1] = op;
  1036.     }
  1037.     nopenfiles++;
  1038.  
  1039.     scriptfile = F;
  1040.     linenumber = 0;
  1041.     return D_OK;
  1042. }
  1043.  
  1044.  
  1045. int
  1046. closescript()
  1047. {
  1048.     int        i;
  1049.     OPENFILE    *op;
  1050.  
  1051.     if (nopenfiles <= 0)
  1052.     return 0;
  1053.  
  1054.     if (scriptfile)
  1055.     (void)fclose(scriptfile);
  1056.     for (i = 0; i < use_nfields; i++)
  1057.     free(use_fields[i]);
  1058.  
  1059.     if (--nopenfiles > 0) {
  1060.     op = openfiles[nopenfiles - 1];
  1061.     scriptfile = op->scriptfile;
  1062.     (void)strcpy(scriptname, op->scriptname);
  1063.     free(op->scriptname);
  1064.     linenumber = op->linenumber;
  1065.     for (use_nfields = op->nfields, i = 0; i < use_nfields; i++)
  1066.         use_fields[i] = op->fields[i];
  1067.     free((char *)op);
  1068.     return nopenfiles;
  1069.     }
  1070.     return 0;
  1071. }
  1072.  
  1073.  
  1074.  
  1075. /*
  1076. **  Read characters from the TTY line and store them in a circular
  1077. **  buffer so that they can be replayed if desired.
  1078. */
  1079.  
  1080. do_replay()
  1081. {
  1082.     replay = 1;
  1083.     repcurr = repfirst;
  1084.     repcount = repsize;
  1085.     if (tranfile && transtyle != TS_LOW) {
  1086.     (void)fprintf(tranfile, "REPLAY\n");
  1087.     (void)fflush(tranfile);
  1088.     }
  1089. }
  1090.  
  1091.  
  1092. do_mark()
  1093. {
  1094.     replay = 0;
  1095.     repsize = 0;
  1096.     repfirst = 0;
  1097.     repnext = 0;
  1098.     if (tranfile && transtyle != TS_LOW) {
  1099.     (void)fprintf(tranfile, "SET MARK\n");
  1100.     (void)fflush(tranfile);
  1101.     }
  1102. }
  1103.  
  1104.  
  1105. static int
  1106. repskipline()
  1107. {
  1108.     for (; repbuff[repfirst] != '\n' && repsize > 0; repsize--)
  1109.     repfirst++;
  1110.  
  1111.     if (repsize <= 0)
  1112.     do_mark();
  1113.     else {
  1114.     repfirst++;
  1115.     repsize--;
  1116.     }
  1117. }
  1118.  
  1119.  
  1120. int
  1121. readchar(timeout)
  1122. int timeout;
  1123. {
  1124.     register int c;
  1125.  
  1126. Top:
  1127.     if (replay) {
  1128.     /* Replaying input.  Anything left? */
  1129.     if (repcount == 0) {
  1130.         replay = 0;
  1131.         goto Top;
  1132.     }
  1133.  
  1134.     c = repbuff[repcurr];
  1135.     if (++repcurr == REPBUFSIZE)
  1136.         repcurr = 0;
  1137.     repcount--;
  1138.  
  1139.     /* Don't replay EOF's.  They get in there when a timeout interrupts
  1140.      * a read. */
  1141.     if (c == EOF)
  1142.         goto Top;
  1143.     }
  1144.     else {
  1145.     /*
  1146.      * Don't issue a read unless a timeout is active.
  1147.      * We could wait forever.
  1148.      */
  1149.     if (!timeout)
  1150.         return EOF;
  1151.     if (repsize == REPBUFSIZE)
  1152.         /* Ran out of buffer space.  Rather then leave a partial line,
  1153.          * delete the first line in the buffer. */
  1154.         repskipline();
  1155.     else
  1156.         repsize++;
  1157.     repbuff[repnext] = c = getc(portfile);
  1158.     if (tranfile) {
  1159.         if (transtyle == TS_LOW)
  1160.         (void)fprintf(tranfile, "%s", seechar(c));
  1161.         else if (isprint(c))
  1162.         (void)fprintf(tranfile, "\tREAD    %c (0%03o)\n", c, c);
  1163.         else
  1164.         (void)fprintf(tranfile, "\tREAD 0x%02x (0%03o)\n", c, c);
  1165.         (void)fflush(tranfile);
  1166.     }
  1167.     if (++repnext == REPBUFSIZE)
  1168.         repnext = 0;
  1169.     }
  1170.     return c;
  1171. }
  1172.