home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 3 Comm / 03-Comm.zip / mincom15.zip / script.c < prev    next >
C/C++ Source or Header  |  1993-09-04  |  17KB  |  949 lines

  1. /*
  2.  * This file is part of the Minicom Communications Program,
  3.  * written by Miquel van Smoorenburg 1991/1992/1993.
  4.  *
  5.  * Runscript    Run a login-or-something script.
  6.  *        A basic like "programming language".
  7.  *        This program also looks like a basic interpreter :
  8.  *        a bit messy. (But hey, I'm no compiler writer :-))
  9.  *
  10.  * Version:    1.01
  11.  *
  12.  * Author:    Miquel van Smoorenburg, miquels@maestro.htsa.aha.nl
  13.  *
  14.  * Bugs:    The "expect" routine is, unlike gosub, NOT reentrant !
  15.  *
  16.  */
  17. #include <sys/types.h>
  18. #if defined (_POSIX_SOURCE) || defined(_BSD43)
  19. #include <stdlib.h>
  20. #include <unistd.h>
  21. #else
  22. char *getenv();
  23. #endif
  24. #include <string.h>
  25. #include <signal.h>
  26. #include <setjmp.h>
  27. #undef NULL
  28. #include <stdio.h>
  29.  
  30. /*
  31.  * Run a script.
  32.  */
  33.  
  34. #define OK    0
  35. #define ERR    -1
  36. #define RETURN    1
  37. #define BREAK    2
  38.  
  39. #ifndef lint
  40. char *Version = "@(#)Runscript V1.1 1992";
  41. #endif
  42.  
  43. struct line {
  44.   char *line;
  45.   int labelcount;
  46.   int lineno;
  47.   struct line *next;
  48. };
  49.  
  50. struct var {
  51.   char *name;
  52.   int value;
  53.   struct var *next;
  54. };
  55.  
  56. #define LNULL ((struct line *)0)
  57. #define VNULL ((struct var  *)0)
  58. #ifndef CNULL
  59. #define CNULL ((char *)0)
  60. #endif
  61.  
  62.  
  63. /*
  64.  * Structure describing the script we are currently executing.
  65.  */
  66. struct env {
  67.   struct line *lines;        /* Start of all lines */
  68.   struct var *vars;        /* Start of all variables */
  69.   char *scriptname;        /* Name of this script */
  70.   int verbose;            /* Are we verbose? */
  71.   jmp_buf ebuf;            /* For exit */
  72.   int exstat;            /* For exit */
  73. };
  74.   
  75. struct env *curenv;        /* Execution environment */  
  76. int gtimeout = 120;        /* Global Timeout */
  77. int etimeout = 0;        /* Timeout in expect routine */
  78. jmp_buf ejmp;            /* To jump to if expect times out */
  79. int inexpect = 0;        /* Are we in the expect routine */
  80. char *newline;            /* What to print for '\n'. */
  81. char *s_login = "name";        /* User's login name */
  82. char *s_pass = "password";    /* User's password */
  83. struct line *thisline;        /* Line to be executed */ 
  84. int laststatus = 0;        /* Status of last command */
  85.  
  86. /* Forward declarations */
  87. int s_exec();
  88. int execscript();
  89.  
  90. /*
  91.  * Print something formatted to stderr.
  92.  */
  93. /*VARARGS*/ 
  94. void s_error(fmt, a1, a2, a3)
  95. char *fmt;
  96. char *a1, *a2, *a3;
  97. {
  98.   fprintf(stderr, fmt, a1, a2, a3);
  99. }
  100.  
  101. /*
  102.  * Walk through the environment, see if LOGIN and/or PASS are present.
  103.  * If so, delete them. (Someone using "ps" might see them!)
  104.  */
  105. void init_env()
  106. {
  107.   extern char **environ;
  108.   char **e;
  109.  
  110.   for(e = environ; *e; e++) {
  111.     if (!strncmp(*e, "LOGIN=", 6)) {
  112.         s_login = (*e) + 6;
  113.         *e = "LOGIN=";
  114.     }
  115.     if (!strncmp(*e, "PASS=", 5)) {
  116.         s_pass = (*e) + 5;
  117.         *e = "PASS=";
  118.     }
  119.   }
  120. }
  121.  
  122.  
  123. /*
  124.  * Return an environment variable.
  125.  */
  126. char *mygetenv(env)
  127. char *env;
  128. {
  129.   if (!strcmp(env, "LOGIN")) return(s_login);
  130.   if (!strcmp(env, "PASS")) return(s_pass);
  131.   return(getenv(env));
  132. }
  133.  
  134. /*
  135.  * Display a syntax error and exit.
  136.  */
  137. void syntaxerr(s)
  138. char *s;
  139. {
  140.   s_error("script \"%s\": syntax error in line %d %s\r\n", curenv->scriptname,
  141.       thisline->lineno, s);
  142.   exit(1);    
  143. }
  144.  
  145. /*
  146.  * Skip all space
  147.  */
  148. void skipspace(s)
  149. char **s;
  150. {
  151.   while(**s == ' ' || **s == '\t') (*s)++;
  152. }
  153.   
  154. /*
  155.  * Our clock. This gets called every second.
  156.  */
  157. /*ARGSUSED*/
  158. void myclock(dummy)
  159. int dummy;
  160. {
  161.   signal(SIGALRM, myclock);
  162.   alarm(1);
  163.  
  164.   if (--gtimeout == 0) {
  165.       s_error("script \"%s\": global timeout\r\n", curenv->scriptname);
  166.       exit(1);
  167.   }    
  168.   if (inexpect && etimeout && --etimeout == 0) longjmp(ejmp, 1);
  169. }
  170.  
  171. /*
  172.  * Read a word and advance pointer.
  173.  * Also processes quoting, variable substituting, and \ escapes.
  174.  */
  175. char *getword(s)
  176. char **s;
  177. {
  178.   static char buf[81];
  179.   int len, f;
  180.   int idx = 0;
  181.   char *t = *s;
  182.   int sawesc = 0;
  183.   int sawq = 0;
  184.   char *env, envbuf[32];
  185.  
  186.   if (**s == 0) return(CNULL);
  187.  
  188.   for(len = 0; len < 81; len++) {
  189.       if (sawesc && t[len]) {
  190.           sawesc = 0;
  191.           if (t[len] <= '7' && t[len] >= '0') {
  192.               buf[idx] = 0;
  193.               for(f = 0; f < 4 && len < 81 && t[len] <= '7' &&
  194.                   t[len] >= '0'; f++) 
  195.                   buf[idx] = 8*buf[idx] + t[len++] - '0';
  196.               if (buf[idx] == 0) buf[idx] = '@';    
  197.               idx++; len--;
  198.               continue;
  199.           }
  200.           switch(t[len]) {
  201.               case 'r':
  202.                   buf[idx++] = '\r';
  203.                   break;
  204.               case 'n':
  205.                   buf[idx++] = '\n';
  206.                   break;
  207.               case 'b':
  208.                   buf[idx++] = '\b';
  209.                   break;
  210. #ifndef _HPUX_SOURCE
  211.               case 'a':
  212.                   buf[idx++] = '\a';    
  213.                   break;
  214. #endif
  215.               case 'f':
  216.                   buf[idx++] = '\f';
  217.                   break;
  218.               case 'c':
  219.                   buf[idx++] = 255;
  220.                   break;
  221.               default:
  222.                   buf[idx++] = t[len];
  223.                   break;
  224.           }    
  225.           sawesc = 0;
  226.           continue;
  227.       }
  228.       if (t[len] == '\\') {
  229.           sawesc = 1;
  230.           continue;
  231.       }
  232.       if (t[len] == '"') {
  233.           if (sawq == 1) {
  234.               sawq = 0;
  235.               len++;
  236.               break;
  237.           }    
  238.           sawq = 1;
  239.           continue;
  240.       }
  241.       if (t[len] == '$' && t[len + 1] == '(') {
  242.           for(f = len; t[f] && t[f] != ')'; f++)
  243.               ;
  244.           if (t[f] == ')') {
  245.               strncpy(envbuf, &t[len + 2], f - len - 2);
  246.               envbuf[f - len - 2] = 0;
  247.               len = f;
  248.               env = mygetenv(envbuf);
  249.               if (env == CNULL) env = "";
  250.               while(*env) buf[idx++] = *env++;
  251.               continue;
  252.           }
  253.       }
  254.       if((!sawq && (t[len] == ' ' || t[len] == '\t')) || t[len] == 0) break;
  255.       buf[idx++] = t[len];
  256.   }
  257.   buf[idx] = 0;    
  258.   (*s) += len;
  259.   skipspace(s);    
  260.   if (sawesc || sawq) syntaxerr("(badly formed word)");
  261.   return(buf);
  262. }
  263.  
  264. /*
  265.  * Save a string to memory. Strip trailing '\n'.
  266.  */
  267. char *strsave(s)
  268. char *s;
  269. {
  270.   char *t;
  271.   int len;
  272.  
  273.   len = strlen(s);
  274.   if (len && s[len - 1] == '\n') s[--len] = 0;
  275.   if ((t = (char *)malloc(len + 1)) == (char *)0) return(t);
  276.   strcpy(t, s);
  277.   return(t);
  278. }
  279.  
  280. /*
  281.  * Throw away all malloced memory.
  282.  */
  283. void freemem()
  284. {
  285.   struct line *l;
  286.   struct var *v;
  287.  
  288.   for(l = curenv->lines; l; l = l->next) {
  289.       free((char *)l->line);
  290.       free((char *)l);
  291.   }
  292.   for(v = curenv->vars; v; v = v->next) {
  293.       free(v->name);
  294.       free((char *)v);
  295.   }
  296. }
  297.  
  298. /*
  299.  * Read a script into memory.
  300.  */
  301. int readscript(s)
  302. char *s;
  303. {
  304.   FILE *fp;
  305.   struct line *tl, *prev = LNULL;
  306.   char *t;
  307.   char buf[81];
  308.   int no = 0;
  309.  
  310.   if ((fp = fopen(s, "r")) == (FILE *)0) {
  311.       s_error("runscript: couldn't open %s\r\n", s);
  312.       exit(1);
  313.   }
  314.   
  315.   /* Read all the lines into a linked list in memory. */
  316.   while((t = fgets(buf, 80, fp)) != CNULL) {
  317.       no++;
  318.       skipspace(&t);
  319.       if (*t == '\n') continue;
  320.       if (  ((tl = (struct line *)malloc(sizeof (struct line))) == LNULL) ||
  321.             ((tl->line = strsave(t)) == CNULL)) {
  322.               s_error("script %s: out of memory\r\n", s);
  323.               exit(1);
  324.       }
  325.       if (prev)
  326.           prev->next = tl;
  327.       else
  328.           curenv->lines = tl;
  329.       tl->next = (struct line *)0;
  330.       tl->labelcount = 0;
  331.       tl->lineno = no;
  332.       prev = tl;
  333.   }
  334.   return(0);
  335. }
  336.  
  337. /*
  338.  * Read the next character, and return a string of characters last read.
  339.  */
  340. char *readchar()
  341. {
  342.   static char buf[65];
  343.   static int bufp = 64;
  344.   char c;
  345.  
  346.   buf[64] = 0;
  347.   
  348. #ifdef _SYSV
  349.   memcpy(buf, buf + 1, 63);
  350. #else
  351.   /* This is Posix, I believe. */
  352.   memmove(buf, buf + 1, 63);
  353. #endif
  354.  
  355.   if (bufp) bufp--;    
  356.   while(read(0, &c, 1) != 1)
  357.       ;
  358.   if (curenv->verbose) fputc(c, stderr);    
  359.   buf[63] = c;
  360.   return(buf + bufp);
  361. }
  362.  
  363. /*
  364.  * See if an expected string has yet been seen.
  365.  */
  366. int expfound(already, seek)
  367. char *already, *seek;
  368. {
  369.   int len1, len2;
  370.   int start = 0;
  371.  
  372.   len1 = strlen(already);
  373.   len2 = strlen(seek);
  374.   
  375.   if (len1 > len2) start = len1 - len2;
  376.   return(!strcmp(already + start, seek));
  377. }
  378.  
  379. /*
  380.  * Send text to a file (stdout or stderr).
  381.  */
  382. int output(text, fp)
  383. char *text;
  384. FILE *fp;
  385. {
  386.   unsigned char *w;
  387.   int first = 1;
  388.   int donl = 1;
  389.  
  390.   while((w = (unsigned char *)getword(&text)) != (unsigned char *)0) {
  391.       if (!first) fputc(' ', fp);
  392.       first = 0;
  393.       for(; *w; w++) {
  394.           if (*w == 255) {
  395.               donl = 0;
  396.               continue;
  397.           }
  398.           if (*w == '\n')
  399.               fputs(newline, fp);
  400.           else    
  401.               fputc(*w, fp);
  402.       }
  403.   }
  404.   if (donl) fputs(newline, fp);
  405.   fflush(fp);
  406.   return(OK);
  407. }
  408.  
  409. /*
  410.  * Find a variable in the list.
  411.  * If it is not there, create it.
  412.  */
  413. struct var *getvar(name, cr)
  414. char *name;
  415. int cr;
  416. {
  417.   struct var *v, *end = VNULL;
  418.   
  419.   for(v = curenv->vars; v; v = v->next) {
  420.       end = v;
  421.       if (!strcmp(v->name, name)) return(v);
  422.   }
  423.   if (!cr) {
  424.       s_error("script \"%s\" line %d: unknown variable \"%s\"\r\n",
  425.           curenv->scriptname, thisline->lineno, name);
  426.       exit(1);
  427.   }
  428.   if ((v = (struct var *)malloc(sizeof(struct var))) == VNULL ||
  429.       (v->name = strsave(name)) == CNULL) {
  430.       s_error("script \"%s\": out of memory\r\n", curenv->scriptname);
  431.       exit(1);
  432.   }
  433.   if (end)
  434.     end->next = v;
  435.   else
  436.       curenv->vars = v;    
  437.   v->next = VNULL;
  438.   v->value = 0;
  439.   return(v);
  440. }
  441.  
  442. /*
  443.  * Read a number or variable.
  444.  */
  445. int getnum(text)
  446. char *text;
  447. {
  448.   int val;
  449.  
  450.   if (!strcmp(text, "$?")) return(laststatus);
  451.   if ((val = atoi(text)) != 0 || *text == '0') return(val);
  452.   return(getvar(text, 0)->value);
  453. }
  454.  
  455.       
  456. /*
  457.  * Get the lines following "expect" into memory.
  458.  */
  459. struct line **buildexpect()
  460. {
  461.   static struct line *seq[17];
  462.   int f;
  463.   char *w, *t;
  464.   
  465.   for(f = 0; f < 16; f++) {
  466.       if (thisline == LNULL) {
  467.           s_error("script \"%s\": unexpected end of file\r\n", 
  468.               curenv->scriptname);
  469.           exit(1);    
  470.       }
  471.       t = thisline->line;
  472.       w = getword(&t);
  473.       if (!strcmp(w, "}")) {
  474.           if (*t) syntaxerr("(garbage after })"); 
  475.           return(seq);
  476.       }
  477.       seq[f] = thisline;
  478.       thisline = thisline->next;
  479.   }
  480.   if (f == 16) syntaxerr("(too many arguments)");
  481.   return(seq);
  482. }
  483.  
  484. /*
  485.  * Our "expect" function.
  486.  */
  487. int expect(text)
  488. char *text;
  489. {
  490.   char *s, *w;
  491.   struct line **seq;
  492.   struct line oneline;
  493.   struct line *dflseq[2];
  494.   char *toact = "exit 1";
  495.   int found = 0, f, val, c;
  496.   char *already, *action = CNULL;
  497.  
  498.   if (inexpect) {
  499.       s_error("script \"%s\" line %d: nested expect\r\n", curenv->scriptname,
  500.           thisline->lineno);
  501.       exit(1);
  502.   }
  503.   etimeout = 120;
  504.   inexpect = 1;
  505.  
  506.   s = getword(&text);
  507.   if (!strcmp(s, "{")) {
  508.       if (*text) syntaxerr("(garbage after {)");
  509.       thisline = thisline->next;
  510.       seq = buildexpect();
  511.   } else {
  512.      dflseq[1] = LNULL;
  513.      dflseq[0] = &oneline;
  514.      oneline.line = text;
  515.      oneline.next = LNULL;
  516.   }
  517.   /* Seek a timeout command */
  518.   for(f = 0; seq[f]; f++) {
  519.       if (!strncmp(seq[f]->line, "timeout", 7)) {
  520.           c = seq[f]->line[7];
  521.           if (c == 0 || (c != ' ' && c != '\t')) continue;
  522.           s = seq[f]->line + 7;
  523.           seq[f] = LNULL;
  524.           skipspace(&s);
  525.           w = getword(&s);
  526.           if (w == CNULL) syntaxerr("(argument expected)");
  527.           val = getnum(w);
  528.           if (val == 0) syntaxerr("(invalid argument)");
  529.           etimeout = val;
  530.           skipspace(&s);
  531.           if (*s != 0) toact = s;
  532.           break;
  533.     }
  534.   }
  535.   if (setjmp(ejmp) != 0) {
  536.       f = s_exec(toact);
  537.       inexpect = 0;
  538.       return(f);
  539.   }
  540.  
  541.   /* Allright. Now do the expect. */
  542.   c = OK;
  543.   while(!found) {
  544.       action = CNULL;
  545.       already = readchar();
  546.       for(f = 0; seq[f]; f++) {
  547.           s = seq[f]->line;
  548.           w = getword(&s);
  549.           if (expfound(already, w)) {
  550.               action = s;
  551.               found = 1;
  552.               break;
  553.           }
  554.       }
  555.       if (action != CNULL && *action) {
  556.           found = 0;
  557.           /* Maybe BREAK or RETURN */
  558.           if ((c = s_exec(action)) != OK) found = 1;
  559.       }
  560.   }
  561.   inexpect = 0;
  562.   etimeout = 0;
  563.   return(c);
  564. }
  565.  
  566. /*
  567.  * Jump to a shell and run a command.
  568.  */
  569. int shell(text)
  570. char *text;
  571. {
  572.   laststatus = system(text);
  573.   return(OK);
  574. }
  575.  
  576. /*
  577.  * Send output to stdout ( = modem)
  578.  */
  579. int dosend(text)
  580. char *text;
  581. {
  582.   newline = "\r";
  583.   return(output(text, stdout));
  584. }
  585.  
  586. /*
  587.  * Exit from the script, possibly with a value.
  588.  */
  589. int doexit(text)
  590. char *text;
  591. {
  592.   char *w;
  593.   int ret = 0;
  594.  
  595.   w = getword(&text);
  596.   if (w != CNULL) ret = getnum(w);
  597.   curenv->exstat = ret;
  598.   longjmp(curenv->ebuf, 1);
  599.   return(0);
  600. }
  601.  
  602. /*
  603.  * Goto a specific label.
  604.  */
  605. int dogoto(text)
  606. char *text;
  607. {
  608.   char *w;
  609.   struct line *l;
  610.   char buf[32];
  611.   int len;
  612.  
  613.   w = getword(&text);
  614.   if (w == CNULL || *text) syntaxerr("(in goto/gosub label)");
  615.   sprintf(buf, "%s:", w);
  616.   len = strlen(buf);
  617.   for(l = curenv->lines; l; l = l->next) if (!strncmp(l->line, buf, len)) break;
  618.   if (l == LNULL) {
  619.       s_error("script \"%s\" line %d: label \"%s\" not found\r\n",
  620.           curenv->scriptname, thisline->lineno, w);
  621.       exit(1);
  622.   }
  623.   thisline = l;
  624.   /* We return break, to automatically break out of expect loops. */
  625.   return(BREAK);
  626. }
  627.  
  628. /*
  629.  * Goto a subroutine.
  630.  */
  631. int dogosub(text)
  632. char *text;
  633. {
  634.   struct line *oldline;
  635.   int ret = OK;
  636.  
  637.   oldline = thisline;
  638.   dogoto(text);
  639.   
  640.   while(ret != ERR) {
  641.     if ((thisline = thisline->next) == LNULL) {
  642.         s_error("script \"%s\": no return from gosub\r\n", 
  643.                     curenv->scriptname);
  644.         exit(1);
  645.     }
  646.     ret = s_exec(thisline->line);
  647.     if (ret == RETURN) {
  648.         ret = OK;
  649.         thisline = oldline;
  650.         break;
  651.     }
  652.   }
  653.   return(ret);
  654. }
  655.  
  656. /*
  657.  * Return from a subroutine.
  658.  */
  659. int doreturn(text)
  660. char *text;
  661. {
  662.   return(RETURN);
  663. }
  664.  
  665. /*
  666.  * Print text to stderr.
  667.  */
  668. int print(text)
  669. char *text;
  670. {
  671.   newline = "\r\n";
  672.  
  673.   return(output(text, stderr));
  674. }
  675.  
  676. /*
  677.  * Declare a variable (integer)
  678.  */
  679. int doset(text)
  680. char *text;
  681. {
  682.   char *w;
  683.   struct var *v;
  684.  
  685.   w = getword(&text);
  686.   if (w == CNULL) syntaxerr("(missing var name)");
  687.   v = getvar(w, 1);
  688.   if (*text) v->value = getnum(getword(&text));
  689.   return(OK);
  690.  
  691. /*
  692.  * Lower the value of a variable.
  693.  */
  694. int dodec(text)
  695. char *text;
  696. {
  697.   char *w;
  698.   struct var *v;
  699.   
  700.   w = getword(&text);
  701.   if (w == CNULL)  syntaxerr("(expected variable)");
  702.   v = getvar(w, 0);
  703.   v->value--;
  704.   return(OK);
  705. }
  706.  
  707. /*
  708.  * Increase the value of a variable.
  709.  */
  710. int doinc(text)
  711. char *text;
  712. {
  713.   char *w;
  714.   struct var *v;
  715.   
  716.   w = getword(&text);
  717.   if (w == CNULL)  syntaxerr("(expected variable)");
  718.   v = getvar(w, 0);
  719.   v->value++;
  720.   return(OK);
  721. }
  722.  
  723. /*
  724.  * If syntax: if n1 [><=] n2 command.
  725.  */
  726. int doif(text)
  727. char *text;
  728. {
  729.   char *w;
  730.   int n1;
  731.   int n2;
  732.   char op;
  733.   
  734.   if ((w = getword(&text)) == CNULL) syntaxerr("(if)");
  735.   n1 = getnum(w);
  736.   if ((w = getword(&text)) == CNULL) syntaxerr("(if)");
  737.   if (*w == 0 || w[1] != 0) syntaxerr("(if)");
  738.   op = *w;
  739.   if ((w = getword(&text)) == CNULL) syntaxerr("(if)");
  740.   n2 = getnum(w);
  741.   if (!*text) syntaxerr("(expected command after if)");
  742.   
  743.   if (op == '=') {
  744.       if (n1 != n2) return(OK);
  745.   } else if (op == '>') {
  746.       if (n1 <= n2) return(OK);
  747.   } else if (op == '<') {
  748.       if (n1 >= n2) return(OK);
  749.   } else {
  750.       syntaxerr("(unknown operator)");
  751.   }
  752.   return(s_exec(text));
  753. }
  754.  
  755. /*
  756.  * Set the global timeout-time.
  757.  */
  758. int dotimeout(text)
  759. char *text;
  760. {
  761.   char *w;
  762.   int val;
  763.  
  764.   w = getword(&text);
  765.   if(w == CNULL) syntaxerr("(argument expected)");
  766.   if ((val = getnum(w)) == 0) syntaxerr("(invalid argument)");
  767.   gtimeout = val;
  768.   return(OK);
  769. }
  770.  
  771. /*
  772.  * Turn verbose on/off (= echo stdin to stderr)
  773.  */
  774. int doverbose(text)
  775. char *text;
  776. {
  777.   char *w;
  778.  
  779.   curenv->verbose = 1;
  780.  
  781.   if ((w = getword(&text)) != CNULL) {
  782.     if (!strcmp(w, "on")) return(OK);
  783.     if (!strcmp(w, "off")) {
  784.         curenv->verbose = 0;
  785.         return(OK);
  786.     }
  787.   }    
  788.   syntaxerr("(unexpected argument)");
  789.   return(ERR);
  790. }
  791.  
  792. /*
  793.  * Sleep for a certain number of seconds.
  794.  */
  795. int dosleep(text)
  796. char *text;
  797. {
  798.   int foo, tm;
  799.   
  800.   tm = getnum(text);
  801.   
  802.   foo = gtimeout - tm;
  803.   
  804.   /* The alarm goes off every second.. */
  805.   while(gtimeout != foo) pause();
  806.   return(OK);
  807. }
  808.  
  809. /*
  810.  * Break out of an expect loop.
  811.  */
  812. int dobreak()
  813. {
  814.   if (!inexpect) {
  815.       s_error("script \"%s\" line %d: break outside of expect\r\n",
  816.           curenv->scriptname, thisline->lineno);
  817.       exit(1);
  818.   }
  819.   return(BREAK);
  820. }
  821.  
  822. /*
  823.  * Call another script!
  824.  */
  825. int docall(text)
  826. char *text;
  827. {
  828.   struct line *oldline;
  829.   struct env *oldenv;
  830.   int er;
  831.  
  832.   if (*text == 0) syntaxerr("(argument expected)");
  833.  
  834.   if (inexpect) {
  835.       s_error("script \"%s\" line %d: call inside expect\r\n",
  836.               curenv->scriptname);
  837.       exit(1);
  838.   }
  839.  
  840.   oldline = thisline;
  841.   oldenv = curenv;
  842.   if ((er = execscript(text)) != 0) exit(er);
  843.   freemem();
  844.   thisline = oldline;
  845.   curenv = oldenv;
  846.   return(0);
  847. }
  848.  
  849.   
  850. /* KEYWORDS */
  851. struct kw {
  852.   char *command;
  853.   int (*fn)();
  854. } keywords[] = {
  855.   { "expect",    expect },
  856.   { "send",    dosend },
  857.   { "!",    shell },
  858.   { "goto",    dogoto },
  859.   { "gosub",    dogosub },
  860.   { "return",    doreturn },
  861.   { "exit",    doexit },
  862.   { "print",    print },
  863.   { "set",    doset },
  864.   { "inc",    doinc },
  865.   { "dec",    dodec },
  866.   { "if",    doif },
  867.   { "timeout",    dotimeout },
  868.   { "verbose",    doverbose },
  869.   { "sleep",    dosleep },
  870.   { "break",    dobreak },
  871.   { "call",    docall },
  872.   { (char *)0,    (int(*)())0 },
  873. };
  874.  
  875. /*
  876.  * Execute one line.
  877.  */
  878. int s_exec(text)
  879. char *text;
  880. {
  881.   char *w;
  882.   struct kw *k;
  883.  
  884.   w = getword(&text);
  885.  
  886.   /* If it is a label or a comment, skip it. */
  887.   if (w == CNULL || *w == '#' || w[strlen(w) - 1] == ':') return(OK);
  888.   
  889.   /* See which command it is. */
  890.   for(k = keywords; k->command; k++)
  891.       if (!strcmp(w, k->command)) break;
  892.  
  893.   /* Command not found? */
  894.   if (k->command == (char *)NULL) {
  895.     s_error("script \"%s\" line %d: unknown command \"%s\"\r\n",
  896.           curenv->scriptname, thisline->lineno, w);
  897.     exit(1);
  898.   }
  899.   return((*(k->fn))(text));
  900. }
  901.  
  902. /*
  903.  * Run the script by continously executing "thisline".
  904.  */
  905. int execscript(s)
  906. char *s;
  907. {
  908.   struct env env;
  909.   int ret;
  910.  
  911.   curenv = &env;
  912.   curenv->lines = LNULL;
  913.   curenv->vars  = VNULL;
  914.   curenv->verbose = 1;
  915.   curenv->scriptname = s;
  916.  
  917.   if (readscript(s) < 0) {
  918.       freemem();
  919.       return(ERR);
  920.   }
  921.   
  922.   signal(SIGALRM, myclock);
  923.   alarm(1);
  924.   if (setjmp(curenv->ebuf) == 0) {
  925.     thisline = curenv->lines;
  926.     while(thisline != LNULL && (ret == s_exec(thisline->line)) != ERR)
  927.           thisline = thisline->next;
  928.   } else
  929.       ret = curenv->exstat ? ERR : 0;        
  930.   return(ret);
  931. }
  932.  
  933. int main(argc, argv)
  934. int argc;
  935. char **argv;
  936. {
  937.   signal(SIGHUP, SIG_IGN);
  938.  
  939.   init_env();
  940.  
  941.   if (argc < 2) {
  942.       s_error("Usage: runscript <scriptfile>\r\n");
  943.       return(1);
  944.   }
  945.  
  946.   return(execscript(argv[1]));
  947. }
  948.