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