home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3597 / xcscrpt.c
Encoding:
C/C++ Source or Header  |  1991-07-11  |  36.7 KB  |  1,637 lines

  1. /*    xcscrpt.c -- script interpreter module for XC
  2.     This file uses 4-character tabstops
  3.     Author: larry gensch, December 4, 1987
  4.     Major rewrite: fred buck, Jan 1989
  5.     This code is released to the public domain
  6. */
  7.  
  8. #include <stdio.h>
  9. #include <sys/types.h>
  10. #include <sys/times.h>
  11. #include <sys/param.h>
  12. #include <ctype.h>
  13. #include <signal.h>
  14. #include <setjmp.h>
  15. #include <sys/stat.h>
  16. #include <termio.h>
  17. #ifdef T6000
  18. #include <sys/ioctl.h>
  19. #endif
  20. #include "xc.h"
  21.  
  22. #define    MAX_LINE    128
  23.  
  24. jmp_buf here;
  25. FILE *cf;
  26. short tty_flag,
  27.     echo_flag = FALSE,
  28.     captflag = FALSE,
  29.     linkflag = FALSE,
  30.     scriptflag = FALSE,
  31.     mrbstart,            /* ring buffer start pointer */
  32.     mrbcount;            /* ring buffer counter */
  33. extern short eof_flag;
  34. extern int    redial(), s_exit(), xmitbrk();
  35. char mringbuf[LG_BUFF];    /* ring buffer for modem input */
  36. static void unsetall(), S_bombout(), S_call();
  37. extern void divert(), set_onoff(), xcdial();
  38.  
  39. static void newsigint()
  40. {
  41.     signal(SIGINT, SIG_IGN);
  42.  
  43.     eof_flag++;
  44.     show_abort();
  45.     S_bombout();
  46.     longjmp(here,1);
  47. }
  48.  
  49. void do_script(file)
  50. char *file;
  51. {
  52.     captflag = FALSE;
  53.     tty_flag = TRUE;
  54.     scriptflag = TRUE;
  55.     eof_flag = FALSE;
  56.  
  57.     if (setjmp(here) == 0){
  58.         signal(SIGINT, newsigint);
  59.         intdel(TRUE);
  60.         S_call(file);
  61.     }
  62.     unsetall();
  63.     if (captflag)
  64.         fclose(cf);
  65.  
  66.     linkflag = 0;
  67.     scriptflag = FALSE;
  68.  
  69.     signal(SIGINT, SIG_IGN);
  70.     intdel(FALSE);
  71.  
  72.     return;
  73. }
  74.  
  75. static k_seen(bytes,fword)
  76. long int bytes;
  77. char *fword;
  78. {
  79.     int i, j, k;
  80.     char *cptr;
  81.  
  82.     cptr = fword;
  83.  
  84.     if (!fword || !*fword){
  85.         show(2,"No argument to SEEN command");
  86.         return -1;
  87.     }
  88.     j = mrbstart - 1;
  89.     if (bytes<=0 || bytes>LG_BUFF)
  90.         bytes = LG_BUFF;
  91.     k = mrbcount - bytes;    /* check only most recent 'bytes' bytes */
  92.     i = 0;
  93.     while ((i++)<mrbcount){
  94.         ++j;
  95.         j = j % LG_BUFF;
  96.         if (i<k)
  97.             continue;
  98.         if (mringbuf[j] != *cptr){
  99.             cptr = fword;
  100.             continue;
  101.         }
  102.         if (*(++cptr)=='\0')
  103.             return 0;
  104.     }
  105.     return -1;
  106. }
  107.  
  108. k_waitfor(interval,fword)
  109. long interval;
  110. char *fword;
  111. {
  112.     register c, i = -1 ;
  113.     register long limit, waitfor_msec = 0;
  114.     char *ptr = NULLS;
  115.     extern void s_cis();
  116.     struct tms tbuf;
  117.  
  118.     mrbstart = mrbcount = 0;
  119.     sprintf(line,"\"%s\"",fword);
  120.     lptr = line;
  121.     getword();
  122.     lc_word(word);
  123.  
  124.     if (interval < -1) {
  125.         waitfor_msec = -interval;
  126.         goto SPITOUT;
  127.     }
  128.     if (!word || word[0] == '\0'){
  129.         show(2,"No argument to WAITFOR command");
  130.         return -1;
  131.     }
  132.  
  133.     waitfor_msec = 1000 * ((interval > 0) ? interval : 30);
  134.     eof_flag = FALSE;
  135.  
  136. SPITOUT: limit = times(&tbuf) + (HZ * waitfor_msec)/1000;
  137.     while (limit >= times(&tbuf) && !eof_flag){
  138.         if ((c = read_mbyte(1)) == -1)
  139.             continue;
  140.  
  141.         if (cismode && c==ENQ){
  142.             s_cis();
  143.             goto SPITOUT;
  144.         }
  145.  
  146.         ++i;
  147.         i = i % LG_BUFF;
  148.         mringbuf[i] = c;
  149.         mrbstart = mrbstart % LG_BUFF;
  150.         if (mrbcount<LG_BUFF)
  151.             ++mrbcount;
  152.         else {
  153.             ++mrbstart;
  154.             mrbstart = mrbstart % LG_BUFF;
  155.         }
  156.  
  157.         if (tty_flag)
  158.             fputc(c,tfp);
  159.  
  160.         if (captflag && c != '\r')
  161.             fputc(c,cf);
  162.  
  163.         if (tolower(c) != *ptr){
  164.             ptr = word;
  165.             continue;
  166.         }
  167.  
  168.         if (*++ptr == '\0')
  169.             return 0;
  170.     }
  171.     return -1;
  172. }
  173.  
  174. static k_transmit(junk,fword)
  175. long int junk;
  176. char *fword;
  177. {
  178.     sprintf(line,"\"%s\"",fword);
  179.     lptr = line;
  180.     getword();
  181.     if (!fword || fword[0] == '\0'){
  182.         show(2,"No argument to TRANSMIT command");
  183.         return -1;
  184.     }
  185.     send_slowly(word);
  186.     return 0;
  187. }
  188.  
  189. static k_pause(pause_time,junk)
  190. long int pause_time;
  191. char *junk;
  192. {
  193.     pause_time = pause_time ? pause_time : 5;
  194.     sleep((unsigned)pause_time);
  195.     return 0;
  196. }
  197.  
  198. static k_dial(junk,fword)
  199. long int junk;
  200. char *fword;
  201. {
  202.     sprintf(line,"%s",fword);
  203.     lptr = line;
  204.     getword();
  205.     if (!word || word[0] == '\0'){
  206.         show(2,"DIAL command must have an argument");
  207.         return -1;
  208.     }
  209.     xcdial(word);
  210.     return 0;
  211. }
  212.  
  213. static k_capture(junk,fword)
  214. long int junk;
  215. char *fword;
  216. {
  217.     int val = captflag;
  218.  
  219.     sprintf(word,"capture");
  220.     sprintf(line,"%s",fword);
  221.     lptr = line;
  222.     set_onoff(&captflag);
  223.  
  224.     if (val == captflag)
  225.         return 0;
  226.  
  227.     if (captflag == 0)
  228.         fclose(cf);
  229.     else {
  230.         if ((cf = fopen(captfile, "a")) == NULLF) {
  231.             sprintf(Msg,"Can't open capture file %s",captfile);
  232.             S2;
  233.             eof_flag++;
  234.             return -1;
  235.         }
  236.     }
  237.     return 0;
  238. }
  239.  
  240. static k_echo(junk,fword)
  241. long int junk;
  242. char *fword;
  243. {
  244.     sprintf(word,"debug");
  245.     sprintf(line,"%s",fword);
  246.     lptr = line;
  247.     set_onoff(&echo_flag);
  248.     return 0;
  249. }
  250.  
  251. static k_tty(junk,fword)
  252. long int junk;
  253. char *fword;
  254. {
  255.     sprintf(word,"tty");
  256.     sprintf(line,"%s",fword);
  257.     lptr = line;
  258.     set_onoff(&tty_flag);
  259.     return 0;
  260. }
  261.  
  262. static k_type(junk,fword)
  263. long int junk;
  264. char *fword;
  265. {
  266.     sprintf(line,"%s",fword);
  267.     lptr = line;
  268.     getword();
  269.     if (!word || word[0] == '\0'){
  270.         show(2,"TYPE command must have an argument");
  271.         return -1;
  272.     }
  273.     divert(TRUE);
  274.     return 0;
  275. }
  276.  
  277. static k_linked()
  278. {
  279.     return(!!(linkflag) - 1);
  280. }
  281.  
  282. extern FILE *cf;
  283. extern short echo_flag;
  284.  
  285. short BREAK = 0;        /* a hook for a later 'trap' keyword */
  286.  
  287. /*    Variables Section */
  288. /*    Most of the variable-handling logic is credit: Steve Manes 1987 */
  289. #define SUCCEED        0
  290. #define FAIL        -1
  291. #define VNAMELEN    8    /* maximum name length for variables */
  292. #define VMAXSIZE    256    /* maximum length for CHAR variable */
  293. #define VMAXVARS    30    /* maximum number of user variables */
  294. #define VCHAR        'C'    /* CHARACTER variable type */
  295. #define VNUM        'N'    /* NUMERIC variable type (always 'long') */
  296.  
  297. /* Variable structure */
  298. typedef struct var {
  299.     char name[VNAMELEN+1];    /* variable name */
  300.     struct var    *next;        /* ptr to next structure in var_list */
  301.     char type;                /* variable type */
  302.     union {                    /* pointer to CHAR or NUM/DATE */
  303.         char str[VMAXSIZE+1];
  304.         long num;
  305.     } u;
  306. } VAR;
  307.  
  308. #define NULLV    (VAR *)0
  309. static VAR    *Varlist = NULLV;    /* top of variable list */
  310. static VAR    *Lastvar = NULLV;    /* bottom of variable list */
  311.  
  312. /* Valid variable name characters */
  313. unsigned char OKname[]=
  314. {
  315.      /* control characters */
  316.       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  317.      /* ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ */
  318.         1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
  319.      /* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` */
  320.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,
  321.      /* a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~       */
  322.         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0
  323. };
  324.  
  325. /* Macros */
  326. #define ISNAME(c)    (OKname[ ((c) & 0x7F) ])
  327.  
  328. /*    Return pointer to VAR structure for User or System variable
  329.     'name', otherwise return NULLV.
  330.     Variable contents in vp->u.[str|num]
  331. */
  332. static VAR *findvar(name)
  333. char *name;
  334. {
  335.     static    VAR *vp;    /* pointer to output structure */
  336.  
  337.     if (!name || !*name || !Varlist) return(NULLV);
  338.     vp = Varlist;
  339.     while (vp != NULLV) {
  340.         if (!strncmp(name, vp->name, VNAMELEN) )
  341.             return(vp);
  342.         vp = vp->next;
  343.     }
  344.     return(NULLV);                /* not found */
  345. }
  346.  
  347. /*    Delete User variable 'name' from VAR list.
  348.     If variable doesn't exist, no error is returned
  349. */
  350. static void unsetvar(name)
  351. char *name;
  352. {
  353.     VAR *p;
  354.     VAR *lastp;
  355.  
  356.     if (!name || !*name) return;
  357.     for (p=Varlist; p != (VAR*)0; lastp = p, p = p->next) {
  358.         if (!strncmp(name, p->name, VNAMELEN) ) {    /* name match? */
  359.             if (Varlist == Lastvar)                    /* only 1 variable */
  360.                 Varlist = Lastvar = (VAR*)0;        /* in list */
  361.             else if (p == Varlist)                    /* first variable */
  362.                 Varlist = p->next;
  363.             else {
  364.                 lastp->next = p->next;                /* dump variable in middle
  365.                                                      * of list */
  366.                 if (p == Lastvar)                    /* or last object */
  367.                     Lastvar = lastp;                /* in list */
  368.             }
  369.             free(p);            /* reclaim memory */
  370.             break;
  371.         }
  372.     }
  373. }
  374.  
  375. /*    Set the value of User variable 'name' to 'val' with 'type'.
  376.     If variable exists, change its contents. Otherwise, create it.
  377.     Returns: FAIL or SUCCEED
  378. */
  379. static setvar(name, val, type)
  380. char *name, *val;
  381. char type;
  382. {
  383.     VAR *vp;
  384.     short i;
  385.  
  386.     if (!name || !*name)
  387.         return FAIL;
  388.     if ((vp = findvar(name)) == NULLV) { /* create new variable */
  389.         for (i=0; i < VNAMELEN && name[i]; i++) {
  390.             if ( !ISNAME(name[i]) || isdigit(name[0]) ) {
  391.                 sprintf(Msg,"Illegal variable name '%s'",name);
  392.                 S_abort();
  393.             }
  394.         }
  395.         if (!(vp = (VAR *)malloc(sizeof(VAR)))) {
  396.             sprintf(Msg,"%s: allocation error",name);
  397.             S2;
  398.             return FAIL;
  399.         }
  400.         lc_word(name);
  401.         strncpy(vp->name, name, VNAMELEN);    /* set vari name */
  402.         vp->next = NULLV;                    /* flag 'no next' */
  403.         if (!Varlist)
  404.             Varlist = vp;                    /* first variable */
  405.         else
  406.             Lastvar->next = vp;                /* add this to the list */
  407.         Lastvar = vp;                        /* set 'last' pointer */
  408.     }
  409.  
  410.     if (type == VCHAR)
  411.         strncpy(vp->u.str, val, VMAXSIZE);
  412.     else
  413.         vp->u.num = *(long *)(val);
  414.     vp->type = type;
  415.     return SUCCEED;
  416. }
  417.  
  418. /*    Unset all user variables, deallocating memory.
  419.     No error returned
  420. */
  421. static void unsetall()
  422. {
  423.     VAR *p;
  424.     VAR *nextp;
  425.  
  426.     if (Varlist==NULLV) return;
  427.     p = Varlist;
  428.     while (p->next != NULLV) {
  429.         nextp = p->next;
  430.         free(p);
  431.         p = nextp;
  432.     }
  433.     Varlist = Lastvar = NULLV;
  434. }
  435. /*    end variables section */
  436.  
  437. /*    Action Primitives */
  438. static struct s_acts {
  439.     char *name;
  440.     /*ACT_TYPE token;*/
  441.     int (*funcptr)();
  442. } s_acttab[] = {
  443.     {"beep",        beep},
  444.     {"capture",        k_capture},
  445.     {"debug",        k_echo},
  446.     {"dial",        k_dial},
  447.     {"hangup",        hangup},
  448.     {"linked",        k_linked},
  449.     {"pause",        k_pause},
  450.     {"quit",        s_exit},
  451.     {"redial",        redial},
  452.     {"seen",        k_seen},
  453.     {"xmitbrk",        xmitbrk},
  454.     {"transmit",    k_transmit},
  455.     {"tty",            k_tty},
  456.     {"type",        k_type},
  457.     {"waitfor",        k_waitfor},
  458.     {NULLS,            0}
  459. };
  460. /*    end of primitives */
  461.  
  462. /*    token types */
  463. #define NULLTOK        0    /* terminating '\0' in script buffer */
  464. #define ACTION        1    /* an action (primitive or script cmd) */
  465. #define AFFIRM        2    /* script 'affirm' */
  466. #define BACKQUOT    3    /* script command substitution */
  467. #define SBREAK        4    /* script 'break' */
  468. #define CALL        5    /* script 'call' */
  469. #define COMMENT        6    /* comment */
  470. #define SCONTNUE    7    /* script 'continue' */
  471. #define DECR        10    /* script 'decr' */
  472. #define DO            11    /* script 'do' */
  473. #define DONE        12    /* script 'done' */
  474. #define ECHOS        13    /* script 'echo' */
  475. #define EFLAG        14    /* '-n' switch for script 'echo' cmd */
  476. #define ELSE        15    /* script 'else' */
  477. #define ENDIF        16    /* script 'endif' */
  478. #define ENDTRAP        17    /* script 'endtrap' */
  479. #define EQ            18    /* operator "equals" */
  480. #define EXIT        19    /* script 'exit' */
  481. #define SFILE        20    /* script 'file' */
  482. #define SFALSE        21    /* script 'false' */
  483. #define IF            22    /* script 'if' */
  484. #define INCR        23    /* script 'incr' */
  485. #define LESSTHAN    24    /* operator "less than" */
  486. #define LITERAL        25    /* a literal string (e.g. "abcde") */
  487. #define MORETHAN    26    /* operator "greater than" */
  488. #define NEGATE        27    /* negation operator for comparisons */
  489. #define NEQ            28    /* operator "not equal to" */
  490. #define NUMBER        29    /* a numeric constant (e.g. 12345) */
  491. #define PIPE        30    /* script 'pipe' */
  492. #define READ        31    /* script 'read' */
  493. #define SHELL        32    /* script 'shell' */
  494. #define SET            33    /* script 'assign' */
  495. #define STRAP        34    /* script 'trap' */
  496. #define TERMINAT    35    /* statement terminators (';' and '\n') */
  497. #define THEN        36    /* script 'then' */
  498. #define STRUE        37    /* script 'true' */
  499. #define UNSET        38    /* script 'unset' */
  500. #define UNTRAP        39    /* script 'untrap' */
  501. #define XCSET        40    /* xc 'set' command */
  502. #define VARNAME        41    /* a variable name */
  503. #define WHILE        42    /* script 'while' */
  504. #define TTERROR        43    /* unrecognizable token */
  505. #define TIMEOUT        44    /* script 'timeout' */
  506.  
  507. #define TOK_TYPE short
  508.  
  509. /*    token table */
  510. static struct s_token {
  511.     char *name;
  512.     TOK_TYPE token;
  513. } s_toktab[] = {
  514.     {"NULLTOK",        NULLTOK},
  515.     {"ACTION",        ACTION},
  516.     {"affirm",        AFFIRM},
  517.     {"BACKQUOT",    BACKQUOT},
  518.     {"break",        SBREAK},
  519.     {"call",        CALL},
  520.     {"COMMENT",        COMMENT},
  521.     {"continue",    SCONTNUE},
  522.     {"decr",        DECR},
  523.     {"do",            DO},
  524.     {"done",        DONE},
  525.     {"echo",        ECHOS},
  526.     {"-n",            EFLAG},
  527.     {"else",        ELSE},
  528.     {"endif",        ENDIF},
  529.     {"fi",            ENDIF},
  530.     {"ENDTRAP",        ENDTRAP},
  531.     {"eq",            EQ},
  532.     {"exit",        EXIT},
  533.     {"false",        SFALSE},
  534.     {"file",        SFILE},
  535.     {"if",            IF},
  536.     {"incr",        INCR},
  537.     {"lessthan",    LESSTHAN},
  538.     {"LITERAL",        LITERAL},
  539.     {"morethan",    MORETHAN},
  540.     {"!",            NEGATE},
  541.     {"neq",            NEQ},
  542.     {"NUMBER",        NUMBER},
  543.     {"pipe",        PIPE},
  544.     {"read",        READ},
  545.     {"assign",        SET},
  546.     {"shell",        SHELL},
  547.     {"TRAP",        STRAP},        /* 'trap' keyword left for later dev't */
  548.     {"TERMINAT",    TERMINAT},
  549.     {"then",        THEN},
  550.     {"timeout",        TIMEOUT},
  551.     {"true",        STRUE},
  552.     {"unassign",    UNSET},
  553.     {"UNTRAP",        UNTRAP},
  554.     {"set",            XCSET},
  555.     {"VARNAME",        VARNAME},
  556.     {"while",        WHILE},
  557.     {"\0",            TTERROR}
  558. };
  559. /*    end token types */
  560.  
  561. /*    tok_value is set by lexan() in the following instances:
  562.      TOK_TYPE NUMBER:     (long) int value of number
  563.      TOK_TYPE LITERAL:     pointer to beginning of quoted string
  564.      TOK_TYPE ACTION:     function pointer to appropriate vector
  565.      TOK_TYPE VARNAME:     pointer to VAR struct
  566.      TOK_TYPE TTERROR:     pointer to strange construction in script
  567.     All other values of TOK_TYPE don't require further information.
  568.  */
  569. static union {
  570.     long numval;        /* numbers */
  571.     char *strptr;        /* for literal strings (points to initial '"') */
  572.     int (*funcptr)();    /* vector for primitives */
  573.     VAR *varptr;        /* for variables */
  574. } tok_value;
  575.  
  576.  
  577. /*    lexan() is the lexical analyzer, which translates words
  578.     into token types and sets tok_value appropriately. It's
  579.     called repeatedly by the language parser, S_parse().
  580. */
  581. static TOK_TYPE lexan(pcptr)
  582. char **pcptr;        /* address of script program counter */
  583. {
  584.     long nvalue, negpos = 1;
  585.     VAR *varptr;
  586.     FILE *bqpipe;
  587.     int i, c;                /* really a char, but 'int' to spot EOF */
  588.     static char *cptr, *lasttok,
  589.                 latoken[VNAMELEN+1], temp[VMAXSIZE+1], bqcmd[VMAXSIZE+1];
  590.     extern FILE *popen();
  591.  
  592.     /* if in debug mode, echo script line to tfp */
  593.     if (echo_flag && *pcptr>lasttok) {
  594.         cptr = *pcptr - 1;
  595.         while (*cptr==' ' || *cptr=='\t') --cptr;
  596.         if (*cptr=='\n') {
  597.             fprintf(tfp,"+ ");
  598.             ++cptr;
  599.             while (*cptr!='\n' && *cptr)
  600.                 fputc(*(cptr++),tfp);
  601.             fputc('\n',tfp);
  602.         }
  603.     }
  604.  
  605.     /* skip to beginning of next token */
  606.     while (**pcptr==' ' || **pcptr=='\t') ++(*pcptr);
  607.     tok_value.strptr = cptr = lasttok = *pcptr;                /* save place */
  608.  
  609.                                     /* negation operator for comparisons */
  610.     if (*cptr=='!' && (*(cptr+1)==' ' || *(cptr+1)=='\t')) {
  611.         ++cptr;
  612.         *pcptr = cptr;
  613.         return(NEGATE);
  614.     }
  615.                                     /* comment in script */
  616.     if (*cptr=='#') {
  617.         while (*cptr && *cptr!='\n') ++cptr;
  618.         *pcptr = cptr;
  619.         return(TERMINAT);
  620.     }
  621.                                     /* statement terminator */
  622.     if (*cptr==';' || *cptr=='\n') {
  623.         ++cptr;
  624.         *pcptr = cptr;
  625.         return(TERMINAT);
  626.     }
  627.                                     /* end of script */
  628.     if (*cptr=='\0')
  629.         return(NULLTOK);
  630.                                     /* quoted literal string */
  631.     if (*cptr=='"') {
  632.         ++cptr;
  633.         while (*cptr && *cptr!='\n' && !(*cptr=='"' && *(cptr-1)!='\\'))
  634.             ++cptr;
  635.         if (*cptr=='"') {
  636.             ++cptr;
  637.             *pcptr = cptr;
  638.             return(LITERAL);
  639.         }
  640.         sprintf(Msg,"Unmatched quote");
  641.         S_abort();
  642.     }
  643.                             /* environment variable (treat as a literal) */
  644.     if (*cptr=='$') {
  645.         ++cptr;
  646.         for (i=0; i<VMAXSIZE; ++i) {
  647.             if (!*cptr || *cptr==' ' || *cptr=='\t' || *cptr=='\n'
  648.                 || *cptr=='\r' || *cptr==';')
  649.                     break;
  650.             temp[i] = *(cptr++);
  651.         }
  652.         temp[i] = '\0';
  653.         tok_value.strptr = (char*) getenv(temp);
  654.         if (!tok_value.strptr) {
  655.             tok_value.strptr = temp;
  656.             sprintf(Msg,"%s: no such environment variable",temp);
  657.             S2;
  658.             tok_value.strptr = "";
  659.         }
  660.         *pcptr = cptr;
  661.         return(LITERAL);
  662.     }
  663.                         /* back-quoted shell command (treat like env var) */
  664.     if (*cptr=='`') {
  665.         ++cptr;
  666.         i = 0;
  667.         while (*cptr && *cptr!='\n' && *cptr!='`' && (++i)<VMAXSIZE)
  668.             ++cptr;
  669.         if (*cptr=='`') {
  670.             for (i=0; i<VMAXSIZE; ++i) {            /* tok_value ptr points */
  671.                 bqcmd[i] = tok_value.strptr[i+1];    /* to leading '`' */
  672.                 if (bqcmd[i]=='`') {
  673.                     bqcmd[i] = '\0';
  674.                     break;
  675.                 }
  676.             }
  677.             bqcmd[i] = '\0';
  678.             signal(SIGCLD,SIG_DFL);
  679.             if ((bqpipe=popen(bqcmd,"r"))==NULLF) {
  680.                 sprintf(Msg,"%s: cannot create pipe",bqcmd);
  681.                 S_abort();
  682.             }
  683.             else {
  684.                 temp[0] = '\0';
  685.                 i = 0;
  686.                 while (i<=VMAXSIZE && (c=fgetc(bqpipe))!=EOF && c!='\n')
  687.                     temp[i++] = c;
  688.                 fflush(bqpipe);
  689.                 pclose(bqpipe);
  690.                 temp[i] = '\0';
  691.                 tok_value.strptr = temp;
  692.                 *pcptr = cptr + 1;
  693.                 return(LITERAL);
  694.             }
  695.         }
  696.         else {
  697.             sprintf(Msg,"Unmatched back-quote:");
  698.             S_abort();
  699.         }
  700.     }
  701.                                 /* dialout port name */
  702.     if (!strncmp(cptr,"portname",8)) {
  703.         tok_value.strptr =mport(NULLS);
  704.         *pcptr += 8;
  705.         return(LITERAL);
  706.     }
  707.                                 /* leading hyphen, maybe a negative number? */
  708.     if (*cptr=='-') {
  709.         negpos = (-1);
  710.         ++cptr;
  711.     }
  712.                                 /* string beginning with a digit */
  713.     if (isdigit(*cptr)) {
  714.         nvalue = (*cptr - '0') * negpos;
  715.         while (*(++cptr)) {
  716.             if (isdigit(*cptr)) {
  717.                 nvalue *= 10;
  718.                 nvalue += (*cptr - '0');
  719.                 if (nvalue>0) nvalue *= negpos;
  720.                 continue;
  721.             }
  722.             else if (strchr(" \t\n;",*cptr)) {
  723.                 tok_value.numval = nvalue;
  724.                 *pcptr = cptr;
  725.                 return(NUMBER);
  726.             }
  727.             sprintf(Msg,"Variable name cannot begin with a digit: ");
  728.             S_abort();
  729.         }
  730.         tok_value.numval = nvalue;
  731.         *pcptr = cptr;
  732.         return(NUMBER);
  733.     }
  734.                     /* check for '-n' switch for echo (type EFLAG) */
  735.     if (negpos<0) {
  736.         if (*cptr=='N' || *cptr=='n') {
  737.             while (*cptr && !strchr(" \t;\n",*cptr)) ++cptr;
  738.             *pcptr = cptr;
  739.             return(EFLAG);
  740.         }
  741.         sprintf(Msg,"Bad option to ECHO command");
  742.         S_abort();
  743.     }
  744.                             /* impermissible initial character */
  745.     if (!isalpha(*cptr)) {
  746.         sprintf(Msg,"bad initial character: %c", *cptr);
  747.         S_abort();
  748.     }
  749.  
  750.         /* remember that tok_value.strptr points to start of token */
  751.     for (i=1; i<(VNAMELEN+1); ++i) {        /* jump to next field separator */
  752.         ++cptr;
  753.         if (*cptr=='\0' || strchr(" \t\n;",*cptr)) break;
  754.     }
  755.     if (i>VNAMELEN) {                        /* word too long */
  756.         sprintf(Msg,"Variable name too long");
  757.         S_abort();
  758.     }
  759.     strncpy(latoken,tok_value.strptr,i);     /* copy word to array 'latoken' */
  760.     latoken[i] = '\0';
  761.     lc_word(latoken);                        /* cvt to lowercase */
  762.                                             /* script keywords */
  763.                     /* scan table for keyword match */
  764.     for (i=0; *(s_toktab[i].name) && strcmp(latoken,s_toktab[i].name); ++i)
  765.         ;
  766.     if (*s_toktab[i].name)    {                /* lc keyword recognized */
  767.         if (s_toktab[i].token==STRUE)
  768.             tok_value.numval = TRUE;
  769.         if (s_toktab[i].token==SFALSE)
  770.             tok_value.numval = FALSE;
  771.         *pcptr = cptr;
  772.         return(s_toktab[i].token);
  773.     }
  774.                                     /* system primitive (ACTION) */
  775.                     /* scan table for keyword match */
  776.     for (i=0;s_acttab[i].name != NULLS && strcmp(latoken,s_acttab[i].name);++i)
  777.         ;
  778.     if (s_acttab[i].name != NULLS) {        /* primitive recognized */
  779.         tok_value.funcptr = s_acttab[i].funcptr;
  780.         *pcptr = cptr;
  781.         return(ACTION);
  782.     }
  783.                             /* user variable name */
  784.     if ((varptr=findvar(latoken))!=NULLV) {     /* existing user variable */
  785.         tok_value.varptr = varptr;
  786.         *pcptr = cptr;
  787.         return(VARNAME);
  788.     }
  789.         /* could this be the name of a new variable? */
  790.     tok_value.strptr = latoken;    /* just in case this is 'on', 'off', etc. */
  791.     if (setvar(latoken,"",VCHAR)==FAIL)        /* can't create it */
  792.         return(TTERROR);
  793.     if ((varptr=findvar(latoken))==NULLV)    /* can't retrieve it */
  794.         return(TTERROR);
  795.     else {                                    /* got it */
  796.         tok_value.varptr = varptr;
  797.         *pcptr = cptr;
  798.         return(VARNAME);
  799.     }
  800. }
  801.  
  802. /*        utility routines called by S_parse() */
  803.  
  804. /*    S_affirm is a placeholder. It's the function to get a yes or no
  805.     response from the user.
  806. */
  807. static S_affirm()
  808. {
  809.     char c, junk;
  810.  
  811.     c = getchar();
  812.     fputc(c,tfp);
  813.     while ((junk=getchar())!='\n' && junk!='\r') {
  814.         fputc(junk,tfp);
  815.     }
  816.     fputc('\n',tfp);
  817.     return(c=='y' || c=='Y');
  818. }
  819.  
  820. /*    S_addsub increments or decrements a numeric variable.
  821.     It assumes that since the INCR or DECR directives call
  822.     lexan() for the variable just before coming here, the
  823.     tok_value structure contains a pointer to the variable
  824.     whose value is to be changed.
  825. */
  826. static S_addsub(direction)
  827. int direction;
  828. {
  829.     long oldval = tok_value.varptr->u.num;
  830.     oldval += direction;
  831.  
  832.     if (setvar(tok_value.varptr->name,&oldval,VNUM)==FAIL) {
  833.         sprintf(Msg,"Error setting variable '%s'", tok_value.varptr->name);
  834.         S_abort();
  835.     }
  836.     return(tok_value.varptr->u.num ? TRUE : FALSE);
  837. }
  838.  
  839. /*    S_qstrip returns a pointer to a (static) string with leading and
  840.     trailing double-quote marks removed. If the string happens to
  841.     lack a leading or trailing double-quote mark, then the string
  842.     will be returned with its beginning unchanged, and its length
  843.     equal to VMAXSIZE or the length of the string, whichever is
  844.     shorter. Double-quote marks escaped with a backslash are included
  845.     in the returned string and the backslash is excised.
  846. */
  847. static char *S_qstrip(strptr)
  848. char *strptr;
  849. {
  850.     int i;
  851.     static char strbuf[VMAXSIZE+2];
  852.  
  853.     if (*strptr=='"') ++strptr;
  854.     for (i=0; i<VMAXSIZE+1; ++i) {
  855.         if (*strptr=='\\' && *(strptr+1)=='"' && *(strptr-1)!='\\')
  856.             ++strptr;
  857.         if ((*strptr=='"' && *(strptr-1)!='\\') || !*strptr || *strptr=='\n')
  858.             break;
  859.         strbuf[i] = *strptr;
  860.         ++strptr;
  861.     }
  862.     strbuf[i] = '\0';
  863.     return(strbuf);
  864. }
  865.  
  866. /*    S_read does the parsing grunts for the script 'read' directive.    On
  867.     entry, the 'read' token has been parsed, but that's all.
  868. */
  869. static S_read(pcptr)
  870. char **pcptr;                /* S_parse()'s program counter (p_pc) */
  871. {
  872.     int i;
  873.     VAR *varptr1;
  874.     static char strbuf[VMAXSIZE+2];
  875.  
  876.     if (lexan(pcptr)!=VARNAME)
  877.         S_abort();
  878.     varptr1 = tok_value.varptr;
  879.     strbuf[0] = '\0';
  880.     for (i=0; i<VMAXSIZE; ++i) {
  881.         strbuf[i] = getchar();
  882.         fputc(strbuf[i],tfp);
  883.         if (strbuf[i]=='\b' && i) {
  884.             --i;
  885.             continue;
  886.         }
  887.         if (strbuf[i]=='\n' || strbuf[i]=='\r') {
  888.             strbuf[i] = '\0';
  889.             break;
  890.         }
  891.     }
  892.     strbuf[VMAXSIZE] = '\0';
  893.     fputc('\n',tfp);
  894.     return(setvar(varptr1->name,strbuf,VCHAR)+1);
  895. }
  896.  
  897. /*    S_set does the parsing grunts for the script 'assign' directive. It's
  898.     a separate function mostly to keep from cluttering up S_parse()
  899.     too much.    On entry, only the 'set' token has been received from
  900.     the directive containing it.
  901. */
  902. static S_set(pcptr)
  903. char **pcptr;                /* S_parse()'s program counter (p_pc) */
  904. {
  905.     TOK_TYPE nexttype;
  906.     VAR *varptr1, *varptr2;
  907.     char *setstr;
  908.  
  909.     if ((nexttype=lexan(pcptr))!=VARNAME)
  910.         S_abort();
  911.     varptr1 = tok_value.varptr;
  912.     if ((nexttype=lexan(pcptr))!=EQ)
  913.         S_abort();
  914.     switch (nexttype = lexan(pcptr)) {
  915.         case LITERAL:
  916.             setstr = S_qstrip(tok_value.strptr);
  917.             return(setvar(varptr1->name,setstr,VCHAR) + 1);
  918.         case ACTION:
  919.         case AFFIRM:
  920.             if (nexttype==ACTION)
  921.                 tok_value.numval = (long) S_perform(pcptr);
  922.             else
  923.                 tok_value.numval = (long) S_affirm();
  924.         case NUMBER:
  925.         case STRUE:
  926.         case SFALSE:
  927.             return(setvar(varptr1->name,&tok_value.numval,VNUM) + 1);
  928.         case VARNAME:
  929.             varptr2 = tok_value.varptr;
  930.             switch (varptr2->type) {
  931.                 case VCHAR:
  932.                     return(setvar(varptr1->name,varptr2->u.str,VCHAR) + 1);
  933.                 default:
  934.                     return(setvar(varptr1->name,&(varptr2->u.num),VNUM) + 1);
  935.             }
  936.         default:
  937.             S_abort();
  938.     }
  939. }
  940.  
  941. /*    S_varcmp() compares a variable's value with a string or numeric
  942.     literal, or with the value of a second variable. Once again,
  943.     this function does parsing grunts for S_parse().
  944. */
  945. static S_varcmp(varptr1,pcptr)
  946. VAR *varptr1;
  947. char **pcptr;                    /* S_parse()'s program counter (p_pc) */
  948. {
  949.     TOK_TYPE compmode;
  950.     long testnum;
  951.     static char strbuf[VMAXSIZE+1];
  952.     char *cmpstr;
  953.     int status, numvar;
  954.     VAR *varptr2;
  955.  
  956.     numvar = (varptr1->type!=VCHAR);
  957.     compmode = lexan(pcptr);
  958.     switch (compmode) {
  959.         case EQ:
  960.         case NEQ:
  961.         case MORETHAN:
  962.         case LESSTHAN:
  963.             break;
  964.         case TERMINAT:
  965.             if (numvar)
  966.                 return(varptr1->u.num ? TRUE : FALSE);
  967.             else
  968.                 return(*varptr1->u.str ? TRUE : FALSE);
  969.         default:
  970.             S_abort();
  971.     }
  972.     switch (lexan(pcptr)) {
  973.         case LITERAL:
  974.             if (numvar) {
  975.                 sprintf(Msg,"Error: %s is a numeric variable",varptr1->name);
  976.                 S_abort();
  977.             }
  978.             ++tok_value.strptr;
  979.             strncpy(strbuf,tok_value.strptr,VMAXSIZE);
  980.             *(strchr(strbuf,'"')) = '\0';
  981.             cmpstr = strbuf;
  982.             break;
  983.         case NUMBER:
  984.         case STRUE:
  985.         case SFALSE:
  986.             if (!numvar) {
  987.                 sprintf(Msg,"Error: %s is a string variable",varptr1->name);
  988.                 S_abort();
  989.             }
  990.             testnum = tok_value.numval;
  991.             break;
  992.         case VARNAME:
  993.             varptr2 = tok_value.varptr;
  994.             if (numvar && varptr2->type==VCHAR) {
  995.                 sprintf(Msg,"Error: %s and %s are of different types",
  996.                     varptr1->name, varptr2->name);
  997.                 S_abort();
  998.             }
  999.             if (numvar)
  1000.                 testnum = varptr2->u.num;
  1001.             else
  1002.                 cmpstr = varptr2->u.str;
  1003.             break;
  1004.         default:
  1005.             S_abort();
  1006.     }
  1007.     if (numvar) {
  1008.         status = (varptr1->u.num==testnum);
  1009.         if (compmode==EQ) return(status);
  1010.         if (compmode==NEQ) return(!status);
  1011.         status = (varptr1->u.num > testnum);
  1012.         if (compmode==MORETHAN) return(status);
  1013.         else return(!status);
  1014.     }
  1015.     status = strcmp(varptr1->u.str,cmpstr);
  1016.     if (compmode==EQ)
  1017.         return(status==0);
  1018.     if (compmode==NEQ)
  1019.         return(status);
  1020.     if (compmode==MORETHAN)
  1021.         return(status>0);
  1022.     else
  1023.         return(status<0);
  1024. }
  1025.  
  1026. static char *S_construct(pcptr)
  1027. char **pcptr;
  1028. {
  1029.     char *cptr;
  1030.     static char newstring[VMAXSIZE+10];
  1031.     TOK_TYPE nexttype;
  1032.  
  1033.     newstring[0] = '\0';
  1034.     cptr = newstring;
  1035.     while ((nexttype=lexan(pcptr))!=NULLTOK) {
  1036.         if (strlen(newstring)>VMAXSIZE) {
  1037.             newstring[VMAXSIZE] = '\0';
  1038.             break;
  1039.         }
  1040.         switch (nexttype) {
  1041.             case TERMINAT:
  1042.                 break;
  1043.             case NUMBER:
  1044.                 sprintf(cptr,"%ld",tok_value.numval);
  1045.                 cptr += strlen(cptr);
  1046.                 continue;
  1047.             case LITERAL:
  1048.                 sprintf(cptr,"%s",S_qstrip(tok_value.strptr));
  1049.                 cptr += strlen(cptr);
  1050.                 continue;
  1051.             case VARNAME:
  1052.                 if (tok_value.varptr->type != VCHAR)
  1053.                     sprintf(cptr,"%ld",tok_value.varptr->u.num);
  1054.                 else
  1055.                     sprintf(cptr,"%s",tok_value.varptr->u.str);
  1056.                 cptr += strlen(cptr);
  1057.                 continue;
  1058.             default:
  1059.                 S_abort();
  1060.         }
  1061.         break;
  1062.     }
  1063.     return(newstring);
  1064. }
  1065.  
  1066. /*    S_perform invokes a system primitive and returns TRUE if the
  1067.     primitive succeeds, FALSE if it fails. On entry, only the ACTION
  1068.     token has been parsed.
  1069. */
  1070. static S_perform(pcptr)
  1071. char **pcptr;
  1072. {
  1073.     int i, (*fptr)();
  1074.     char *cptr;
  1075.     VAR *varptr1;
  1076.     long n = -1;
  1077.  
  1078.     fptr = tok_value.funcptr;
  1079.     cptr = NULLS;
  1080.     while (TRUE) {
  1081.         switch (lexan(pcptr)) {
  1082.             case TERMINAT:
  1083.                 break;
  1084.             case NUMBER:
  1085.                 if (n != -1)
  1086.                     S_abort();
  1087.                 n = tok_value.numval;
  1088.                 continue;
  1089.             case LITERAL:
  1090.             case TTERROR:
  1091.                 cptr = S_qstrip(tok_value.strptr);
  1092.                 continue;
  1093.             case VARNAME:
  1094.                 varptr1 = tok_value.varptr;
  1095.                 if (varptr1->type==VCHAR) {
  1096.                         cptr = varptr1->u.str;
  1097.                         break;
  1098.                 }
  1099.                 if (n != -1)
  1100.                     S_abort();
  1101.                 n = varptr1->u.num;
  1102.                 continue;
  1103.             default:
  1104.                 S_abort();
  1105.         }
  1106.         break;
  1107.     }
  1108.     i = (*fptr)(n,cptr);
  1109.     return(i+1);    /* FAIL+1=0 (FALSE); SUCCEED+1=1 (TRUE) */
  1110. }
  1111.  
  1112. /*        stack protection and break/continue stuff */
  1113.  
  1114. static int    nest_while,        /* number of nested loops */
  1115.             nest_parse,        /* number of nested calls to S_parse() */
  1116.             nest_cmd;        /* number of nested commands */
  1117.  
  1118. static long deadline;        /* deadline for 'timeout' keyword */
  1119.  
  1120. jmp_buf env;                /* cell for environment, setjmp */
  1121.  
  1122.  
  1123. #define CMDNEST        8        /* max number of nested scripts */
  1124. #define PARSNEST    50        /* max number of nested calls to parser */
  1125.  
  1126. static char *areas[CMDNEST];
  1127.  
  1128. #define DNP        --nest_parse
  1129. #define BCCHK(x) if(x==SBREAK||x==SCONTNUE){DNP;return(nest_while?x:S_abort());}
  1130.  
  1131. /* cleanup any debris left after a non-trapped keyboard interrupt */
  1132. static void S_bombout()
  1133. {
  1134.     int i;
  1135.  
  1136.     for (i=0; i<nest_cmd; ++i) {
  1137.         if (areas[i]) {
  1138.             free(areas[i]);
  1139.             areas[i] = NULLS;
  1140.         }
  1141.     }
  1142.     nest_cmd = nest_while = nest_parse = 0;
  1143.     deadline = 0;
  1144.     return;
  1145. }
  1146.  
  1147. /*    S_call() */
  1148. static char *tvector;        /* trap vector */
  1149. static void S_call(scriptname)    /* load a script and call S_parse to run it */
  1150. char *scriptname;
  1151. {
  1152.     int i, quiet=0;
  1153.     jmp_buf senv;
  1154.     char *oldtvec, *newptr, *oldptr;
  1155.     long int filesize;
  1156.     FILE *scriptfp;
  1157.     static struct stat statbuf;
  1158.  
  1159.     memset(Msg, 0, SM_BUFF);
  1160.     if (++nest_cmd>CMDNEST) {
  1161.         show(2,"Too many nested scripts");
  1162.         --nest_cmd;
  1163.         return;
  1164.     }
  1165.     scriptfp = openfile(scriptname);
  1166.     if (scriptfp == NULLF) {
  1167.         sprintf(Msg,"Can't open %s",scriptname);
  1168.         S2;
  1169.         --nest_cmd;
  1170.         return;
  1171.     }
  1172.  
  1173.     /* this succeeds, openfile() has called isregfile() to stat the file */
  1174.     fstat(fileno(scriptfp),&statbuf);
  1175.     filesize = statbuf.st_size;
  1176.  
  1177.     areas[nest_cmd - 1] = NULLS;
  1178.     if ((areas[nest_cmd-1]=(char*)calloc((unsigned)filesize+10,1))==NULLS) {
  1179.         sprintf(Msg,"%s: allocation error",scriptname);
  1180.         S2;
  1181.         fclose(scriptfp);
  1182.         --nest_cmd;
  1183.         return;
  1184.     }
  1185.     if (linkflag == 2)
  1186.         quiet = 1,
  1187.         linkflag = 0;
  1188.     if (!quiet)
  1189.         sprintf(Msg,"RUNNING %s",scriptname),
  1190.         S2;
  1191.     *(areas[nest_cmd - 1]) = '\n';
  1192.     fread((areas[nest_cmd - 1] + 1),filesize,1,scriptfp);
  1193.     *(areas[nest_cmd - 1] + filesize + 1) = '\0';
  1194.     fclose(scriptfp);
  1195.  
  1196.     oldtvec = tvector;
  1197.     tvector = NULLS;
  1198.     newptr = (char *)senv;
  1199.     oldptr = (char *)env;
  1200.     for (i=0; i<sizeof(env); ++i)
  1201.         *(newptr++) = *(oldptr++);
  1202.     if ((i=setjmp(env))==0)
  1203.         S_parse(areas[nest_cmd - 1],NULLTOK);
  1204.     else if (i==TTERROR)
  1205.         show(2,"Abnormal script termination");
  1206.     else if (i==TIMEOUT)
  1207.         show(2,"Timeout");
  1208.     else if (i==EXIT)
  1209.         show(2,"Script encountered 'exit'");
  1210.  
  1211.     tvector = oldtvec;
  1212.     newptr = (char *)env;
  1213.     oldptr = (char *)senv;
  1214.     for (i=0; i<sizeof(env); ++i)
  1215.         *(newptr++) = *(oldptr++);
  1216.     --nest_cmd;
  1217.     if (areas[nest_cmd])
  1218.         free(areas[nest_cmd]);
  1219.  
  1220.     if (!quiet)
  1221.         sprintf(Msg,"%s COMPLETE",scriptname),
  1222.         S2;
  1223.     return;
  1224. }
  1225.  
  1226. /*    S_parse() */
  1227. static char *intercom;        /* last previous value of S_parse program ctr */
  1228. static FILE *savetfp;        /* to stash tfp when tfp redirected */
  1229. static struct termio ourmode;    /* remember tty settings during a shell */
  1230. static S_parse(p_pc, t_invoke)
  1231. char *p_pc;
  1232. TOK_TYPE t_invoke;
  1233. {
  1234.     long n,                    /* "leading" number for primitives */
  1235.          todnow;            /* current time */
  1236.     int i,
  1237.         status,                /* status of last performed operation */
  1238.         retval,                /* value to be returned by this function */
  1239.         testing,            /* flag set if within 'if' or 'while' clause */
  1240.         direction,            /* if 1, increment, if -1, decrement */
  1241.         w_status,            /* used to correct nest_parse in 'while' */
  1242.         counter,            /* for WHILE and IF, to track keywords */
  1243.         negating;            /* ! operator in effect for comparisons */
  1244.     char *cptr, *S_WHILE, *S_DO, *S_DONE;
  1245.     TOK_TYPE nexttype;
  1246.     VAR *varptr1;
  1247.  
  1248.     ++nest_parse;
  1249.     testing = (t_invoke==THEN || t_invoke==DO);
  1250.     retval = status = negating = FALSE;
  1251.     n = -1;
  1252.     counter = 0;
  1253.     while (TRUE) {
  1254.         /* we come through here only at the beginning of expressions */
  1255.         if (deadline) {
  1256.             time(&todnow);
  1257.             if (todnow>deadline) {        /* deadline; exit current script */
  1258.                 deadline = 0;
  1259.                 longjmp(env,TIMEOUT);
  1260.             }
  1261.         }
  1262.         if (BREAK && tvector!=NULLS && t_invoke!=ENDTRAP) {    /* interrupt trap */
  1263.             BREAK = FALSE;
  1264.             cptr = tvector;
  1265.             status = S_parse(cptr,ENDTRAP);
  1266.             DNP;
  1267.         }
  1268.         BREAK = FALSE;
  1269.         retval = testing ? (retval || status) : status;
  1270.         direction = 1;
  1271.         intercom = p_pc;
  1272.         nexttype = lexan(&p_pc);
  1273.                 /* check for list terminator */
  1274.         if (nexttype==t_invoke || (t_invoke==ENDIF && nexttype==ELSE)) {
  1275.             if (echo_flag && testing) {
  1276.                 fprintf(tfp,"\n\t\t\t\t\t\t\tCondition: %s\n",
  1277.                     retval ? "TRUE" : "FALSE");
  1278.             }
  1279.             return(retval);
  1280.         }
  1281.         switch (nexttype) {
  1282.             case NULLTOK:        /* inconsistent list terminators */
  1283.             case DO:                                /**/
  1284.             case DONE:                                /**/
  1285.             case THEN:                                /**/
  1286.             case ELSE:                                /**/
  1287.             case ENDIF:                                /**/
  1288.             case TTERROR:                            /**/
  1289.             case EFLAG:            /* not at beginning of expressions */
  1290.             case EQ:                                /**/
  1291.             case NEQ:                                /**/
  1292.             case MORETHAN:                            /**/
  1293.             case LESSTHAN:                            /**/
  1294.                 S_abort();
  1295.             case LITERAL:
  1296.                 if (!testing)
  1297.                     S_abort();
  1298.                 status = !!strlen(tok_value.strptr);
  1299.                 if (negating)
  1300.                     status = !status;
  1301.                 continue;
  1302.             case NEGATE:
  1303.                 if (!testing || negating)
  1304.                     S_abort();
  1305.                 negating = 1;
  1306.                 continue;
  1307.             case NUMBER:
  1308.                 if (n!=(-1))
  1309.                     S_abort();
  1310.                 n = tok_value.numval;
  1311.                 continue;
  1312.             case TERMINAT:
  1313.                 negating = 0;
  1314.                 continue;
  1315.             case ACTION:
  1316.                 status = S_perform(&p_pc);
  1317.                 if (negating)
  1318.                     status = !status;
  1319.                 break;
  1320.             case AFFIRM:
  1321.                 status = S_affirm();
  1322.                 if (negating)
  1323.                     status = !status;
  1324.                 break;
  1325.             case SBREAK:
  1326.             case SCONTNUE:
  1327.                 if (testing || t_invoke==NULLTOK)
  1328.                     S_abort();
  1329.                 return(nexttype==SBREAK ? SBREAK : SCONTNUE);
  1330.             case CALL:
  1331.                 lexan(&p_pc);
  1332.                 i = nest_while;
  1333.                 nest_while = 0;
  1334.                 S_call(S_qstrip(tok_value.strptr));
  1335.                 DNP;
  1336.                 nest_while = i;
  1337.                 break;
  1338.             case COMMENT:
  1339.                 continue;
  1340.             case DECR:
  1341.                 direction = (-1);        /* and fall through to... */
  1342.             case INCR:
  1343.                 if ((nexttype=lexan(&p_pc))!=VARNAME)
  1344.                     S_abort();
  1345.                 if ((tok_value.varptr->type) != VNUM) {
  1346.                     sprintf(Msg,"Error: %s is not a numeric variable",
  1347.                         tok_value.varptr->name);
  1348.                     S_abort();
  1349.                 }
  1350.                 status = S_addsub(direction);
  1351.                 if (negating) status =
  1352.                     !status;
  1353.                 break;
  1354.             case ECHOS:
  1355.                 status = 1;
  1356.                 if ((nexttype = lexan(&p_pc))==EFLAG)
  1357.                     status = 0;
  1358.                 else {
  1359.                     p_pc = intercom;
  1360.                     lexan(&p_pc);
  1361.                 }
  1362.                 cptr = S_construct(&p_pc);
  1363.                 fprintf(tfp,"%s",cptr);
  1364.                 if (status)
  1365.                     fputc('\n',tfp);
  1366.                 else
  1367.                     status = 1;
  1368.                 break;
  1369.             case EXIT:
  1370.                 longjmp(env,EXIT);
  1371.             case READ:
  1372.                 status = S_read(&p_pc);
  1373.                 break;
  1374.             case SET:
  1375.                 status = S_set(&p_pc);
  1376.                 if (negating)
  1377.                     status = !status;
  1378.                 break;
  1379.             case SHELL:
  1380.                 sprintf(word,"!");
  1381.                 sprintf(line,"%s",S_construct(&p_pc));
  1382.                 if (tfp==cf)
  1383.                     sprintf(&line[strlen(line)]," >>%s",captfile);
  1384.                 lptr = line;
  1385.                 ioctl(0,TCGETA,&ourmode);
  1386.                 status = !s_shell();
  1387.                 if (negating)
  1388.                     status = !status;
  1389.                 ioctl(0,TCSETAF,&ourmode);
  1390.                 break;
  1391.             case PIPE:
  1392.                 sprintf(word,"$");
  1393.                 sprintf(line,"%s",S_construct(&p_pc));
  1394.                 lptr = line;
  1395.                 ioctl(0,TCGETA,&ourmode);
  1396.                 status = !s_shell();
  1397.                 if (negating)
  1398.                     status = !status;
  1399.                 ioctl(0,TCSETAF,&ourmode);
  1400.                 break;
  1401.             case SFILE:
  1402.                 savetfp = tfp;
  1403.                 if (!captflag) {
  1404.                     show(2,"Capture option not on");
  1405.                     while ((nexttype=lexan(&p_pc))!=TERMINAT &&
  1406.                         nexttype != NULLTOK)
  1407.                         ;
  1408.                     break;
  1409.                 }
  1410.                 tfp = cf;
  1411.                 status = S_parse(p_pc,TERMINAT);
  1412.                 if (negating)
  1413.                     status = !status;
  1414.                 DNP;
  1415.                 while ((nexttype=lexan(&p_pc)) !=TERMINAT &&
  1416.                     nexttype != NULLTOK)
  1417.                     ;
  1418.                 tfp = savetfp;
  1419.                 fseek(cf,0L,2);
  1420.                 break;
  1421.             case STRUE:
  1422.             case SFALSE:
  1423.                 status = (nexttype==STRUE);
  1424.                 intercom = p_pc;
  1425.                 if ((nexttype=(lexan(&p_pc)))!=TERMINAT && nexttype!=NULLTOK)
  1426.                         S_abort();
  1427.                 if (negating)
  1428.                     status = !status;
  1429.                 break;
  1430.             case STRAP:
  1431.                 tvector = p_pc;
  1432.                 cptr = tvector;
  1433.                 while ((nexttype=lexan(&p_pc))!=ENDTRAP)
  1434.                     if (nexttype==NULLTOK)
  1435.                         S_abort();
  1436.                 break;
  1437.             case TIMEOUT:
  1438.                 if ((nexttype=lexan(&p_pc))!=NUMBER)
  1439.                     S_abort();
  1440.                 if (tok_value.numval>=0) {
  1441.                     deadline = 0;
  1442.                     if (tok_value.numval) {
  1443.                         time(&todnow);
  1444.                         deadline = todnow + (tok_value.numval*60);
  1445.                     }
  1446.                 }
  1447.                 while ((nexttype=lexan(&p_pc))!=TERMINAT && nexttype!=NULLTOK)
  1448.                     ;
  1449.                 break;
  1450.             case UNSET:
  1451.                 if ((nexttype=lexan(&p_pc))!=VARNAME)
  1452.                     S_abort();
  1453.                 status = 1;
  1454.                 unsetvar(tok_value.varptr->name);
  1455.                 break;
  1456.             case UNTRAP:
  1457.                 tvector = NULLS;
  1458.                 if ((nexttype=lexan(&p_pc))!=TERMINAT)
  1459.                     S_abort();
  1460.                 break;
  1461.             case XCSET:
  1462.                 i = 0;
  1463.                 line[0] = '\0';
  1464.                 while (*p_pc!='\n')
  1465.                     line[i++] = *(p_pc++);
  1466.                 line[i] = '\0';
  1467.                 lptr = line;
  1468.                 s_set();
  1469.                 break;
  1470.             case VARNAME:
  1471.                 varptr1 = tok_value.varptr;
  1472.                 if (!testing) {
  1473.                     if (varptr1->type==VCHAR || n!=(-1))
  1474.                         S_abort();
  1475.                     n = varptr1->u.num;
  1476.                     continue;
  1477.                 }
  1478.                 status = S_varcmp(varptr1,&p_pc);
  1479.                 if (negating)
  1480.                     status = !status;
  1481.                 break;
  1482.             case IF:
  1483.                 if (nest_parse > PARSNEST) {
  1484.                     sprintf(Msg,"Nesting level too deep");
  1485.                     S_abort();
  1486.                 }
  1487.                 status = S_parse(p_pc,THEN);
  1488.                 DNP;
  1489.                 if (status==TRUE) {
  1490.                     lexan(&intercom);
  1491.                     p_pc = intercom;
  1492.                     status = S_parse(p_pc,ENDIF);
  1493.                     BCCHK(status)
  1494.                     DNP;
  1495.                     cptr = intercom;
  1496.                     nexttype = lexan(&cptr);
  1497.                     p_pc = cptr;
  1498.                     if (nexttype==ELSE) {
  1499.                         counter = 0;
  1500.                         while (TRUE) {
  1501.                             switch ((nexttype=lexan(&cptr))) {
  1502.                                 case IF:
  1503.                                     ++counter;
  1504.                                     continue;
  1505.                                 case ENDIF:
  1506.                                     if (counter) {
  1507.                                         --counter;
  1508.                                         continue;
  1509.                                     }
  1510.                                     p_pc = cptr;
  1511.                                     break;
  1512.                                 case NULLTOK:
  1513.                                     S_abort();
  1514.                                 default:
  1515.                                     continue;
  1516.                             }
  1517.                             break;
  1518.                         } /* now p_pc points to just after matching 'endif' */
  1519.                     }
  1520.                 }
  1521.                 else {    /* intercom is now the THEN token */
  1522.                     counter = 0;
  1523.                     cptr = intercom;
  1524.                     while (TRUE) {
  1525.                         switch ((nexttype=lexan(&cptr))) {
  1526.                             case IF:
  1527.                                 ++counter;
  1528.                                 continue;
  1529.                             case ELSE:
  1530.                             case ENDIF:
  1531.                                 if (counter) {
  1532.                                     --counter;
  1533.                                     continue;
  1534.                                 }
  1535.                                 p_pc = cptr;
  1536.                                 break;
  1537.                             case NULLTOK:
  1538.                                 S_abort();
  1539.                             default:
  1540.                                 continue;
  1541.                         }
  1542.                         break;
  1543.                     }
  1544.                     if (nexttype==ELSE) {
  1545.                         status = S_parse(p_pc,ENDIF);
  1546.                         BCCHK(status)
  1547.                         DNP;
  1548.                         p_pc = intercom;
  1549.                         lexan(&p_pc);
  1550.                     }
  1551.                     /* p_pc now points to just after ENDIF */
  1552.                 }
  1553.                 break;
  1554.             case WHILE:
  1555.                 if (nest_parse > PARSNEST) {
  1556.                     sprintf(Msg,"Nesting level too deep");
  1557.                     S_abort();
  1558.                 }
  1559.                 S_WHILE = p_pc;
  1560.                 S_DO = S_DONE = NULLS;
  1561.                 while ((w_status=S_parse(S_WHILE,DO))==TRUE) {
  1562.                     DNP;
  1563.                     --nest_while;
  1564.                     if (S_DO==NULLS) {
  1565.                         lexan(&intercom);
  1566.                         S_DO = intercom;
  1567.                     }
  1568.                     status = S_parse(S_DO,DONE);
  1569.                     DNP;
  1570.                     --nest_while;
  1571.                     if (status == SBREAK) {
  1572.                         status = 0;
  1573.                         break;
  1574.                     }                    /* note SCONTNUE is automatic */
  1575.                     if (S_DONE==NULLS && status!=SCONTNUE) {
  1576.                         lexan(&intercom);
  1577.                         S_DONE = intercom;
  1578.                     }
  1579.                 }
  1580.                 if (S_DONE)
  1581.                     p_pc = S_DONE;
  1582.                 else {
  1583.                     cptr = (S_DO==NULLS) ? S_WHILE : S_DO;
  1584.                     while (TRUE) {
  1585.                         switch ((nexttype=lexan(&cptr))) {
  1586.                             case WHILE:
  1587.                                 ++counter;
  1588.                                 continue;
  1589.                             case DONE:
  1590.                                 if (counter) {
  1591.                                     --counter;
  1592.                                     continue;
  1593.                                 }
  1594.                                 p_pc = cptr;
  1595.                                 break;
  1596.                             case NULLTOK:
  1597.                                 S_abort();
  1598.                             default:
  1599.                                 continue;
  1600.                         }
  1601.                         break;
  1602.                     }
  1603.                     /* p_pc now points to just after matching 'done' */
  1604.                 }
  1605.                 if (w_status==FALSE)
  1606.                     DNP;
  1607.                 break;
  1608.         } /* end of main switch, whew */
  1609.         if (t_invoke==TERMINAT)
  1610.             return(status);
  1611.         n = -1;
  1612.     }
  1613. }
  1614.  
  1615. /*    S_abort */
  1616. static S_abort()
  1617. {
  1618.     char *cptr;
  1619.  
  1620.     if (*Msg)
  1621.         S2;
  1622.  
  1623.     cptr = intercom;
  1624.     while (*cptr && *cptr!='\n')
  1625.         --cptr;
  1626.  
  1627.     ++cptr;
  1628.     while (*cptr && *cptr!='\n')
  1629.         fputc(*(cptr++),tfp);
  1630.     fputc('\n',tfp);
  1631.  
  1632.     if (tfp==cf)
  1633.         tfp = savetfp;
  1634.     unsetall();
  1635.     longjmp(env,TTERROR);
  1636. }
  1637.