home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / src / runtime / imain.r < prev    next >
Text File  |  2001-12-12  |  18KB  |  696 lines

  1. #if !COMPILER
  2. /*
  3.  * File: imain.r
  4.  * Interpreter main program, argument handling, and such.
  5.  * Contents: main, icon_call, icon_setup, resolve, xmfree
  6.  */
  7.  
  8. #include "../h/version.h"
  9. #include "../h/header.h"
  10. #include "../h/opdefs.h"
  11.  
  12. void    icon_setup    (int argc, char **argv, int *ip);
  13.  
  14. extern int set_up;
  15.  
  16. word istart[4];
  17. int mterm = Op_Quit;
  18.  
  19. #ifndef MultiThread
  20.    int n_globals = 0;            /* number of globals */
  21.    int n_statics = 0;            /* number of statics */
  22. #endif                    /* MultiThread */
  23.  
  24.  
  25.  
  26. #ifdef MSWindows
  27. /*
  28.  * Special initialization under MS Windows.
  29.  */
  30.  
  31. FILE *finredir, *fouredir, *ferredir;
  32.  
  33. void detectRedirection()
  34. {
  35.    struct stat sb;
  36.    /*
  37.     * Look at the standard file handles and attempt to detect
  38.     * redirection.
  39.     */
  40.    if (fstat(stdin->_file, &sb) == 0) {
  41.       if (sb.st_mode & S_IFCHR) {        /* stdin is a device */
  42.      }
  43.       if (sb.st_mode & S_IFREG) {        /* stdin is a regular file */
  44.      }
  45.       /* stdin is of size sb.st_size */
  46.       if (sb.st_size > 0) {
  47.          ConsoleFlags |= StdInRedirect;
  48.      }
  49.       }
  50.    else {                    /* unable to identify stdin */
  51.       }
  52.  
  53.    if (fstat(stdout->_file, &sb) == 0) {
  54.       if (sb.st_mode & S_IFCHR) {        /* stdout is a device */
  55.      }
  56.       if (sb.st_mode & S_IFREG) {        /* stdout is a regular file */
  57.      }
  58.       /* stdout is of size sb.st_size */
  59.       if (sb.st_size == 0)
  60.          ConsoleFlags |= StdOutRedirect;
  61.       }
  62.    else {                    /* unable to identify stdout */
  63.      }
  64. }
  65.  
  66. int CmdParamToArgv(char *s, char ***avp)
  67.    {
  68.    char tmp[MaxPath], dir[MaxPath];
  69.    char *t, *t2;
  70.    int rv=0, i;
  71.    FILE *f;
  72.  
  73.    t = salloc(s);
  74.    t2 = t;
  75.  
  76.    *avp = malloc(2 * sizeof(char *));
  77.    (*avp)[rv] = NULL;
  78.  
  79.    detectRedirection();
  80.  
  81.    while (*t2) {
  82.       while (*t2 && isspace(*t2)) t2++;
  83.       switch (*t2) {
  84.      case '\0': break;
  85.      case '<': case '>': {
  86.         /*
  87.          * perform file redirection; this is for Windows 3.1
  88.          * and other situations where Wiconx is launched from
  89.          * a shell that does not process < and > characters.
  90.          */
  91.         char c = *t2++, buf[128], *t3;
  92.         FILE *f;
  93.         while (*t2 && isspace(*t2)) t2++;
  94.         t3 = buf;
  95.         while (*t2 && !isspace(*t2)) *t3++ = *t2++;
  96.         *t3 = '\0';
  97.         if (c == '<')
  98.            f = fopen(buf, "r");
  99.         else
  100.            f = fopen(buf, "w");
  101.         if (f == NULL) {
  102.            MessageBox(0, "unable to redirect i/o", "system error",
  103.               MB_ICONHAND);
  104.            c_exit(-1);
  105.            }
  106.         if (c == '<') {
  107.            finredir = f;
  108.            ConsoleFlags |= StdInRedirect;
  109.            }
  110.         else {
  111.            fouredir = f;
  112.            ConsoleFlags |= StdOutRedirect;
  113.            }
  114.         break;
  115.         }
  116.      case '"': {
  117.         char *t3 = ++t2;            /* skip " */
  118.  
  119.             while (*t2 && (*t2 != '"')) t2++;
  120.             if (*t2)
  121.            *t2++ = '\0';
  122.         *avp = realloc(*avp, (rv + 2) * sizeof (char *));
  123.         (*avp)[rv++] = salloc(t3);
  124.             (*avp)[rv] = NULL;
  125.  
  126.         break;
  127.         }
  128.          default: {
  129.             FINDDATA_T fd;
  130.         char *t3 = t2;
  131.             while (*t2 && !isspace(*t2)) t2++;
  132.         if (*t2)
  133.            *t2++ = '\0';
  134.             strcpy(tmp, t3);
  135.         if (!FINDFIRST(tmp, &fd)) {
  136.            *avp = realloc(*avp, (rv + 2) * sizeof (char *));
  137.            (*avp)[rv++] = salloc(t3);
  138.                (*avp)[rv] = NULL;
  139.                }
  140.         else {
  141.                int end;
  142.                strcpy(dir, t3);
  143.            do {
  144.               end = strlen(dir)-1;
  145.               while (end >= 0 && dir[end] != '\\' && dir[end] != '/' &&
  146.             dir[end] != ':') {
  147.                      dir[end] = '\0';
  148.              end--;
  149.                  }
  150.           strcat(dir, FILENAME(&fd));
  151.               *avp = realloc(*avp, (rv + 2) * sizeof (char *));
  152.               (*avp)[rv++] = salloc(dir);
  153.                   (*avp)[rv] = NULL;
  154.               } while (FINDNEXT(&fd));
  155.            FINDCLOSE(&fd);
  156.            }
  157.             break;
  158.         }
  159.          }
  160.       }
  161.  
  162.    free(t);
  163.    return rv;
  164.    }
  165.  
  166. char *lognam;
  167. char tmplognam[128];
  168.  
  169. void MSStartup(HINSTANCE hInstance, HINSTANCE hPrevInstance)
  170.    {
  171.    WNDCLASS wc;
  172.    #ifdef ConsoleWindow
  173.       extern FILE *flog;
  174.  
  175.       /*
  176.        * Select log file name.  Might make this a command-line option.
  177.        * Default to "WICON.LOG".  The log file is used by Wi to report
  178.        * translation errors and jump to the offending source code line.
  179.        */
  180.       if ((lognam = getenv("WICONLOG")) == NULL)
  181.          lognam = "WICON.LOG";
  182.       remove(lognam);
  183.       lognam = strdup(lognam);
  184.       flog = fopen(tmpnam(tmplognam), "w");
  185.  
  186.       if (flog == NULL) {
  187.          syserr("unable to open logfile");
  188.          }
  189.    #endif                /* ConsoleWindow */
  190.    if (!hPrevInstance) {
  191.       #if NT
  192.          wc.style = CS_HREDRAW | CS_VREDRAW;
  193.       #else                /* NT */
  194.          wc.style = 0;
  195.       #endif                /* NT */
  196.       wc.lpfnWndProc = WndProc;
  197.       wc.cbClsExtra = 0;
  198.       wc.cbWndExtra = 0;
  199.       wc.hInstance  = hInstance;
  200.       wc.hIcon      = NULL;
  201.       wc.hCursor    = NULL;
  202.       wc.hbrBackground = GetStockObject(WHITE_BRUSH);
  203.       wc.lpszMenuName = NULL;
  204.       wc.lpszClassName = "iconx";
  205.       RegisterClass(&wc);
  206.       }
  207.    }
  208.  
  209. void iconx(int argc, char **argv);
  210.  
  211. jmp_buf mark_sj;
  212.  
  213. int_PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  214.                    LPSTR lpszCmdLine, int nCmdShow)
  215.    {
  216.    int argc;
  217.    char **argv;
  218.  
  219.    mswinInstance = hInstance;
  220.    ncmdShow = nCmdShow;
  221.  
  222.    argc = CmdParamToArgv(GetCommandLine(), &argv);
  223.    MSStartup(hInstance, hPrevInstance);
  224.    if (setjmp(mark_sj) == 0)
  225.       iconx(argc,argv);
  226.    while (--argc>=0)
  227.       free(argv[argc]);
  228.    free(argv);
  229.    wfreersc();
  230.    xmfree();
  231.    return 0;
  232. }
  233. #define main iconx
  234. #endif                    /* MSWindows */
  235.  
  236. /*
  237.  * Initial icode sequence. This is used to invoke the main procedure with one
  238.  *  argument.  If main returns, the Op_Quit is executed.
  239.  */
  240. int main(argc, argv)
  241. int argc;
  242. char **argv;
  243.    {
  244.    int i, slen;
  245.  
  246. #ifdef MultiThread
  247.    /*
  248.     * Look for MultiThread programming environment in which to execute
  249.     * this program, specified by MTENV environment variable.
  250.     */
  251.    {
  252.    char *p;
  253.    char **new_argv;
  254.    int i, j = 1, k = 1;
  255.    if ((p = getenv("MTENV")) != NULL) {
  256.       for(i=0;p[i];i++)
  257.      if (p[i] == ' ')
  258.         j++;
  259.       new_argv = malloc((argc + j) * sizeof(char *));
  260.       new_argv[0] = argv[0];
  261.       for (i=0; p[i]; ) {
  262.      new_argv[k++] = p+i;
  263.      while (p[i] && (p[i] != ' '))
  264.         i++;
  265.      if (p[i] == ' ')
  266.         p[i++] = '\0';
  267.      }
  268.       for(i=1;i<argc;i++)
  269.      new_argv[k++] = argv[i];
  270.       argc += j;
  271.       argv = new_argv;
  272.       }
  273.    }
  274. #endif                    /* MultiThread */
  275.  
  276.    ipc.opnd = NULL;
  277.  
  278. #if UNIX
  279.    /*
  280.     *  Append to FPATH the bin directory from which iconx was executed.
  281.     */
  282.    {
  283.       char *p, *q, buf[1000];
  284.       p = getenv("FPATH");
  285.       q = relfile(argv[0], "/..");
  286.       sprintf(buf, "FPATH=%s %s", (p ? p : "."), (q ? q : "."));
  287.       putenv(buf);
  288.       }
  289. #endif
  290.  
  291.    /*
  292.     * Setup Icon interface.  It's done this way to avoid duplication
  293.     *  of code, since the same thing has to be done if calling Icon
  294.     *  is enabled.
  295.     */
  296.  
  297.    icon_setup(argc, argv, &i);
  298.  
  299.    if (i < 0) {
  300.       argc++;
  301.       argv--;
  302.       i++;
  303.       }
  304.  
  305.    while (i--) {            /* skip option arguments */
  306.       argc--;
  307.       argv++;
  308.       }
  309.  
  310.    if (argc <= 1)
  311.       error(NULL, "no icode file specified");
  312.  
  313.    /*
  314.     * Call icon_init with the name of the icode file to execute.    [[I?]]
  315.     */
  316.    icon_init(argv[1], &argc, argv);
  317.  
  318.    /*
  319.     *  Point sp at word after b_coexpr block for &main, point ipc at initial
  320.     *    icode segment, and clear the gfp.
  321.     */
  322.  
  323.    stackend = stack + mstksize/WordSize;
  324.    sp = stack + Wsizeof(struct b_coexpr);
  325.  
  326.    ipc.opnd = istart;
  327.    *ipc.op++ = Op_Noop;  /* aligns Invoke's operand */    /*    [[I?]] */
  328.    *ipc.op++ = Op_Invoke;                /*    [[I?]] */
  329.    *ipc.opnd++ = 1;
  330.    *ipc.op = Op_Quit;
  331.    ipc.opnd = istart;
  332.  
  333.    gfp = 0;
  334.  
  335.    /*
  336.     * Set up expression frame marker to contain execution of the
  337.     *  main procedure.  If failure occurs in this context, control
  338.     *  is transferred to mterm, the address of an Op_Quit.
  339.     */
  340.    efp = (struct ef_marker *)(sp);
  341.    efp->ef_failure.op = &mterm;
  342.    efp->ef_gfp = 0;
  343.    efp->ef_efp = 0;
  344.    efp->ef_ilevel = 1;
  345.    sp += Wsizeof(*efp) - 1;
  346.  
  347.    pfp = 0;
  348.    ilevel = 0;
  349.  
  350. /*
  351.  * We have already loaded the
  352.  * icode and initialized things, so it's time to just push main(),
  353.  * build an Icon list for the rest of the arguments, and called
  354.  * interp on a "invoke 1" bytecode.
  355.  */
  356.    /*
  357.     * The first global variable holds the value of "main".  If it
  358.     *  is not of type procedure, this is noted as run-time error 117.
  359.     *  Otherwise, this value is pushed on the stack.
  360.     */
  361.    if (globals[0].dword != D_Proc)
  362.       fatalerr(117, NULL);
  363.    PushDesc(globals[0]);
  364.    PushNull;
  365.    glbl_argp = (dptr)(sp - 1);
  366.  
  367.    /*
  368.     * If main() has a parameter, it is to be invoked with one argument, a list
  369.     *  of the command line arguments.  The command line arguments are pushed
  370.     *  on the stack as a series of descriptors and Ollist is called to create
  371.     *  the list.  The null descriptor first pushed serves as Arg0 for
  372.     *  Ollist and receives the result of the computation.
  373.     */
  374.    if (((struct b_proc *)BlkLoc(globals[0]))->nparam > 0) {
  375.       for (i = 2; i < argc; i++) {
  376.          char *tmp;
  377.          slen = strlen(argv[i]);
  378.          PushVal(slen);
  379.          Protect(tmp=alcstr(argv[i],(word)slen), fatalerr(0,NULL));
  380.          PushAVal(tmp);
  381.          }
  382.  
  383.       Ollist(argc - 2, glbl_argp);
  384.       }
  385.  
  386.  
  387.    sp = (word *)glbl_argp + 1;
  388.    glbl_argp = 0;
  389.  
  390.    set_up = 1;            /* post fact that iconx is initialized */
  391.  
  392.    /*
  393.     * Start things rolling by calling interp.  This call to interp
  394.     *  returns only if an Op_Quit is executed.    If this happens,
  395.     *  c_exit() is called to wrap things up.
  396.     */
  397.    interp(0,(dptr)NULL);
  398.    c_exit(EXIT_SUCCESS);
  399.    return 0;
  400. }
  401.  
  402. /*
  403.  * icon_setup - handle interpreter command line options.
  404.  */
  405. void icon_setup(argc,argv,ip)
  406. int argc;
  407. char **argv;
  408. int *ip;
  409.    {
  410.  
  411.    #ifdef TallyOpt
  412.       extern int tallyopt;
  413.    #endif                /* TallyOpt */
  414.  
  415.    *ip = 0;            /* number of arguments processed */
  416.  
  417.    #ifdef ExecImages
  418.       if (dumped) {
  419.          /*
  420.           * This is a restart of a dumped interpreter.  Normally, argv[0] is
  421.           *  iconx, argv[1] is the icode file, and argv[2:(argc-1)] are the
  422.           *  arguments to pass as a list to main().  For a dumped interpreter
  423.           *  however, argv[0] is the executable binary, and the first argument
  424.           *  for main() is argv[1].  The simplest way to handle this is to
  425.           *  back up argv to point at argv[-1] and increment argc, giving the
  426.           *  illusion of an additional argument at the head of the list.  Note
  427.           *  that this argument is never referenced.
  428.           */
  429.          argv--;
  430.          argc++;
  431.          (*ip)--;
  432.          }
  433.    #endif                /* ExecImages */
  434.  
  435.    #if NT
  436.       /*
  437.        * if we didn't start with nticonx.exe or wiconx.exe, backup one
  438.        * so that our icode filename is argv[1].
  439.        */
  440.       {
  441.       char tmp[256], *t2;
  442.       int len = 0;
  443.       strcpy(tmp, argv[0]);
  444.       t2 = tmp;
  445.       while (*t2) {
  446.          *t2 = tolower(*t2);
  447.          t2++;
  448.          len++;
  449.          }
  450.  
  451.       /*
  452.        * if argv[0] is not a reference to our interpreter, take it as the
  453.        * name of the icode file, and back up for it.
  454.        */
  455.       #ifdef MSWindows
  456.          if (!((len == 6 && !strcmp(tmp+len-6, "wiconx")) ||
  457.                (len > 6 && !strcmp(tmp+len-7, "\\wiconx")) ||
  458.                (len == 10 && !strcmp(tmp+len-10, "wiconx.exe")) ||
  459.                (len > 10 && !strcmp(tmp+len-11, "\\wiconx.exe")))) {
  460.       #else                /* MSWindows */
  461.          if (!((len == 7 && !strcmp(tmp+len-7, "nticonx")) ||
  462.                (len > 7 && !strcmp(tmp+len-8, "\\nticonx")) ||
  463.                (len == 11 && !strcmp(tmp+len-11, "nticonx.exe")) ||
  464.                (len > 11 && !strcmp(tmp+len-12, "\\nticonx.exe")))) {
  465.       #endif                /* MSWindows */
  466.          argv--;
  467.          argc++;
  468.          (*ip)--;
  469.          }
  470.       }
  471.    #endif                /* NT */
  472.  
  473.    /*
  474.     * Handle command line options.
  475.     */
  476.    while ( argv[1] != 0 && *argv[1] == '-' ) {
  477.       switch ( *(argv[1]+1) ) {
  478.  
  479.       #ifdef TallyOpt
  480.       /*
  481.        * Set tallying flag if -T option given
  482.        */
  483.       case 'T':
  484.          tallyopt = 1;
  485.          break;
  486.       #endif                /* TallyOpt */
  487.  
  488.       /*
  489.        * Announce version on stderr if -V is given.
  490.        */
  491.       case 'V':
  492.          fprintf(stderr, "%s  (%s, %s)\n", Version, Config, __DATE__);
  493.      if (!argv[2])
  494.         exit(0);
  495.          break;
  496.  
  497.       /*
  498.        * Set stderr to new file if -e option is given.
  499.        */
  500.      case 'e': {
  501.         char *p;
  502.         if ( *(argv[1]+2) != '\0' )
  503.            p = argv[1]+2;
  504.         else {
  505.            argv++;
  506.            argc--;
  507.                (*ip)++;
  508.            p = argv[1];
  509.            if ( !p )
  510.           error(NULL, "no file name given for redirection of &errout");
  511.            }
  512.             if (!redirerr(p))
  513.                syserr("Unable to redirect &errout\n");
  514.         break;
  515.         }
  516.         }
  517.     argc--;
  518.         (*ip)++;
  519.     argv++;
  520.       }
  521.    }
  522.  
  523. /*
  524.  * resolve - perform various fix-ups on the data read from the icode
  525.  *  file.
  526.  */
  527. #ifdef MultiThread
  528.    void resolve(pstate)
  529.    struct progstate *pstate;
  530. #else                    /* MultiThread */
  531.    void resolve()
  532. #endif                    /* MultiThread */
  533.  
  534.    {
  535.    register word i, j;
  536.    register struct b_proc *pp;
  537.    register dptr dp;
  538.    extern Omkrec();
  539.    #ifdef MultiThread
  540.       register struct progstate *savedstate;
  541.    #endif                /* MultiThread */
  542.  
  543.    #ifdef MultiThread
  544.       savedstate = curpstate;
  545.       if (pstate) curpstate = pstate;
  546.    #endif                /* MultiThread */
  547.  
  548.    /*
  549.     * Relocate the names of the global variables.
  550.     */
  551.    for (dp = gnames; dp < egnames; dp++)
  552.       StrLoc(*dp) = strcons + (uword)StrLoc(*dp);
  553.  
  554.    /*
  555.     * Scan the global variable array for procedures and fill in appropriate
  556.     *  addresses.
  557.     */
  558.    for (j = 0; j < n_globals; j++) {
  559.  
  560.       if (globals[j].dword != D_Proc)
  561.          continue;
  562.  
  563.       /*
  564.        * The second word of the descriptor for procedure variables tells
  565.        *  where the procedure is.  Negative values are used for built-in
  566.        *  procedures and positive values are used for Icon procedures.
  567.        */
  568.       i = IntVal(globals[j]);
  569.  
  570.       if (i < 0) {
  571.          /*
  572.           * globals[j] points to a built-in function; call (bi_)strprc
  573.       *  to look it up by name in the interpreter's table of built-in
  574.       *  functions.
  575.           */
  576.      if((BlkLoc(globals[j])= (union block *)bi_strprc(gnames+j,0)) == NULL)
  577.             globals[j] = nulldesc;        /* undefined, set to &null */
  578.          }
  579.       else {
  580.  
  581.          /*
  582.           * globals[j] points to an Icon procedure or a record; i is an offset
  583.           *  to location of the procedure block in the code section.  Point
  584.           *  pp at the block and replace BlkLoc(globals[j]).
  585.           */
  586.          pp = (struct b_proc *)(code + i);
  587.          BlkLoc(globals[j]) = (union block *)pp;
  588.  
  589.          /*
  590.           * Relocate the address of the name of the procedure.
  591.           */
  592.          StrLoc(pp->pname) = strcons + (uword)StrLoc(pp->pname);
  593.  
  594.          if (pp->ndynam == -2) {
  595.             /*
  596.              * This procedure is a record constructor.    Make its entry point
  597.              *    be the entry point of Omkrec().
  598.              */
  599.             pp->entryp.ccode = Omkrec;
  600.  
  601.         /*
  602.          * Initialize field names
  603.          */
  604.             for (i = 0; i < pp->nfields; i++)
  605.                StrLoc(pp->lnames[i]) = strcons + (uword)StrLoc(pp->lnames[i]);
  606.  
  607.         }
  608.          else {
  609.             /*
  610.              * This is an Icon procedure.  Relocate the entry point and
  611.              *    the names of the parameters, locals, and static variables.
  612.              */
  613.             pp->entryp.icode = code + pp->entryp.ioff;
  614.             for (i = 0; i < abs((int)pp->nparam)+pp->ndynam+pp->nstatic; i++)
  615.                StrLoc(pp->lnames[i]) = strcons + (uword)StrLoc(pp->lnames[i]);
  616.             }
  617.          }
  618.       }
  619.  
  620.    /*
  621.     * Relocate the names of the fields.
  622.     */
  623.  
  624.    for (dp = fnames; dp < efnames; dp++)
  625.       StrLoc(*dp) = strcons + (uword)StrLoc(*dp);
  626.  
  627.    #ifdef MultiThread
  628.       curpstate = savedstate;
  629.    #endif                /* MultiThread */
  630.    }
  631.  
  632.  
  633. /*
  634.  * Free malloc-ed memory; the main regions then co-expressions.  Note:
  635.  *  this is only correct if all allocation is done by routines that are
  636.  *  compatible with free() -- which may not be the case if Allocreg()
  637.  *  in rmemfix.c is defined to be other than malloc().
  638.  */
  639.  
  640. void xmfree()
  641.    {
  642.    register struct b_coexpr **ep, *xep;
  643.    register struct astkblk *abp, *xabp;
  644.  
  645.    if (mainhead != (struct b_coexpr *)NULL)
  646.       free((pointer)mainhead->es_actstk);    /* activation block for &main */
  647.    free((pointer)code);            /* icode */
  648.    code = NULL;
  649.    free((pointer)stack);        /* interpreter stack */
  650.    stack = NULL;
  651.    /*
  652.     * more is needed to free chains of heaps, also a multithread version
  653.     * of this function may be needed someday.
  654.     */
  655.    if (strbase)
  656.       free((pointer)strbase);        /* allocated string region */
  657.    strbase = NULL;
  658.    if (blkbase)
  659.       free((pointer)blkbase);        /* allocated block region */
  660.    blkbase = NULL;
  661.    if (curstring != &rootstring)
  662.       free((pointer)curstring);        /* string region */
  663.    curstring = NULL;
  664.    if (curblock != &rootblock)
  665.       free((pointer)curblock);        /* allocated block region */
  666.    curblock = NULL;
  667.    if (quallist)
  668.       free((pointer)quallist);        /* qualifier list */
  669.    quallist = NULL;
  670.  
  671.    /*
  672.     * The co-expression blocks are linked together through their
  673.     *  nextstk fields, with stklist pointing to the head of the list.
  674.     *  The list is traversed and each stack is freeing.
  675.     */
  676.    ep = &stklist;
  677.    while (*ep != NULL) {
  678.       xep = *ep;
  679.       *ep = (*ep)->nextstk;
  680.        /*
  681.         * Free the astkblks.  There should always be one and it seems that
  682.         *  it's not possible to have more than one, but nonetheless, the
  683.         *  code provides for more than one.
  684.         */
  685.       for (abp = xep->es_actstk; abp; ) {
  686.          xabp = abp;
  687.          abp = abp->astk_nxt;
  688.          free((pointer)xabp);
  689.          }
  690.       free((pointer)xep);
  691.       stklist = NULL;
  692.       }
  693.  
  694.    }
  695. #endif                    /* !COMPILER */
  696.