home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / xc-4.1 / part03 / xcscrpt.c < prev   
Encoding:
C/C++ Source or Header  |  1993-04-13  |  43.7 KB  |  2,014 lines

  1. /*    xcscrpt.c -- script interpreter module for XC");
  2.     This file uses 4-character tabstops
  3.     Author: larry gensch, December 1987
  4.     Major rewrite: fred buck, Jan 1989
  5.     Binding code: larry gensch, September 1991
  6.     This code is released to the public domain
  7. */
  8.  
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <sys/types.h>
  12. #include <sys/times.h>
  13. #include <sys/param.h>
  14. #include <ctype.h>
  15. #include <signal.h>
  16. #include <setjmp.h>
  17. #include <sys/stat.h>
  18. #include "xc.h"
  19.  
  20. jmp_buf            here;
  21. extern FILE        *cfp;
  22. short            ttyflag, debugflag, linkflag, scriptflag,
  23.                 mrbstart,    /* ring buffer start pointer */
  24.                 mrbcount,    /* ring buffer counter */
  25.                 BREAK = 0;    /* a hook for a later 'trap' keyword */
  26. extern short    eofflag;
  27. extern int        redial(), s_set(), s_exit(), xmitbrk();
  28. extern void        divert(), set_onoff(), xcdial();
  29.  
  30. int                my_escape = 1;    /* Control-A; resettable at run-time */
  31.  
  32. char            mringbuf[LG_BUFF];    /* ring buffer for modem input */
  33. static            S_abort();
  34. static void        unsetall(), S_bombout(), S_call();
  35. static char        *NO_ARG = "%s command must have an argument";
  36.  
  37. static bindfunc_t   find_function(); /* bindfunc_t : see xc.h */
  38.  
  39. typedef struct bindstruct {
  40.     int                    bs_c;        /* Character prefix */
  41.     int                    bs_function;/* Function code */
  42.     char                *bs_string;    /* String/script to emit */
  43.     struct bindstruct    *bs_next;    /* Pointer to next entry */
  44. } binding_t;
  45. static binding_t *first_binding = NIL(binding_t);
  46.  
  47. typedef struct {
  48.     bindfunc_t    bf_function;/* Function code */
  49.     char        *bf_name;    /* bind_function() name */
  50.     char        *bf_string;    /* String/script assigned */
  51. } bindstr_t;
  52.  
  53. static bindstr_t function_list[] = {
  54.     {ENDCHAR, "endchar", "Exit terminal mode"},
  55.     {QUITCHR, "quitchr", "Quit program"},
  56.     {CAPTYES, "captyes", "Turn on terminal mode capture"},
  57.     {CAPTEND, "captend", "Turn off terminal mode capture"},
  58.     {DIVCHAR, "divchar", "Send file through modem"},
  59.     {DIALCHR, "dialchr", "Dial from phonelist"},
  60.     {HUPCHAR, "hupchar", "Hang up modem"},
  61.     {SCRPCHR, "scrpchr", "Prompt for script file"},
  62.     {HLPCHAR, "hlpchar", "Display terminal mode key bindings"},
  63.     {BRKCHAR, "brkchar", "Send modem BREAK signal"},
  64.     {EMITSTR, "emitstr", "Emit string"},
  65.     {DOSCRPT, "doscrpt", "Execute script file"}
  66. };
  67.  
  68. #define FUNCTION_COUNT (sizeof(function_list) / sizeof(function_list[0]))
  69.  
  70. static void 
  71. newsigint(junk)
  72. int junk;
  73. {
  74.     eofflag++;
  75.     show_abort();
  76.     S_bombout();
  77.     longjmp(here,1);
  78. }
  79.  
  80. void 
  81. do_script(file)
  82. char *file;
  83. {
  84.     void (*oldvec)();
  85.  
  86.     capture = eofflag = FALSE;
  87.     ttyflag = scriptflag = TRUE;
  88.  
  89.     oldvec = signal(SIGINT, newsigint);
  90.     if (!setjmp(here))
  91.         S_call(file);
  92.  
  93.     unsetall();
  94.     if (capture)
  95.         capture = FALSE,
  96.         fclose(cfp);
  97.  
  98.     linkflag = scriptflag = FALSE;
  99.  
  100.     signal(SIGINT, oldvec);
  101. }
  102.  
  103. static 
  104. k_seen(bytes,fword)
  105. long bytes;
  106. char *fword;
  107. {
  108.     int i, j, k;
  109.     char *cptr;
  110.  
  111.     cptr = fword;
  112.  
  113.     if (!fword || !*fword)
  114.         sprintf(Msg,NO_ARG,"SEEN"),
  115.         S2(Msg);
  116.  
  117.     j = mrbstart - 1;
  118.     if (bytes<=0 || bytes>LG_BUFF)
  119.         bytes = LG_BUFF;
  120.     k = mrbcount - bytes;    /* check only most recent 'bytes' bytes */
  121.     i = 0;
  122.     while ((i++)<mrbcount){
  123.         ++j;
  124.         j = j % LG_BUFF;
  125.         if (i<k)
  126.             continue;
  127.         if (mringbuf[j] != *cptr){
  128.             cptr = fword;
  129.             continue;
  130.         }
  131.         if (*(++cptr)=='\0')
  132.             return SUCCESS;
  133.     }
  134.     return FAILURE;
  135. }
  136.  
  137. k_waitfor(interval,fword)
  138. long interval;
  139. char *fword;
  140. {
  141.     register c, i = -1 ;
  142.     register long limit, waitfor_msec = 0;
  143.     char *ptr;
  144.     struct tms tbuf;
  145.  
  146.     mrbstart = mrbcount = 0;
  147.     sprintf(line,"\"%s\"",fword);
  148.     lptr = line;
  149.     getword();
  150.     lc_word(word);
  151.     ptr = word;
  152.  
  153.     if (interval < -1){
  154.         waitfor_msec = -interval;
  155.         goto SPITOUT;
  156.     }
  157.     if (!fword || word[0] == '\0'){
  158.         sprintf(Msg,NO_ARG,"WAITFOR");
  159.         S2(Msg);
  160.         return FAILURE;
  161.     }
  162.  
  163.     waitfor_msec = 1000 * ((interval > 0) ? interval : 30);
  164.     eofflag = FALSE;
  165.  
  166. SPITOUT: limit = times(&tbuf) + (HZ * waitfor_msec)/1000;
  167.     while (limit >= times(&tbuf) && !eofflag){
  168.         if ((c = readbyte(1)) == -1)
  169.             continue;
  170.  
  171.         if (cismode && c==ENQ){
  172.             s_cis();
  173.             goto SPITOUT;
  174.         }
  175.  
  176.         ++i;
  177.         i = i % LG_BUFF;
  178.         mringbuf[i] = c;
  179.         mrbstart = mrbstart % LG_BUFF;
  180.         if (mrbcount<LG_BUFF)
  181.             ++mrbcount;
  182.         else
  183.             ++mrbstart,
  184.             mrbstart = mrbstart % LG_BUFF;
  185.  
  186.         if (ttyflag)
  187.             fputc(c,tfp);
  188.  
  189.         if (capture && c != '\r')
  190.             fputc(c,cfp);
  191.  
  192.         if (tolower(c) != *ptr){
  193.             ptr = word;
  194.             continue;
  195.         }
  196.  
  197.         if (*++ptr == '\0')
  198.             return SUCCESS;
  199.     }
  200.     return FAILURE;
  201. }
  202.  
  203. static 
  204. k_transmit(junk,fword)
  205. long junk;
  206. char *fword;
  207. {
  208.     sprintf(line,"\"%s\"",fword);
  209.     lptr = line;
  210.     getword();
  211.     if (!fword || word[0] == '\0'){
  212.         sprintf(Msg,NO_ARG,"TRANSMIT");
  213.         S2(Msg);
  214.         return FAILURE;
  215.     }
  216.     send_string(word);
  217.     return SUCCESS;
  218. }
  219.  
  220. static 
  221. k_pause(pause_time,junk)
  222. long pause_time;
  223. char *junk;
  224. {
  225.     pause_time = pause_time ? pause_time : 5;
  226.     sleep((unsigned)pause_time);
  227.     return SUCCESS;
  228. }
  229.  
  230. static 
  231. k_dial(junk,fword)
  232. long junk;
  233. char *fword;
  234. {
  235.     sprintf(line,"%s",fword);
  236.     lptr = line;
  237.     getword();
  238.     if (!fword || word[0] == '\0'){
  239.         sprintf(Msg,NO_ARG,"DIAL");
  240.         S2(Msg);
  241.         return FAILURE;
  242.     }
  243.     xcdial(word);
  244.     return SUCCESS;
  245. }
  246.  
  247. static 
  248. k_capture(junk,fword)
  249. long junk;
  250. char *fword;
  251. {
  252.     int val = capture;
  253.  
  254.     sprintf(word,"capture");
  255.     sprintf(line,"%s",fword);
  256.     lptr = line;
  257.     set_onoff(&capture);
  258.  
  259.     if (val == capture)
  260.         return SUCCESS;
  261.  
  262.     if (!capture)
  263.         fclose(cfp);
  264.     else {
  265.         if (!(cfp = fopen(captfile, "a"))){
  266.             sprintf(Msg,"Can't open capture file %s",captfile);
  267.             S2(Msg);
  268.             eofflag++;
  269.             return FAILURE;
  270.         }
  271.         setbuf(cfp,NIL(char));
  272.     }
  273.     return SUCCESS;
  274. }
  275.  
  276. static 
  277. k_debug(junk,fword)
  278. long junk;
  279. char *fword;
  280. {
  281.     sprintf(word,"debug");
  282.     sprintf(line,"%s",fword);
  283.     lptr = line;
  284.     set_onoff(&debugflag);
  285.     return SUCCESS;
  286. }
  287.  
  288. static 
  289. k_tty(junk,fword)
  290. long junk;
  291. char *fword;
  292. {
  293.     sprintf(word,"tty");
  294.     sprintf(line,"%s",fword);
  295.     lptr = line;
  296.     set_onoff(&ttyflag);
  297.     return SUCCESS;
  298. }
  299.  
  300. static 
  301. k_type(junk,fword)
  302. long junk;
  303. char *fword;
  304. {
  305.     sprintf(line,"%s",fword);
  306.     lptr = line;
  307.     getword();
  308.     if (!fword || word[0] == '\0'){
  309.         sprintf(Msg,NO_ARG,"TYPE");
  310.         S2(Msg);
  311.         return FAILURE;
  312.     }
  313.     divert(TRUE);
  314.     return SUCCESS;
  315. }
  316.  
  317. static 
  318. k_linked()
  319. {
  320.     return linkflag;
  321. }
  322.  
  323. /*    unbind_key() removes the binding attached to the keycode specified by (c).*/
  324. static void 
  325. unbind_key(c)
  326. int c;
  327. {
  328.     binding_t *ptr = first_binding, *prev = NIL(binding_t);
  329.     if (!ptr)
  330.         return;
  331.  
  332.     while (ptr) {
  333.         if (ptr->bs_c == c) {
  334.             if (ptr->bs_string)
  335.                 free(ptr->bs_string);
  336.             if (prev)
  337.                 prev->bs_next = ptr->bs_next;
  338.             else
  339.                 first_binding->bs_next = ptr->bs_next;
  340.             free(ptr);
  341.             return;
  342.         }
  343.         prev = ptr;
  344.         ptr = ptr->bs_next;
  345.     }
  346. }
  347.  
  348. /*    bind_key() binds the key whose ASCII code is specified by (c) to the
  349.     function whose code is specified by (function). Emit strings
  350.     (for EMITSTR) and script names (for DOSCRPT) are specified by (string).
  351. */
  352. static void 
  353. bind_key(c,function,string)
  354. int c;
  355. int function;
  356. char *string;
  357. {
  358.     binding_t *ptr = (binding_t *) malloc(sizeof(binding_t)), *curr, *prev;
  359.  
  360.     if (!ptr) {
  361.             S2("BIND_KEY allocation error");
  362.             S_abort();
  363.     }
  364.  
  365.     unbind_key(c);
  366.  
  367.     ptr->bs_c = c;
  368.     ptr->bs_function = function;
  369.     ptr->bs_string = strdup(string);
  370.  
  371.     /* The following is an insertion sort to ensure that the bindings are
  372.        stored in ascending sequence by key code.  This makes
  373.        show_bindings()'s display easier to read. -lg
  374.     */
  375.  
  376.     for (prev = NIL(binding_t), curr = first_binding;
  377.         curr != NIL(binding_t);
  378.         prev = curr, curr = curr->bs_next) {
  379.         if (ptr->bs_c < curr->bs_c) {
  380.             if (!prev) {
  381.                 ptr->bs_next = first_binding;
  382.                 first_binding = ptr;
  383.                 break;
  384.             } else {
  385.                 ptr->bs_next = curr;
  386.                 prev->bs_next = ptr;
  387.                 break;
  388.             }
  389.         }
  390.     }
  391.  
  392.     if (curr == NIL(binding_t)) {
  393.         if (!prev)
  394.             first_binding = ptr;
  395.         else
  396.             prev->bs_next = ptr;
  397.         ptr->bs_next = NIL(binding_t);
  398.     }
  399. }
  400.  
  401. /*    bind_function() Binds the key whose code is represented by the value in
  402.     (n) to execute the XC builtin function whose name is pointed to be (cptr).
  403.     Any previous binding of the specified keycode is forgotten.
  404.     This routine is designed to be an XC script builtin.
  405. */
  406. static 
  407. bind_function(n,cptr)
  408. long n;
  409. char *cptr;
  410. {
  411.     int c;
  412.     bindfunc_t code;
  413.  
  414.     if (n < 1L || n > 127L) {
  415.         sprintf(Msg,"Invalid key code %d in BIND_FUNCTION command",n);
  416.         S2(Msg);
  417.         return FAILURE;
  418.     }
  419.  
  420.     c = tolower((int) n);
  421.  
  422.     sprintf(line, "\"%s\"", cptr);
  423.     lptr = line;
  424.     getword();
  425.     if (cptr && cptr[0] != '\0') {
  426.         lc_word(word);
  427.         code = find_function(word);
  428.         if (code == BADFUNC) {
  429.             sprintf(Msg,"Invalid function name '%s' in BIND_FUNCTION command",
  430.             word);
  431.             S2(Msg);
  432.             return FAILURE;
  433.         }
  434.         bind_key(c, code, word);
  435.     } else {
  436.         sprintf(Msg,NO_ARG,"BIND_FUNCTION");
  437.         S2(Msg);
  438.         return FAILURE;
  439.     }
  440.  
  441.     return SUCCESS;
  442. }
  443.  
  444. /*    bind_string() binds the key whose code is represented by the
  445.     decimal value in (n) to emit the string pointed to by (cptr).
  446.     Any previous binding of the specified keycode is forgotten.
  447.     This routine is designed to be an XC script builtin.
  448. */
  449. static 
  450. bind_string(n,cptr)
  451. long n;
  452. char *cptr;
  453. {
  454.     int c;
  455.  
  456.     if (n < 1L || n > 127L) {
  457.         sprintf(Msg,"Invalid key code %d in BIND_STRING command",n);
  458.         S2(Msg);
  459.         return FAILURE;
  460.     }
  461.  
  462.     c = tolower((int) n);
  463.  
  464.     sprintf(line, "\"%s\"", cptr);
  465.     lptr = line;
  466.     getword();
  467.     if (cptr && cptr[0] != '\0')
  468.         bind_key(c, EMITSTR, word);
  469.     else {
  470.         sprintf(Msg,NO_ARG,"BIND_STRING");
  471.         S2(Msg);
  472.         return FAILURE;
  473.     }
  474.  
  475.     return SUCCESS;
  476. }
  477.  
  478. /*    bind_script() binds the key whose code is represented by the value in
  479.     (n) to execute the XC script whose name is pointed to by (cptr).
  480.     Any previous binding of the specified keycode is forgotten.
  481.     This routine is designed to be an XC script builtin.
  482. */
  483. static 
  484. bind_script(n,cptr)
  485. long n;
  486. char *cptr;
  487. {
  488.     int c;
  489.  
  490.     if (n < 1L || n > 127L) {
  491.         sprintf(Msg,"Invalid key code %d in BIND_STRING command",n);
  492.         S2(Msg);
  493.         return FAILURE;
  494.     }
  495.  
  496.     c = tolower((int) n);
  497.  
  498.     sprintf(line, "\"%s\"", cptr);
  499.     lptr = line;
  500.     getword();
  501.     if (cptr && cptr[0] != '\0')
  502.         bind_key(c, DOSCRPT, word);
  503.     else {
  504.         sprintf(Msg,NO_ARG,"BIND_SCRIPT");
  505.         S2(Msg);
  506.         return FAILURE;
  507.     }
  508.  
  509.     return SUCCESS;
  510. }
  511.  
  512. /*    Variables Section */
  513. /*    Most of the variable-handling logic is credit: Steve Manes 1987 */
  514. #define VNAMELEN    16    /* maximum name length for variables */
  515. #define VMAXSIZE    256    /* maximum length for CHAR variable */
  516. #define VMAXVARS    30    /* maximum number of user variables */
  517. #define VCHAR        'C'    /* CHARACTER variable type */
  518. #define VNUM        'N'    /* NUMERIC variable type (always 'long') */
  519.  
  520. /* Variable structure */
  521. typedef struct var {
  522.     char name[VNAMELEN+1];    /* variable name */
  523.     struct var    *next;        /* ptr to next structure in var_list */
  524.     char type;                /* variable type */
  525.     union {                    /* pointer to CHAR or NUM/DATE */
  526.         char str[VMAXSIZE+1];
  527.         long num;
  528.     } u;
  529. } VAR;
  530.  
  531. static VAR    *Varlist = NIL(VAR);    /* top of variable list */
  532. static VAR    *Lastvar = NIL(VAR);    /* bottom of variable list */
  533.  
  534. /* Valid variable name characters */
  535. unchar 
  536. OKname[]= {
  537.      /* control characters */
  538.       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,
  539.      /* ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ */
  540.         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,
  541.      /* 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 [ \ ] ^ _ ` */
  542.         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,
  543.      /* 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 { | } ~       */
  544.         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
  545. };
  546.  
  547. /*    Return pointer to VAR structure for User or System variable
  548.     'name', otherwise return NIL(VAR).
  549.     Variable contents in vp->u.[str|num]
  550. */
  551. static VAR *
  552. findvar(name)
  553. char *name;
  554. {
  555.     static    VAR *vp;    /* pointer to output structure */
  556.  
  557.     if (!name || !*name || !Varlist)
  558.         return NIL(VAR);
  559.     vp = Varlist;
  560.     while (vp){
  561.         if (!strncmp(name, vp->name, VNAMELEN) )
  562.             return vp;
  563.         vp = vp->next;
  564.     }
  565.     return NIL(VAR);                /* not found */
  566. }
  567.  
  568. /*    Delete User variable 'name' from VAR list.
  569.     If variable doesn't exist, no error is returned
  570. */
  571. static void 
  572. unsetvar(name)
  573. char *name;
  574. {
  575.     VAR *p;
  576.     VAR *lastp;
  577.  
  578.     if (!name || !*name)
  579.         return;
  580.     for (p=Varlist; p; lastp = p, p = p->next){
  581.         if (!strncmp(name, p->name, VNAMELEN) ){    /* name match? */
  582.             if (Varlist == Lastvar)                    /* only 1 variable */
  583.                 Varlist = Lastvar = NIL(VAR);        /* in list */
  584.             else if (p == Varlist)                    /* first variable */
  585.                 Varlist = p->next;
  586.             else {
  587.                 lastp->next = p->next;                /* dump variable in middle
  588.                                                        of list */
  589.                 if (p == Lastvar)                    /* or last object */
  590.                     Lastvar = lastp;                /* in list */
  591.             }
  592.             free(p);            /* reclaim memory */
  593.             break;
  594.         }
  595.     }
  596. }
  597.  
  598. /*    Set the value of User variable 'name' to 'val' with 'type'.
  599.     If variable exists, change its contents. Otherwise, create it.
  600.     Returns: FAILURE or SUCCESS
  601. */
  602. static 
  603. setvar(name,type,str,val)
  604. char *name;
  605. char type;
  606. char *str;
  607. long *val;
  608. {
  609.     VAR *vp;
  610.     short i;
  611.  
  612.     if (!name || !*name)
  613.         return FAILURE;
  614.     if (!(vp = findvar(name))){ /* create new variable */
  615.         for (i=0; i < VNAMELEN && name[i]; i++){
  616.             if( !OKname[(name[i] & 0x7F)] || isdigit(name[0]) ){
  617.                 sprintf(Msg,"Illegal variable name '%s'",name);
  618.                 S_abort();
  619.             }
  620.         }
  621.         if (!(vp = (VAR *)malloc(sizeof(VAR)))){
  622.             sprintf(Msg,"%s: allocation error",name);
  623.             S2(Msg);
  624.             return FAILURE;
  625.         }
  626.         lc_word(name);
  627.         strncpy(vp->name, name, VNAMELEN);    /* set vari name */
  628.         vp->next = NIL(VAR);                /* flag 'no next' */
  629.         if (!Varlist)
  630.             Varlist = vp;                    /* first variable */
  631.         else
  632.             Lastvar->next = vp;                /* add this to the list */
  633.         Lastvar = vp;                        /* set 'last' pointer */
  634.     }
  635.  
  636.     if (type == VCHAR)
  637.         strncpy(vp->u.str, str, VMAXSIZE);
  638.     else
  639.         vp->u.num = *val;
  640.     vp->type = type;
  641.     return SUCCESS;
  642. }
  643.  
  644. /*    Unset all user variables, deallocating memory.
  645.     No error returned
  646. */
  647. static void 
  648. unsetall()
  649. {
  650.     VAR *p;
  651.     VAR *nextp;
  652.  
  653.     if (!Varlist)
  654.         return;
  655.     p = Varlist;
  656.     while (p->next){
  657.         nextp = p->next;
  658.         free(p);
  659.         p = nextp;
  660.     }
  661.     Varlist = Lastvar = NIL(VAR);
  662. }
  663. /*    end variables section */
  664.  
  665. /*    Action Primitives */
  666. static struct {
  667.     char *name;
  668.     int (*funcptr)();
  669. } s_acttab[] = {
  670.     {"beep",        beep},
  671.     {"bind_function",    bind_function},
  672.     {"bind_script",    bind_script},
  673.     {"bind_string",    bind_string},
  674.     {"capture",        k_capture},
  675.     {"debug",        k_debug},
  676.     {"dial",        k_dial},
  677.     {"hangup",        hangup},
  678.     {"linked",        k_linked},
  679.     {"pause",        k_pause},
  680.     {"quit",        s_exit},
  681.     {"redial",        redial},
  682.     {"seen",        k_seen},
  683.     {"xmitbrk",        xmitbrk},
  684.     {"transmit",    k_transmit},
  685.     {"tty",            k_tty},
  686.     {"type",        k_type},
  687.     {"waitfor",        k_waitfor},
  688.     {NIL(char),            0}
  689. };
  690. /*    end of primitives */
  691.  
  692. /*    token types */
  693. typedef enum {
  694.     NULLTOK,    /* terminating '\0' in script buffer */
  695.     ACTION,        /* an action (primitive or script cmd) */
  696.     AFFIRM,        /* script 'affirm' */
  697.     BACKQUOT,    /* script command substitution */
  698.     SBREAK,        /* script 'break' */
  699.     CALL,        /* script 'call' */
  700.     COMMENT,    /* comment */
  701.     SCONTNUE,    /* script 'continue' */
  702.     DECR,        /* script 'decr' */
  703.     DO,            /* script 'do' */
  704.     DONE,        /* script 'done' */
  705.     ECHOS,        /* script 'echo' */
  706.     EFLAG,        /* '-n' switch for script 'echo' cmd */
  707.     ELSE,        /* script 'else' */
  708.     ENDIF,        /* script 'endif' */
  709.     ENDTRAP,    /* script 'endtrap' */
  710.     EQ,            /* operator "equals" */
  711.     EXIT,        /* script 'exit' */
  712.     SFILE,        /* script 'file' */
  713.     SFALSE,        /* script 'false' */
  714.     IF,            /* script 'if' */
  715.     INCR,        /* script 'incr' */
  716.     LESSTHAN,    /* operator "less than" */
  717.     LITERAL,    /* a literal string (e.g. "abcde") */
  718.     MORETHAN,    /* operator "greater than" */
  719.     NEGATE,        /* negation operator for comparisons */
  720.     NEQ,        /* operator "not equal to" */
  721.     NUMBER,        /* a numeric constant (e.g. 12345) */
  722.     PIPE,        /* script 'pipe' */
  723.     READ,        /* script 'read' */
  724.     SHELL,        /* script 'shell' */
  725.     SET,        /* script 'assign' */
  726.     STRAP,        /* script 'trap' */
  727.     TERMINAT,    /* statement terminators (';' and '\n') */
  728.     THEN,        /* script 'then' */
  729.     STRUE,        /* script 'true' */
  730.     UNSET,        /* script 'unset' */
  731.     UNTRAP,        /* script 'untrap' */
  732.     XCSET,        /* xc 'set' command */
  733.     VARNAME,    /* a variable name */
  734.     WHILE,        /* script 'while' */
  735.     TTERROR,    /* unrecognizable token */
  736.     TIMEOUT        /* script 'timeout' */
  737. } TOK_TYPE;
  738.  
  739. /*    token table */
  740. static struct {
  741.     char *name;
  742.     TOK_TYPE token;
  743. } s_toktab[] = {
  744.     {"NULLTOK",        NULLTOK},
  745.     {"ACTION",        ACTION},
  746.     {"affirm",        AFFIRM},
  747.     {"BACKQUOT",    BACKQUOT},
  748.     {"break",        SBREAK},
  749.     {"call",        CALL},
  750.     {"COMMENT",        COMMENT},
  751.     {"continue",    SCONTNUE},
  752.     {"decr",        DECR},
  753.     {"do",            DO},
  754.     {"done",        DONE},
  755.     {"echo",        ECHOS},
  756.     {"-n",            EFLAG},
  757.     {"else",        ELSE},
  758.     {"endif",        ENDIF},
  759.     {"fi",            ENDIF},
  760.     {"ENDTRAP",        ENDTRAP},
  761.     {"eq",            EQ},
  762.     {"exit",        EXIT},
  763.     {"false",        SFALSE},
  764.     {"file",        SFILE},
  765.     {"if",            IF},
  766.     {"incr",        INCR},
  767.     {"lessthan",    LESSTHAN},
  768.     {"LITERAL",        LITERAL},
  769.     {"morethan",    MORETHAN},
  770.     {"!",            NEGATE},
  771.     {"neq",            NEQ},
  772.     {"NUMBER",        NUMBER},
  773.     {"pipe",        PIPE},
  774.     {"read",        READ},
  775.     {"assign",        SET},
  776.     {"shell",        SHELL},
  777.     {"TRAP",        STRAP},        /* 'trap' keyword left for later dev't */
  778.     {"TERMINAT",    TERMINAT},
  779.     {"then",        THEN},
  780.     {"timeout",        TIMEOUT},
  781.     {"true",        STRUE},
  782.     {"unassign",    UNSET},
  783.     {"UNTRAP",        UNTRAP},
  784.     {"set",            XCSET},
  785.     {"VARNAME",        VARNAME},
  786.     {"while",        WHILE},
  787.     {"\0",            TTERROR}
  788. };
  789. /*    end token types */
  790.  
  791. /*    tok_value is set by lexan() in the following instances:
  792.      TOK_TYPE NUMBER:     (long) value of number
  793.      TOK_TYPE LITERAL:     pointer to beginning of quoted string
  794.      TOK_TYPE ACTION:     function pointer to appropriate vector
  795.      TOK_TYPE VARNAME:     pointer to VAR struct
  796.      TOK_TYPE TTERROR:     pointer to strange construction in script
  797.     All other values of TOK_TYPE don't require further information.
  798. */
  799. static union {
  800.     long numval;        /* numbers */
  801.     char *strptr;        /* for literal strings (points to initial '"') */
  802.     int (*funcptr)();    /* vector for primitives */
  803.     VAR *varptr;        /* for variables */
  804. } tok_value;
  805.  
  806.  
  807. /*    lexan() is the lexical analyzer, which translates words
  808.     into token types and sets tok_value appropriately. It's
  809.     called repeatedly by the language parser, S_parse().
  810. */
  811. static TOK_TYPE
  812. lexan(pcptr)
  813. char **pcptr;            /* address of script program counter */
  814. {
  815.     long nvalue, negpos = 1;
  816.     VAR *varptr;
  817.     FILE *bqpipe;
  818.     int i, c;                /* really a char, but 'int' to spot EOF */
  819.     static char *cptr, *lasttok,
  820.                 latoken[VNAMELEN+1], temp[VMAXSIZE+1], bqcmd[VMAXSIZE+1];
  821.     extern FILE *popen();
  822.  
  823.     /* if in debug mode, echo script line to tfp */
  824.     if (debugflag && *pcptr>lasttok){
  825.         cptr = *pcptr - 1;
  826.         while (*cptr==' ' || *cptr=='\t') --cptr;
  827.         if (*cptr=='\n'){
  828.             fputs("+ ",tfp);
  829.             ++cptr;
  830.             while (*cptr!='\n' && *cptr)
  831.                 fputc(*(cptr++),tfp);
  832.             fputc('\r',tfp),
  833.             fputc('\n',tfp);
  834.         }
  835.     }
  836.  
  837.     /* skip to beginning of next token */
  838.     while (**pcptr==' ' || **pcptr=='\t') ++(*pcptr);
  839.     tok_value.strptr = cptr = lasttok = *pcptr;                /* save place */
  840.  
  841.                                     /* negation operator for comparisons */
  842.     if (*cptr=='!' && (*(cptr+1)==' ' || *(cptr+1)=='\t')){
  843.         ++cptr;
  844.         *pcptr = cptr;
  845.         return NEGATE;
  846.     }
  847.                                     /* comment in script */
  848.     if (*cptr=='#'){
  849.         while (*cptr && *cptr!='\n') ++cptr;
  850.         *pcptr = cptr;
  851.         return TERMINAT;
  852.     }
  853.                                     /* statement terminator */
  854.     if (*cptr==';' || *cptr=='\n'){
  855.         ++cptr;
  856.         *pcptr = cptr;
  857.         return TERMINAT;
  858.     }
  859.                                     /* end of script */
  860.     if (*cptr=='\0')
  861.         return NULLTOK;
  862.                                     /* quoted literal string */
  863.     if (*cptr=='"'){
  864.         ++cptr;
  865.         while (*cptr && *cptr!='\n' && !(*cptr=='"' && *(cptr-1)!='\\'))
  866.             ++cptr;
  867.         if (*cptr=='"'){
  868.             ++cptr;
  869.             *pcptr = cptr;
  870.             return LITERAL;
  871.         }
  872.         sprintf(Msg,"Unmatched quote");
  873.         S_abort();
  874.     }
  875.                             /* environment variable (treat as a literal) */
  876.     if (*cptr=='$'){
  877.         ++cptr;
  878.         for (i=0; i<VMAXSIZE; ++i){
  879.             if (!*cptr || *cptr==' ' || *cptr=='\t' || *cptr=='\n'
  880.                 || *cptr=='\r' || *cptr==';')
  881.                     break;
  882.             temp[i] = *(cptr++);
  883.         }
  884.         temp[i] = '\0';
  885.         tok_value.strptr = getenv(temp);
  886.         if (!tok_value.strptr)
  887.             tok_value.strptr = temp,
  888.             sprintf(Msg,"%s: no such environment variable",temp),
  889.             S2(Msg),
  890.             tok_value.strptr = "";
  891.         *pcptr = cptr;
  892.         return LITERAL;
  893.     }
  894.                         /* back-quoted shell command (treat like env var) */
  895.     if (*cptr=='`'){
  896.         ++cptr;
  897.         i = 0;
  898.         while (*cptr && *cptr!='\n' && *cptr!='`' && (++i)<VMAXSIZE)
  899.             ++cptr;
  900.         if (*cptr=='`'){
  901.             for (i=0; i<VMAXSIZE; ++i){            /* tok_value ptr points */
  902.                 bqcmd[i] = tok_value.strptr[i+1];    /* to leading '`' */
  903.                 if (bqcmd[i]=='`'){
  904.                     bqcmd[i] = '\0';
  905.                     break;
  906.                 }
  907.             }
  908.             bqcmd[i] = '\0';
  909.             signal(SIGCLD,SIG_DFL);
  910.             if (!(bqpipe=popen(bqcmd,"r"))){
  911.                 sprintf(Msg,"%s: cannot create pipe",bqcmd);
  912.                 S_abort();
  913.             }
  914.             else {
  915.                 temp[0] = '\0';
  916.                 i = 0;
  917.                 while (i<=VMAXSIZE && (c=fgetc(bqpipe))!=EOF && c!='\n')
  918.                     temp[i++] = c;
  919.                 fflush(bqpipe);
  920.                 pclose(bqpipe);
  921.                 temp[i] = '\0';
  922.                 tok_value.strptr = temp;
  923.                 *pcptr = cptr + 1;
  924.                 return LITERAL;
  925.             }
  926.         }
  927.         else {
  928.             sprintf(Msg,"Unmatched back-quote:");
  929.             S_abort();
  930.         }
  931.     }
  932.                                 /* dialout port name */
  933.     if (!strncmp(cptr,"portname",8)){
  934.         tok_value.strptr =mport(NIL(char));
  935.         *pcptr += 8;
  936.         return LITERAL;
  937.     }
  938.                                 /* leading hyphen, maybe a negative number? */
  939.     if (*cptr=='-')
  940.         negpos = (-1),
  941.         ++cptr;
  942.                                 /* string beginning with a digit */
  943.     if (isdigit(*cptr)){
  944.         nvalue = (*cptr - '0') * negpos;
  945.         while (*(++cptr)){
  946.             if (isdigit(*cptr)){
  947.                 nvalue *= 10;
  948.                 nvalue += (*cptr - '0');
  949.                 if (nvalue>0) nvalue *= negpos;
  950.                 continue;
  951.             }
  952.             else if (strchr(" \t\n;",*cptr)){
  953.                 tok_value.numval = nvalue;
  954.                 *pcptr = cptr;
  955.                 return NUMBER;
  956.             }
  957.             sprintf(Msg,"Variable name cannot begin with a digit: ");
  958.             S_abort();
  959.         }
  960.         tok_value.numval = nvalue;
  961.         *pcptr = cptr;
  962.         return NUMBER;
  963.     }
  964.                     /* check for '-n' switch for echo (type EFLAG) */
  965.     if (negpos<0){
  966.         if (*cptr=='N' || *cptr=='n'){
  967.             while (*cptr && !strchr(" \t\n;",*cptr)) ++cptr;
  968.             *pcptr = cptr;
  969.             return EFLAG;
  970.         }
  971.         sprintf(Msg,"Bad option to ECHO command");
  972.         S_abort();
  973.     }
  974.                             /* impermissible initial character */
  975.     if (!isalpha(*cptr)){
  976.         sprintf(Msg,"bad initial character: %c", *cptr);
  977.         S_abort();
  978.     }
  979.  
  980.         /* remember that tok_value.strptr points to start of token */
  981.     for (i=1; i<(VNAMELEN+1); ++i){        /* jump to next field separator */
  982.         ++cptr;
  983.         if (*cptr=='\0' || strchr(" \t\n;",*cptr))
  984.             break;
  985.     }
  986.     if (i>VNAMELEN){                        /* word too long */
  987.         sprintf(Msg,"Variable name too long");
  988.         S_abort();
  989.     }
  990.     strncpy(latoken,tok_value.strptr,i);    /* copy word to array 'latoken' */
  991.     latoken[i] = '\0';
  992.     lc_word(latoken);                        /* cvt to lowercase */
  993.                                             /* script keywords */
  994.                     /* scan table for keyword match */
  995.     for (i=0; *(s_toktab[i].name) && strcmp(latoken,s_toktab[i].name); ++i)
  996.         ;
  997.     if (*s_toktab[i].name)    {                /* lc keyword recognized */
  998.         if (s_toktab[i].token==STRUE)
  999.             tok_value.numval = TRUE;
  1000.         if (s_toktab[i].token==SFALSE)
  1001.             tok_value.numval = FALSE;
  1002.         *pcptr = cptr;
  1003.         return s_toktab[i].token;
  1004.     }
  1005.                                     /* system primitive (ACTION) */
  1006.                                     /* scan table for keyword match */
  1007.     for (i=0;s_acttab[i].name && strcmp(latoken,s_acttab[i].name);++i)
  1008.         ;
  1009.     if (s_acttab[i].name){            /* primitive recognized */
  1010.         tok_value.funcptr = s_acttab[i].funcptr;
  1011.         *pcptr = cptr;
  1012.         return ACTION;
  1013.     }
  1014.                                     /* user variable name */
  1015.     if ((varptr=findvar(latoken))){    /* existing user variable */
  1016.         tok_value.varptr = varptr;
  1017.         *pcptr = cptr;
  1018.         return VARNAME;
  1019.     }
  1020.                 /* could this be the name of a new variable? */
  1021.     tok_value.strptr = latoken;    /* just in case this is 'on', 'off', etc. */
  1022.     if (!setvar(latoken,VCHAR,"",0))    /* can't create it */
  1023.         return TTERROR;
  1024.     if (!(varptr=findvar(latoken)))    /* can't retrieve it */
  1025.         return TTERROR;
  1026.     else {                            /* got it */
  1027.         tok_value.varptr = varptr;
  1028.         *pcptr = cptr;
  1029.         return VARNAME;
  1030.     }
  1031. }
  1032.  
  1033. /*        utility routines called by S_parse() */
  1034.  
  1035. /*    S_affirm is a placeholder. It's the function to get a yes or no
  1036.     response from the user.
  1037. */
  1038. static 
  1039. S_affirm()
  1040. {
  1041.     char c, junk;
  1042.  
  1043.     c = getchar();
  1044.     fputc(c,tfp);
  1045.     while ((junk=getchar()) !='\n' && junk !='\r')
  1046.         fputc(junk,tfp);
  1047.  
  1048.     fputc('\r',tfp),
  1049.     fputc('\n',tfp);
  1050.     return(c=='y' || c=='Y');
  1051. }
  1052.  
  1053. /*    S_addsub increments or decrements a numeric variable.
  1054.     It assumes that since the INCR or DECR directives call
  1055.     lexan() for the variable just before coming here, the
  1056.     tok_value structure contains a pointer to the variable
  1057.     whose value is to be changed.
  1058. */
  1059. static 
  1060. S_addsub(direction)
  1061. int direction;
  1062. {
  1063.     long oldval = tok_value.varptr->u.num;
  1064.     oldval += direction;
  1065.  
  1066.     if (!setvar(tok_value.varptr->name,VNUM,"",&oldval)){
  1067.         sprintf(Msg,"Error setting variable '%s'", tok_value.varptr->name);
  1068.         S_abort();
  1069.     }
  1070.     return(tok_value.varptr->u.num ? SUCCESS : FAILURE);
  1071. }
  1072.  
  1073. /*    S_qstrip returns a pointer to a (static) string with leading and
  1074.     trailing double-quote marks removed. If the string happens to
  1075.     lack a leading or trailing double-quote mark, then the string
  1076.     will be returned with its beginning unchanged, and its length
  1077.     equal to VMAXSIZE or the length of the string, whichever is
  1078.     shorter. Double-quote marks escaped with a backslash are included
  1079.     in the returned string and the backslash is excised.
  1080. */
  1081. static char *
  1082. S_qstrip(strptr)
  1083. char *strptr;
  1084. {
  1085.     int i;
  1086.     static char strbuf[VMAXSIZE+2];
  1087.  
  1088.     if (*strptr=='"')
  1089.         ++strptr;
  1090.     for (i=0; i<VMAXSIZE+1; ++i){
  1091.         if (*strptr=='\\' && *(strptr+1)=='"' && *(strptr-1)!='\\')
  1092.             ++strptr;
  1093.         if ((*strptr=='"' && *(strptr-1)!='\\') || !*strptr || *strptr=='\n')
  1094.             break;
  1095.         strbuf[i] = *strptr;
  1096.         ++strptr;
  1097.     }
  1098.     strbuf[i] = '\0';
  1099.     return strbuf;
  1100. }
  1101.  
  1102. /*    S_read does the parsing grunts for the script 'read' directive.    On
  1103.     entry, the 'read' token has been parsed, but that's all.
  1104. */
  1105. static 
  1106. S_read(pcptr)
  1107. char **pcptr;
  1108. {
  1109.     int i;
  1110.     VAR *varptr1;
  1111.     static char strbuf[VMAXSIZE+2];
  1112.  
  1113.     if (lexan(pcptr)!=VARNAME)
  1114.         S_abort();
  1115.     varptr1 = tok_value.varptr;
  1116.     strbuf[0] = '\0';
  1117.     for (i=0; i<VMAXSIZE; ++i){
  1118.         strbuf[i] = getchar();
  1119.         if (strbuf[i]==BS){
  1120.             if (i>0)
  1121.                 fputs("\b \b",tfp),
  1122.                 i -= 2;
  1123.             else
  1124.                 i = -1;
  1125.             continue;
  1126.         }
  1127.         fputc(strbuf[i],tfp);
  1128.         if (strbuf[i]=='\n' || strbuf[i]=='\r'){
  1129.             strbuf[i] = '\0';
  1130.             break;
  1131.         }
  1132.     }
  1133.     strbuf[VMAXSIZE] = '\0';
  1134.     fputc('\r',tfp),
  1135.     fputc('\n',tfp);
  1136.     return(setvar(varptr1->name,VCHAR,strbuf,0));
  1137. }
  1138.  
  1139. /*    S_perform invokes a system primitive and returns SUCCESS if the
  1140.     primitive succeeds, FAILURE if it fails. On entry, only the ACTION
  1141.     token has been parsed.
  1142. */
  1143. static 
  1144. S_perform(pcptr)
  1145. char **pcptr;
  1146. {
  1147.     int (*fptr)();
  1148.     char *cptr;
  1149.     VAR *varptr1;
  1150.     long n = -1;
  1151.  
  1152.     fptr = tok_value.funcptr;
  1153.     cptr = NIL(char);
  1154.     while (TRUE){
  1155.         switch (lexan(pcptr)){
  1156.             case TERMINAT:
  1157.                 break;
  1158.             case NUMBER:
  1159.                 if (n != -1)
  1160.                     S_abort();
  1161.                 n = tok_value.numval;
  1162.                 continue;
  1163.             case LITERAL:
  1164.             case TTERROR:
  1165.                 cptr = S_qstrip(tok_value.strptr);
  1166.                 continue;
  1167.             case VARNAME:
  1168.                 varptr1 = tok_value.varptr;
  1169.                 if (varptr1->type==VCHAR){
  1170.                         cptr = varptr1->u.str;
  1171.                         break;
  1172.                 }
  1173.                 if (n != -1)
  1174.                     S_abort();
  1175.                 n = varptr1->u.num;
  1176.                 continue;
  1177.             default:
  1178.                 S_abort();
  1179.         }
  1180.         break;
  1181.     }
  1182.     return (*fptr)(n,cptr);
  1183. }
  1184.  
  1185. /*    S_set does the parsing grunts for the script 'assign' directive. It's
  1186.     a separate function mostly to keep from cluttering up S_parse()
  1187.     too much.    On entry, only the 'set' token has been received from
  1188.     the directive containing it.
  1189. */
  1190. static
  1191. S_set(pcptr)
  1192. char **pcptr;                /* S_parse()'s program counter (p_pc) */
  1193. {
  1194.     TOK_TYPE nexttype;
  1195.     VAR *varptr1, *varptr2;
  1196.     char *setstr;
  1197.  
  1198.     if ((nexttype=lexan(pcptr))!=VARNAME)
  1199.         S_abort();
  1200.     varptr1 = tok_value.varptr;
  1201.     if ((nexttype=lexan(pcptr))!=EQ)
  1202.         S_abort();
  1203.     switch (nexttype = lexan(pcptr)){
  1204.         case LITERAL:
  1205.             setstr = S_qstrip(tok_value.strptr);
  1206.             return(setvar(varptr1->name,VCHAR,setstr,0));
  1207.         case ACTION:
  1208.         case AFFIRM:
  1209.             if (nexttype==ACTION)
  1210.                 tok_value.numval = (long) S_perform(pcptr);
  1211.             else
  1212.                 tok_value.numval = (long) S_affirm();
  1213.         case NUMBER:
  1214.         case STRUE:
  1215.         case SFALSE:
  1216.             return(setvar(varptr1->name,VNUM,"",&tok_value.numval));
  1217.         case VARNAME:
  1218.             varptr2 = tok_value.varptr;
  1219.             switch (varptr2->type){
  1220.                 case VCHAR:
  1221.                     return(setvar(varptr1->name,VCHAR,varptr2->u.str,0));
  1222.                 default:
  1223.                     return(setvar(varptr1->name,VNUM,"",&(varptr2->u.num)));
  1224.             }
  1225.         default:
  1226.             S_abort();
  1227.     }
  1228. }
  1229.  
  1230. /*    S_varcmp() compares a variable's value with a string or numeric
  1231.     literal, or with the value of a second variable. Once again,
  1232.     this function does parsing grunts for S_parse().
  1233. */
  1234. static
  1235. S_varcmp(varptr1, pcptr)
  1236. VAR *varptr1;
  1237. char **pcptr;
  1238. {
  1239.     TOK_TYPE compmode;
  1240.     long testnum;
  1241.     static char strbuf[VMAXSIZE+1];
  1242.     char *cmpstr;
  1243.     int status, numvar;
  1244.     VAR *varptr2;
  1245.  
  1246.     numvar = (varptr1->type!=VCHAR);
  1247.     compmode = lexan(pcptr);
  1248.     switch (compmode){
  1249.         case EQ:
  1250.         case NEQ:
  1251.         case MORETHAN:
  1252.         case LESSTHAN:
  1253.             break;
  1254.         case TERMINAT:
  1255.             if (numvar)
  1256.                 return(varptr1->u.num ? TRUE : FALSE);
  1257.             else
  1258.                 return(*varptr1->u.str ? TRUE : FALSE);
  1259.         default:
  1260.             S_abort();
  1261.     }
  1262.     switch (lexan(pcptr)){
  1263.         case LITERAL:
  1264.             if (numvar){
  1265.                 sprintf(Msg,"Error: %s is a numeric variable",varptr1->name);
  1266.                 S_abort();
  1267.             }
  1268.             ++tok_value.strptr;
  1269.             strncpy(strbuf,tok_value.strptr,VMAXSIZE);
  1270.             *(strchr(strbuf,'"')) = '\0';
  1271.             cmpstr = strbuf;
  1272.             break;
  1273.         case NUMBER:
  1274.         case STRUE:
  1275.         case SFALSE:
  1276.             if (!numvar){
  1277.                 sprintf(Msg,"Error: %s is a string variable",varptr1->name);
  1278.                 S_abort();
  1279.             }
  1280.             testnum = tok_value.numval;
  1281.             break;
  1282.         case VARNAME:
  1283.             varptr2 = tok_value.varptr;
  1284.             if (numvar && varptr2->type==VCHAR){
  1285.                 sprintf(Msg,"Error: %s and %s are of different types",
  1286.                     varptr1->name, varptr2->name);
  1287.                 S_abort();
  1288.             }
  1289.             if (numvar)
  1290.                 testnum = varptr2->u.num;
  1291.             else
  1292.                 cmpstr = varptr2->u.str;
  1293.             break;
  1294.         default:
  1295.             S_abort();
  1296.     }
  1297.     if (numvar){
  1298.         status = (varptr1->u.num==testnum);
  1299.         if (compmode==EQ)
  1300.             return status;
  1301.         if (compmode==NEQ)
  1302.             return !status;
  1303.         status = (varptr1->u.num > testnum);
  1304.         if (compmode==MORETHAN)
  1305.             return status;
  1306.         else
  1307.             return !status;
  1308.     }
  1309.     status = strcmp(varptr1->u.str,cmpstr);
  1310.     if (compmode==EQ)
  1311.         return(status==0);
  1312.     if (compmode==NEQ)
  1313.         return status;
  1314.     if (compmode==MORETHAN)
  1315.         return(status>0);
  1316.     else
  1317.         return(status<0);
  1318. }
  1319.  
  1320. static char *
  1321. S_construct(pcptr)
  1322. char **pcptr;
  1323. {
  1324.     char *cptr;
  1325.     static char newstring[VMAXSIZE+10];
  1326.     TOK_TYPE nexttype;
  1327.  
  1328.     newstring[0] = '\0';
  1329.     cptr = newstring;
  1330.     while ((nexttype=lexan(pcptr))!=NULLTOK){
  1331.         if (strlen(newstring)>VMAXSIZE){
  1332.             newstring[VMAXSIZE] = '\0';
  1333.             break;
  1334.         }
  1335.         switch (nexttype){
  1336.             case TERMINAT:
  1337.                 break;
  1338.             case NUMBER:
  1339.                 sprintf(cptr,"%ld",tok_value.numval);
  1340.                 cptr += strlen(cptr);
  1341.                 continue;
  1342.             case LITERAL:
  1343.                 sprintf(cptr,"%s",S_qstrip(tok_value.strptr));
  1344.                 cptr += strlen(cptr);
  1345.                 continue;
  1346.             case VARNAME:
  1347.                 if (tok_value.varptr->type != VCHAR)
  1348.                     sprintf(cptr,"%ld",tok_value.varptr->u.num);
  1349.                 else
  1350.                     sprintf(cptr,"%s",tok_value.varptr->u.str);
  1351.                 cptr += strlen(cptr);
  1352.                 continue;
  1353.             default:
  1354.                 S_abort();
  1355.         }
  1356.         break;
  1357.     }
  1358.     return newstring;
  1359. }
  1360.  
  1361. /*        stack protection and break/continue stuff */
  1362.  
  1363. static int    nest_while,        /* number of nested loops */
  1364.             nest_parse,        /* number of nested calls to S_parse() */
  1365.             nest_cmd;        /* number of nested commands */
  1366.  
  1367. static long deadline;        /* deadline for 'timeout' keyword */
  1368.  
  1369. jmp_buf env;                /* cell for environment, setjmp */
  1370.  
  1371. #define CMDNEST        8        /* max number of nested scripts */
  1372. #define PARSNEST    50        /* max number of nested calls to parser */
  1373.  
  1374. static char *areas[CMDNEST];
  1375.  
  1376. #define DNP        --nest_parse
  1377. #define BCCHK(x) if(x==SBREAK||x==SCONTNUE){DNP;return(nest_while?x:S_abort());}
  1378.  
  1379. /* cleanup any debris left after a non-trapped keyboard interrupt */
  1380. static void 
  1381. S_bombout()
  1382. {
  1383.     int i;
  1384.  
  1385.     for (i=0; i<nest_cmd; ++i){
  1386.         if (areas[i])
  1387.             free(areas[i]),
  1388.             areas[i] = NIL(char);
  1389.     }
  1390.     nest_cmd = nest_while = nest_parse = 0;
  1391.     deadline = 0;
  1392. }
  1393.  
  1394. /*    S_parse() */
  1395. static char    *intercom,        /* last previous value of S_parse program ctr */
  1396.             *tvector;        /* trap vector */
  1397. static FILE *savetfp;        /* to stash tfp when tfp redirected */
  1398. static 
  1399. S_parse(p_pc, t_invoke)
  1400. char *p_pc;
  1401. TOK_TYPE t_invoke;
  1402. {
  1403.     long n;                    /* "leading" number for primitives */
  1404.     int i,
  1405.         status,                /* status of last performed operation */
  1406.         retval,                /* value to be returned by this function */
  1407.         testing,            /* flag set if within 'if' or 'while' clause */
  1408.         direction,            /* if 1, increment, if -1, decrement */
  1409.         w_status,            /* used to correct nest_parse in 'while' */
  1410.         counter,            /* for WHILE and IF, to track keywords */
  1411.         negating;            /* ! operator in effect for comparisons */
  1412.     char *cptr, *S_WHILE, *S_DO, *S_DONE;
  1413.     TOK_TYPE nexttype;
  1414.     VAR *varptr1;
  1415.  
  1416.     ++nest_parse;
  1417.     testing = (t_invoke==THEN || t_invoke==DO);
  1418.     retval = status = negating = FALSE;
  1419.     n = -1;
  1420.     counter = 0;
  1421.     while (TRUE){
  1422.         /* we come through here only at the beginning of expressions */
  1423.         if (deadline){
  1424.             if (time(0)>deadline){        /* deadline; exit current script */
  1425.                 deadline = 0;
  1426.                 longjmp(env,TIMEOUT);
  1427.             }
  1428.         }
  1429.         if (BREAK && tvector && t_invoke!=ENDTRAP)    /* interrupt trap */
  1430.             BREAK = FALSE,
  1431.             cptr = tvector,
  1432.             status = S_parse(cptr,ENDTRAP),
  1433.             DNP;
  1434.  
  1435.         BREAK = FALSE;
  1436.         retval = testing ? (retval || status) : status;
  1437.         direction = 1;
  1438.         intercom = p_pc;
  1439.         nexttype = lexan(&p_pc);
  1440.                 /* check for list terminator */
  1441.         if (nexttype==t_invoke || (t_invoke==ENDIF && nexttype==ELSE)){
  1442.             if (debugflag && testing)
  1443.                 fprintf(tfp,"\r\n\t\t\t\t\t\t\tCondition: %s\r\n",
  1444.                     retval ? "TRUE" : "FALSE");
  1445.             return retval;
  1446.         }
  1447.         switch (nexttype){
  1448.             case NULLTOK:        /* inconsistent list terminators */
  1449.             case DO:                                /**/
  1450.             case DONE:                                /**/
  1451.             case THEN:                                /**/
  1452.             case ELSE:                                /**/
  1453.             case ENDIF:                                /**/
  1454.             case TTERROR:                            /**/
  1455.             case EFLAG:            /* not at beginning of expressions */
  1456.             case EQ:                                /**/
  1457.             case NEQ:                                /**/
  1458.             case MORETHAN:                            /**/
  1459.             case LESSTHAN:                            /**/
  1460.                 S_abort();
  1461.             case LITERAL:
  1462.                 if (!testing)
  1463.                     S_abort();
  1464.                 status = !!strlen(tok_value.strptr);
  1465.                 if (negating)
  1466.                     status = !status;
  1467.                 continue;
  1468.             case NEGATE:
  1469.                 if (!testing || negating)
  1470.                     S_abort();
  1471.                 negating = 1;
  1472.                 continue;
  1473.             case NUMBER:
  1474.                 if (n!=(-1))
  1475.                     S_abort();
  1476.                 n = tok_value.numval;
  1477.                 continue;
  1478.             case TERMINAT:
  1479.                 negating = 0;
  1480.                 continue;
  1481.             case ACTION:
  1482.                 status = S_perform(&p_pc);
  1483.                 if (negating)
  1484.                     status = !status;
  1485.                 break;
  1486.             case AFFIRM:
  1487.                 status = S_affirm();
  1488.                 if (negating)
  1489.                     status = !status;
  1490.                 break;
  1491.             case SBREAK:
  1492.             case SCONTNUE:
  1493.                 if (testing || t_invoke==NULLTOK)
  1494.                     S_abort();
  1495.                 return(nexttype==SBREAK ? SBREAK : SCONTNUE);
  1496.             case CALL:
  1497.                 lexan(&p_pc);
  1498.                 i = nest_while;
  1499.                 nest_while = 0;
  1500.                 S_call(S_qstrip(tok_value.strptr));
  1501.                 DNP;
  1502.                 nest_while = i;
  1503.                 break;
  1504.             case COMMENT:
  1505.                 continue;
  1506.             case DECR:
  1507.                 direction = (-1);        /* and fall through to... */
  1508.             case INCR:
  1509.                 if ((nexttype=lexan(&p_pc))!=VARNAME)
  1510.                     S_abort();
  1511.                 if ((tok_value.varptr->type) != VNUM){
  1512.                     sprintf(Msg,"Error: %s is not a numeric variable",
  1513.                         tok_value.varptr->name);
  1514.                     S_abort();
  1515.                 }
  1516.                 status = S_addsub(direction);
  1517.                 if (negating) status =
  1518.                     !status;
  1519.                 break;
  1520.             case ECHOS:
  1521.                 status = 1;
  1522.                 if ((nexttype = lexan(&p_pc))==EFLAG)
  1523.                     status = 0;
  1524.                 else
  1525.                     p_pc = intercom,
  1526.                     lexan(&p_pc);
  1527.                 cptr = S_construct(&p_pc);
  1528.                 fprintf(tfp,"%s",cptr);
  1529.                 if (status)
  1530.                     if (tfp != cfp)
  1531.                         fputc('\r',tfp),
  1532.                     fputc('\n',tfp);
  1533.                 else
  1534.                     status = 1;
  1535.                 break;
  1536.             case EXIT:
  1537.                 longjmp(env,EXIT);
  1538.             case READ:
  1539.                 status = S_read(&p_pc);
  1540.                 break;
  1541.             case SET:
  1542.                 status = S_set(&p_pc);
  1543.                 if (negating)
  1544.                     status = !status;
  1545.                 break;
  1546.             case SHELL:
  1547.                 sprintf(word,"!");
  1548.                 sprintf(line,"%s",S_construct(&p_pc));
  1549.                 if (tfp==cfp)
  1550.                     sprintf(&line[strlen(line)]," >>%s",captfile);
  1551.                 lptr = line;
  1552.                 status = !s_shell();
  1553.                 if (negating)
  1554.                     status = !status;
  1555.                 break;
  1556.             case PIPE:
  1557.                 sprintf(word,"$");
  1558.                 sprintf(line,"%s",S_construct(&p_pc));
  1559.                 lptr = line;
  1560.                 status = !s_shell();
  1561.                 if (negating)
  1562.                     status = !status;
  1563.                 break;
  1564.             case SFILE:
  1565.                 savetfp = tfp;
  1566.                 if (!capture){
  1567.                     S2("Capture option not on");
  1568.                     while ((nexttype=lexan(&p_pc))!=TERMINAT &&
  1569.                         nexttype != NULLTOK);
  1570.                     break;
  1571.                 }
  1572.                 tfp = cfp;
  1573.                 status = S_parse(p_pc,TERMINAT);
  1574.                 if (negating)
  1575.                     status = !status;
  1576.                 DNP;
  1577.                 while ((nexttype=lexan(&p_pc)) !=TERMINAT &&
  1578.                     nexttype != NULLTOK)
  1579.                     ;
  1580.                 tfp = savetfp;
  1581.                 fseek(cfp,0L,2);
  1582.                 break;
  1583.             case STRUE:
  1584.             case SFALSE:
  1585.                 status = (nexttype==STRUE);
  1586.                 intercom = p_pc;
  1587.                 if ((nexttype=(lexan(&p_pc)))!=TERMINAT && nexttype!=NULLTOK)
  1588.                         S_abort();
  1589.                 if (negating)
  1590.                     status = !status;
  1591.                 break;
  1592.             case STRAP:
  1593.                 tvector = p_pc;
  1594.                 cptr = tvector;
  1595.                 while ((nexttype=lexan(&p_pc))!=ENDTRAP)
  1596.                     if (nexttype==NULLTOK)
  1597.                         S_abort();
  1598.                 break;
  1599.             case TIMEOUT:
  1600.                 if ((nexttype=lexan(&p_pc))!=NUMBER)
  1601.                     S_abort();
  1602.                 if (tok_value.numval>=0){
  1603.                     deadline = 0;
  1604.                     if (tok_value.numval)
  1605.                         deadline = time(0) + (tok_value.numval*60);
  1606.                 }
  1607.                 while ((nexttype=lexan(&p_pc))!=TERMINAT && nexttype!=NULLTOK)
  1608.                     ;
  1609.                 break;
  1610.             case UNSET:
  1611.                 if ((nexttype=lexan(&p_pc))!=VARNAME)
  1612.                     S_abort();
  1613.                 status = 1;
  1614.                 unsetvar(tok_value.varptr->name);
  1615.                 break;
  1616.             case UNTRAP:
  1617.                 tvector = NIL(char);
  1618.                 if ((nexttype=lexan(&p_pc))!=TERMINAT)
  1619.                     S_abort();
  1620.                 break;
  1621.             case XCSET:
  1622.                 i = 0;
  1623.                 line[0] = '\0';
  1624.                 while (*p_pc!='\n')
  1625.                     line[i++] = *(p_pc++);
  1626.                 line[i] = '\0';
  1627.                 lptr = line;
  1628.                 s_set();
  1629.                 break;
  1630.             case VARNAME:
  1631.                 varptr1 = tok_value.varptr;
  1632.                 if (!testing){
  1633.                     if (varptr1->type==VCHAR || n!=(-1))
  1634.                         S_abort();
  1635.                     n = varptr1->u.num;
  1636.                     continue;
  1637.                 }
  1638.                 status = S_varcmp(varptr1,&p_pc);
  1639.                 if (negating)
  1640.                     status = !status;
  1641.                 break;
  1642.             case IF:
  1643.                 if (nest_parse > PARSNEST){
  1644.                     sprintf(Msg,"Nesting level too deep");
  1645.                     S_abort();
  1646.                 }
  1647.                 status = S_parse(p_pc,THEN);
  1648.                 DNP;
  1649.                 if (status==TRUE){
  1650.                     lexan(&intercom);
  1651.                     p_pc = intercom;
  1652.                     status = S_parse(p_pc,ENDIF);
  1653.                     BCCHK(status)
  1654.                     DNP;
  1655.                     cptr = intercom;
  1656.                     nexttype = lexan(&cptr);
  1657.                     p_pc = cptr;
  1658.                     if (nexttype==ELSE){
  1659.                         counter = 0;
  1660.                         while (TRUE){
  1661.                             switch ((nexttype=lexan(&cptr))){
  1662.                                 case IF:
  1663.                                     ++counter;
  1664.                                     continue;
  1665.                                 case ENDIF:
  1666.                                     if (counter){
  1667.                                         --counter;
  1668.                                         continue;
  1669.                                     }
  1670.                                     p_pc = cptr;
  1671.                                     break;
  1672.                                 case NULLTOK:
  1673.                                     S_abort();
  1674.                                 default:
  1675.                                     continue;
  1676.                             }
  1677.                             break;
  1678.                         } /* now p_pc points to just after matching 'endif' */
  1679.                     }
  1680.                 }
  1681.                 else {    /* intercom is now the THEN token */
  1682.                     counter = 0;
  1683.                     cptr = intercom;
  1684.                     while (TRUE){
  1685.                         switch ((nexttype=lexan(&cptr))){
  1686.                             case IF:
  1687.                                 ++counter;
  1688.                                 continue;
  1689.                             case ELSE:
  1690.                             case ENDIF:
  1691.                                 if (counter){
  1692.                                     --counter;
  1693.                                     continue;
  1694.                                 }
  1695.                                 p_pc = cptr;
  1696.                                 break;
  1697.                             case NULLTOK:
  1698.                                 S_abort();
  1699.                             default:
  1700.                                 continue;
  1701.                         }
  1702.                         break;
  1703.                     }
  1704.                     if (nexttype==ELSE){
  1705.                         status = S_parse(p_pc,ENDIF);
  1706.                         BCCHK(status)
  1707.                         DNP;
  1708.                         p_pc = intercom;
  1709.                         lexan(&p_pc);
  1710.                     }
  1711.                     /* p_pc now points to just after ENDIF */
  1712.                 }
  1713.                 break;
  1714.             case WHILE:
  1715.                 if (nest_parse > PARSNEST){
  1716.                     sprintf(Msg,"Nesting level too deep");
  1717.                     S_abort();
  1718.                 }
  1719.                 S_WHILE = p_pc;
  1720.                 S_DO = S_DONE = NIL(char);
  1721.                 while ((w_status=S_parse(S_WHILE,DO))==TRUE){
  1722.                     DNP;
  1723.                     --nest_while;
  1724.                     if (!S_DO)
  1725.                         lexan(&intercom),
  1726.                         S_DO = intercom;
  1727.                     status = S_parse(S_DO,DONE);
  1728.                     DNP;
  1729.                     --nest_while;
  1730.                     if (status == SBREAK){
  1731.                         status = 0;
  1732.                         break;
  1733.                     }                    /* note SCONTNUE is automatic */
  1734.                     if (!S_DONE && status!=SCONTNUE)
  1735.                         lexan(&intercom),
  1736.                         S_DONE = intercom;
  1737.                 }
  1738.                 if (S_DONE)
  1739.                     p_pc = S_DONE;
  1740.                 else {
  1741.                     cptr = S_DO ? S_DO : S_WHILE;
  1742.                     while (TRUE){
  1743.                         switch ((nexttype=lexan(&cptr))){
  1744.                             case WHILE:
  1745.                                 ++counter;
  1746.                                 continue;
  1747.                             case DONE:
  1748.                                 if (counter){
  1749.                                     --counter;
  1750.                                     continue;
  1751.                                 }
  1752.                                 p_pc = cptr;
  1753.                                 break;
  1754.                             case NULLTOK:
  1755.                                 S_abort();
  1756.                             default:
  1757.                                 continue;
  1758.                         }
  1759.                         break;
  1760.                     }
  1761.                     /* p_pc now points to just after matching 'done' */
  1762.                 }
  1763.                 if (w_status==FALSE)
  1764.                     DNP;
  1765.                 break;
  1766.         } /* end of main switch, whew */
  1767.         if (t_invoke==TERMINAT)
  1768.             return status;
  1769.         n = -1;
  1770.     }
  1771. }
  1772.  
  1773. /* load a script and call S_parse to run it */
  1774. static void 
  1775. S_call(scriptname)
  1776. char *scriptname;
  1777. {
  1778.     int i;
  1779.     jmp_buf senv;
  1780.     char *oldtvec, *newptr, *oldptr, script[SM_BUFF];
  1781.     long filesize;
  1782.     FILE *scriptfp;
  1783.     static struct stat statbuf;
  1784.  
  1785.     strcpy(script, scriptname);
  1786.     memset(Msg, 0, SM_BUFF);
  1787.     if (++nest_cmd>CMDNEST){
  1788.         S2("Too many nested scripts");
  1789.         --nest_cmd;
  1790.         return;
  1791.     }
  1792.     if (!(scriptfp = openfile(script))){
  1793.         sprintf(Msg,"Can't open '%s'",script);
  1794.         S2(Msg);
  1795.         --nest_cmd;
  1796.         return;
  1797.     }
  1798.  
  1799.     /* this succeeds, openfile() has called isregfile() to stat the file */
  1800.     fstat(fileno(scriptfp),&statbuf);
  1801.     filesize = statbuf.st_size;
  1802.  
  1803.     areas[nest_cmd - 1] = NIL(char);
  1804.     if (!(areas[nest_cmd-1]=(char*)calloc((unsigned)filesize+10,1))){
  1805.         sprintf(Msg,"%s: allocation error",script);
  1806.         S2(Msg);
  1807.         fclose(scriptfp);
  1808.         --nest_cmd;
  1809.         return;
  1810.     }
  1811.     if (strcmp(STARTUP,script))
  1812.         sprintf(Msg,"Running %s",script),
  1813.         S2(Msg);
  1814.     *(areas[nest_cmd - 1]) = '\n';
  1815.     fread((areas[nest_cmd - 1] + 1),filesize,1,scriptfp);
  1816.     *(areas[nest_cmd - 1] + filesize + 1) = '\0';
  1817.     fclose(scriptfp);
  1818.  
  1819.     oldtvec = tvector;
  1820.     tvector = NIL(char);
  1821.     newptr = (char *)senv;
  1822.     oldptr = (char *)env;
  1823.     for (i=0; i<sizeof(env); ++i)
  1824.         *(newptr++) = *(oldptr++);
  1825.     if ((i=setjmp(env))==0)
  1826.         S_parse(areas[nest_cmd - 1],NULLTOK);
  1827.     else if (i==TTERROR)
  1828.         S2("Abnormal script termination");
  1829.     else if (i==TIMEOUT)
  1830.         S2("Timeout");
  1831.     else if (i==EXIT)
  1832.         S2("Script encountered 'exit'");
  1833.  
  1834.     tvector = oldtvec;
  1835.     newptr = (char *)env;
  1836.     oldptr = (char *)senv;
  1837.     for (i=0; i<sizeof(env); ++i)
  1838.         *(newptr++) = *(oldptr++);
  1839.     --nest_cmd;
  1840.     if (areas[nest_cmd])
  1841.         free(areas[nest_cmd]);
  1842.  
  1843.     if (strcmp(STARTUP,script))
  1844.         sprintf(Msg,"%s COMPLETE",script),
  1845.         S2(Msg);
  1846. }
  1847.  
  1848. static 
  1849. S_abort()
  1850. {
  1851.     char *cptr;
  1852.  
  1853.     if (*Msg)
  1854.         S2(Msg);
  1855.  
  1856.     cptr = intercom;
  1857.     while (*cptr && *cptr!='\n')
  1858.         --cptr;
  1859.  
  1860.     ++cptr;
  1861.     while (*cptr && *cptr!='\n')
  1862.         fputc(*(cptr++),tfp);
  1863.     fputc('\r',tfp),
  1864.     fputc('\n',tfp);
  1865.  
  1866.     if (tfp==cfp)
  1867.         tfp = savetfp;
  1868.     unsetall();
  1869.     longjmp(env,TTERROR);
  1870. }
  1871.  
  1872. /*    get_bound_char() get a character from the user's keyboard.  If the terminal
  1873.     mode escape character is seen, look ahead to the next character and act on
  1874.     it. If unrecognized, simply swallow the escape character and return the
  1875.     second character. Returns ASCII character code, or XCBIND function code
  1876. */
  1877. get_bound_char()
  1878. {
  1879.     int            c, lc;
  1880.     binding_t    *ptr;
  1881.     static char    *emit_string = NIL(char);
  1882.  
  1883.     if (emit_string) {
  1884.         c = *(emit_string++);
  1885.         if (!c)
  1886.             emit_string = NIL(char);
  1887.     }
  1888.  
  1889.     if (!emit_string) {
  1890.         c = getchar();
  1891.         if (c == my_escape) {
  1892.             lc = tolower(c=getchar());
  1893.             for (ptr = first_binding; ptr; ptr = ptr->bs_next){
  1894.                 if (ptr->bs_c == lc){
  1895.                     switch (ptr->bs_function){
  1896.                     case EMITSTR:
  1897.                         emit_string = ptr->bs_string;
  1898.                         return *(emit_string++);
  1899.                     case DOSCRPT:
  1900.                         strcpy(ddsname, ptr->bs_string);
  1901.                         return DOSCRPT;
  1902.                     default:
  1903.                         return ptr->bs_function;
  1904.                     }
  1905.                 }
  1906.             }
  1907.         }
  1908.     }
  1909.     return c;
  1910. }
  1911.  
  1912. /*    default_bindings restores XC to its default key bindings.
  1913.     Uppercase keys are mapped to lower case.
  1914. */
  1915. void 
  1916. default_bindings()
  1917. {
  1918.     bind_key('/', HLPCHAR, "");
  1919.     bind_key('?', HLPCHAR, "");
  1920.     bind_key('b', BRKCHAR, "");
  1921.     bind_key('d', DIALCHR, "");
  1922.     bind_key('f', DIVCHAR, "");
  1923.     bind_key('h', HUPCHAR, "");
  1924.     bind_key('n', CAPTEND, "");
  1925.     bind_key('q', QUITCHR, "");
  1926.     bind_key('s', SCRPCHR, "");
  1927.     bind_key('x', ENDCHAR, "");
  1928.     bind_key('y', CAPTYES, "");
  1929. }
  1930.  
  1931. /*    show_emit() performs an unctrl() operation on an entire buffer. */
  1932. static void 
  1933. show_emit(str)
  1934. char *str;
  1935. {
  1936.     while (*str)
  1937.         fprintf(tfp,"%s", unctrl(*str++));
  1938. }
  1939.  
  1940. /*    get_function() returns the description corresponding to the function code
  1941.     specified by (code).
  1942. */
  1943. static char *
  1944. get_function(code)
  1945. int code;
  1946. {
  1947.     bindstr_t *ptr = function_list;
  1948.     int i;
  1949.  
  1950.     for (i = 0; i < FUNCTION_COUNT; i++, ptr++)
  1951.         if ((int) ptr->bf_function == code)
  1952.             return ptr->bf_string;
  1953.  
  1954.     return "???";
  1955. }
  1956.  
  1957. show_bindings()
  1958. {
  1959.     binding_t *ptr = first_binding;
  1960.     char *escape_str = strdup(unctrl(my_escape));
  1961.     int curline = 0;
  1962.  
  1963.     cls();
  1964.  
  1965.     fprintf(tfp,"\tTERMINAL mode escape character is %s.\r\n\r\n", escape_str);
  1966.     curline += 2;
  1967.  
  1968.     while (ptr){
  1969.         if (curline >= LI - 2){
  1970.             S0("PRESS ENTER");
  1971.             getline();
  1972.             cls();
  1973.             curline = 0;
  1974.         }
  1975.  
  1976.         fprintf(tfp,"\t%s - %-3.3s %s", escape_str, unctrl(ptr->bs_c),
  1977.             get_function(ptr->bs_function));
  1978.  
  1979.         switch (ptr->bs_function){
  1980.         case EMITSTR:
  1981.         case DOSCRPT:
  1982.             fputc(' ',tfp);
  1983.             fputc('"',tfp);
  1984.             show_emit(ptr->bs_string);
  1985.             fputc('"',tfp);
  1986.             break;
  1987.         }
  1988.         fputc('\r',tfp);
  1989.         fputc('\n',tfp);
  1990.         curline++;
  1991.  
  1992.         ptr = ptr->bs_next;
  1993.     }
  1994.  
  1995.     free(escape_str);
  1996. }
  1997.  
  1998. /*    find_function() returns the function code referenced by the name
  1999.     pointed to by (name).  Returns BADFUNC if the name is unrecognized.
  2000. */
  2001. static 
  2002. bindfunc_t find_function(name)
  2003. char *name;
  2004. {
  2005.     bindstr_t *ptr = function_list;
  2006.     int i;
  2007.  
  2008.     for (i = 0; i < FUNCTION_COUNT; i++, ptr++)
  2009.         if (strcmp(ptr->bf_name, name) == 0)
  2010.             return ptr->bf_function;
  2011.  
  2012.     return BADFUNC;
  2013. }
  2014.