home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume9 / teco / part03 / te_exec2.c < prev   
Encoding:
C/C++ Source or Header  |  1987-03-11  |  28.6 KB  |  906 lines

  1. /* TECO for Ultrix   Copyright 1986 Matt Fichtenbaum                        */
  2. /* This program and its components belong to GenRad Inc, Concord MA 01742    */
  3. /* They may be copied if this copyright notice is included                    */
  4.  
  5. /* te_exec2.c   process "E" and "F" commands   2/26/87 */
  6. #include "te_defs.h"
  7. #include <sys/wait.h>
  8.  
  9. struct qh oldcstring;                        /* hold command string during ei */
  10.  
  11. /* file stuff for input/output files */
  12.  
  13. struct infiledata pi_file = { NULL, -1 };    /* input files */
  14. struct infiledata si_file = { NULL, -1 };
  15. struct infiledata *infile = &pi_file;        /* pointer to currently active input file structure */
  16. struct outfiledata po_file, so_file;        /* output files */
  17. struct outfiledata *outfile = &po_file;        /* pointer to currently active output file structure */
  18. FILE *eisw;                                    /* indirect command file pointer */
  19.  
  20. /* process E commands */
  21.  
  22. do_e()
  23.     {
  24.     char c;                            /* temps */
  25.     int old_var;
  26.     FILE *t_eisw;
  27.  
  28.     switch (mapch_l[getcmdc(trace_sw)])        /* read next character and dispatch */
  29.         {    
  30.  
  31. /* numeric values */
  32.  
  33.         case 'd':                /* ED */
  34.             set_var(&ed_val);
  35.             break;
  36.  
  37.         case 's':                /* ES */
  38.             set_var(&es_val);
  39.             break;
  40.  
  41.         case 't':                /* ET */
  42.             old_var = et_val;
  43.             set_var(&et_val);
  44.             et_val = (et_val & 0100651) | 001006;    /* force read_only bits */
  45.             if ((et_val ^ old_var) & ET_TRUNC) window(WIN_REDRAW);        /* redraw if ET & 256 changes */
  46.             break;
  47.  
  48.         case 'u':                /* EU */
  49.             set_var(&eu_val);
  50.             break;
  51.  
  52.         case 'v':                /* EV */
  53.             set_var(&ev_val);
  54.             break;
  55.  
  56.         case 'z':                /* EZ */
  57.             old_var = ez_val;
  58.             set_var(&ez_val);
  59.             tabmask = (ez_val & EZ_TAB4) ? 3 : 7;        /* set tab mask */
  60.             if ((ez_val ^ old_var) & EZ_TAB4) window(WIN_REDRAW);        /* force window redisplay if EZ_TAB4 changes */
  61.             break;
  62. /* E_ search */
  63.  
  64.         case '_':
  65.             do_nsearch('e');
  66.             break;
  67.  
  68. /* file I/O commands */
  69.  
  70.         case 'a':                /* set secondary output */
  71.             outfile = &so_file;
  72.             break;
  73.  
  74.         case 'b':                /* open read/write with backup */
  75.             if (!read_filename(1, 'r')) ERROR(E_NFI);    /* read the name */
  76.             if (infile->fd) fclose(infile->fd);            /* close previously-open file */
  77.             if (!(infile->fd = fopen(fbuf.f->ch, "r")))
  78.                 {
  79.                 if (!colonflag) ERROR(E_FNF);
  80.                 }
  81.             else
  82.                 {
  83.                 if (outfile->fd) ERROR(E_OFO);        /* output file already open */
  84.                 for (ll = 0; ll < CELLSIZE; ll++)        /* save file string */
  85.                     if ((outfile->t_name[ll] = outfile->f_name[ll] = fbuf.f->ch[ll]) == '\0') break;
  86.                 outfile->name_size = ll;
  87.                 outfile->t_name[ll++] = '.';
  88.                 outfile->t_name[ll++] = 't';
  89.                 outfile->t_name[ll++] = 'm';
  90.                 outfile->t_name[ll++] = 'p';
  91.                 outfile->t_name[ll] = '\0';
  92.                 if (!(outfile->fd = fopen(outfile->t_name, "w"))) ERROR(E_COF);
  93.                 outfile->bak = 1;    /* set backup mode */
  94.                 }
  95.             infile->eofsw = -1 - (esp->val1 = (infile->fd) ? -1 : 0);
  96.             esp->flag1 = colonflag;
  97.             colonflag = 0;
  98.             break;
  99.  
  100.         case 'x':                /* finish edit and exit */
  101.             exitflag = -1;
  102.  
  103.             /* --- fall through to "EC" --- */
  104.  
  105.         case 'c':                /* finish edit */
  106.             set_pointer(0, &aa);                /* set a pointer to start of text buffer */
  107.             write_file(&aa, z, ctrl_e);            /* write the current buffer */
  108.             dot = z = 0;                        /* empty the buffer */
  109.             window(WIN_REDRAW);                    /* force window redraw */
  110.             if ((outfile->fd) && (infile->fd) && !(infile->eofsw))    /* if any input remaining, copy it to output */
  111.                 while ((c = getc(infile->fd)) != EOF) putc(c, outfile->fd);
  112.  
  113.             /* --- fall through to "EF" --- */
  114.         case 'f':                /* close output file */
  115.             if (outfile->fd)    /* only if one is open */
  116.                 {
  117.                 fclose(outfile->fd);
  118.                 if (outfile->bak & 1)        /* if this is "backup" mode */
  119.                     {
  120.                     outfile->f_name[outfile->name_size] = '.';
  121.                     outfile->f_name[outfile->name_size+1] = 'b';
  122.                     outfile->f_name[outfile->name_size+2] = 'a';
  123.                     outfile->f_name[outfile->name_size+3] = 'k';
  124.                     outfile->f_name[outfile->name_size+4] = '\0';
  125.                     outfile->t_name[outfile->name_size] = '\0';
  126.                     rename(outfile->t_name, outfile->f_name);    /* rename orig file */
  127.                     }
  128.  
  129.                 if (!(outfile->bak & 8))        /* if output file had ".tmp" extension */
  130.                     {                                /* remove it */
  131.                     outfile->t_name[outfile->name_size] = '.';
  132.                     outfile->f_name[outfile->name_size] = '\0';
  133.                     rename(outfile->t_name, outfile->f_name);    /* rename output */
  134.                     }
  135.                 }
  136.             outfile->fd = NULL;            /* mark "no output file open" */
  137.             break;
  138.  
  139.         case 'i':                /* indirect file execution */
  140.             if (!read_filename(1, 'i'))        /* if no filename specified, reset command input */
  141.                 {
  142.                 if (eisw)        /* if ending a file execute, restore the previous "old command string" */
  143.                     {
  144.                     fclose(eisw);                /* return the file descriptor */
  145.                     dly_free_blist(cbuf.f);        /* return the command string used by the file (after execution done) */
  146.                     cbuf.f = oldcstring.f;
  147.                     cbuf.z = oldcstring.z;
  148.                     }
  149.                 t_eisw = 0;
  150.                 }
  151.             else if (!(t_eisw = fopen(fbuf.f->ch, "r")))
  152.                 {
  153.                 if (!colonflag) ERROR(E_FNF);
  154.                 }
  155.             else if (!eisw)            /* if this "ei" came from the command string */
  156.                 {
  157.                 oldcstring.f = cbuf.f;        /* save current command string */
  158.                 oldcstring.z = cbuf.z;
  159.                 cbuf.f = NULL;                /* and make it inaccessible to "rdcmd" */
  160.                 }
  161.             if (eisw) fclose(eisw);            /* if a command file had been open, close it */
  162.             esp->val1 = (eisw = t_eisw) ? -1 : 0;
  163.             esp->flag1 = colonflag;
  164.             colonflag = 0;
  165.             esp->op = OP_START;
  166.             break;
  167.         case 'k':                /* kill output file */
  168.             kill_output(outfile);
  169.             break;
  170.  
  171.         case 'p':                /* switch to secondary input */
  172.             infile = &si_file;
  173.             break;
  174.  
  175.         case 'r':                /* specify input file, or switch to primary input */
  176.             if (!read_filename(0, 'r')) infile = &pi_file;        /* no name, switch to primary input */
  177.             else
  178.                 {
  179.                 if (infile->fd) fclose(infile->fd);                /* close previously-open file */
  180.                 if (!(infile->fd = fopen(fbuf.f->ch, "r")))
  181.                     {
  182.                     if (!colonflag) ERROR(E_FNF);
  183.                     }
  184.                 }
  185.             infile->eofsw = -1 - (esp->val1 = (infile->fd) ? -1 : 0);
  186.             esp->flag1 = colonflag;
  187.             colonflag = 0;
  188.             esp->op = OP_START;
  189.             break;
  190.  
  191.         case 'w':                /* specify output file, or switch to primary output */
  192.             if(!read_filename(0, 'w')) outfile = &po_file;
  193.             else
  194.                 {
  195.                 if (outfile->fd) ERROR(E_OFO);        /* output file already open */
  196.                 for (ll = 0; ll < CELLSIZE; ll++)            /* save file string */
  197.                     if ((outfile->t_name[ll] = outfile->f_name[ll] = fbuf.f->ch[ll]) == '\0') break;
  198.                 outfile->name_size = ll;
  199.                 if (!(ez_val & EZ_NOTMPFIL))            /* if not using literal output name */
  200.                     {
  201.                     outfile->t_name[ll++] = '.';        /* use provisional suffix ".tmp" */
  202.                     outfile->t_name[ll++] = 't';
  203.                     outfile->t_name[ll++] = 'm';
  204.                     outfile->t_name[ll++] = 'p';
  205.                     outfile->t_name[ll] = '\0';
  206.                     }
  207.                 if (!(outfile->fd = fopen(outfile->t_name, "w"))) ERROR(E_COF);
  208.                 outfile->bak = ez_val & EZ_NOTMPFIL;            /* save "temp suffix" status */
  209.                 }
  210.             break;
  211.  
  212.         case 'y':                /* EY is Y without protection */
  213.             if (esp->flag1) ERROR(E_NYA);
  214.             dot = z = 0;            /* clear buffer */
  215.             set_pointer(0, &aa);
  216.             read_file(&aa, &z, (ed_val & ED_EXPMEM ? -1 : 0) );
  217.             esp->flag1 = colonflag;
  218.             colonflag = 0;
  219.             esp->op = OP_START;
  220.             break;
  221.         case 'n':                /* wildcard filespec */
  222.             esp->val1 = do_en();
  223.             esp->flag1 = colonflag;
  224.             colonflag = 0;
  225.             esp->op = OP_START;
  226.             break;
  227.  
  228.         case 'q':                /* system command */
  229.             esp->val1 = do_eq();            /* do this as a separate routine */
  230.             esp->flag1 = colonflag;
  231.             colonflag = 0;
  232.             esp->op = OP_START;
  233.             break;
  234.  
  235.         default:
  236.             ERROR(E_IEC);
  237.         }
  238.     }
  239. /* routine to execute a system command            */
  240. /* this is done by forking off another process    */
  241. /* to execute a shell via 'execl'                */
  242. /* routine returns -1 if success, 0 if error in fork */
  243.  
  244. int do_eq()
  245.     {
  246.     int t;
  247.     union wait status;
  248.     char *pname;                /* pointer to name of shell */
  249.     extern char *getenv();
  250.  
  251.     build_string(&sysbuf);
  252.     if (sysbuf.z > CELLSIZE-2) ERROR(E_STL);    /* command must fit within one cell */
  253.     sysbuf.f->ch[sysbuf.z] = '\0';                /* store terminating null */
  254.     if (!(pname = getenv("SHELL"))) ERROR(E_SYS);    /* read shell name */
  255.  
  256.     if (!esp->flag1)            /* if not m,nEQ command */
  257.         {
  258.         if (win_data[7]) window(WIN_SUSP);            /* restore full screen */
  259.         crlf();                                        /* force characters out */
  260.         setup_tty(TTY_SUSP);                        /* restore terminal to normal mode */
  261.  
  262.         t = vfork();                            /* fork a new process */
  263.         if (t == 0)                                /* if this is the child */
  264.             {
  265.             execl(pname, pname, "-c", &sysbuf.f->ch[0], 0);        /* call the named Unix routine */
  266.             printf("Error in 'execl'\n");        /* normally shouldn't get here */
  267.             exit(1);
  268.             }
  269.  
  270.         while (wait(&status) != t);                /* if parent, wait for child to finish */
  271.         if (status.w_retcode) t = -1;            /* keep failure indication from child */
  272.         
  273.         setup_tty(TTY_RESUME);                        /* restore terminal to teco mode */
  274.         if (win_data[7])                    /* if window was enabled */
  275.             {
  276.             vt(VT_SETSPEC1);                /* set reverse video */
  277.             fputs("Type RETURN to continue", stdout);        /* require CR before continuing */
  278.             vt(VT_CLRSPEC);                    /* reverse video off */
  279.             while (gettty() != LF);
  280.             putchar(CR);                    /* back to beginning of line */
  281.             vt(VT_EEOL);                    /* and erase the message */
  282.             window(WIN_RESUME);                /* re-enable window */
  283.             window(WIN_REDRAW);                /* and make it redraw afterwards */
  284.             }
  285.         }
  286.  
  287.     else t = do_eq1(pname);                    /* m,nEQ command */
  288.  
  289.     return( (t == -1) ? 0 : -1);            /* return failure if fork failed or proc status nonzero */
  290.     }
  291. /* Execute m,nEQtext$ command.  Run "text" as a Unix command that    */
  292. /* receives its std input from chars m through n of teco's buffer.    */
  293. /* Output from the command is placed in Q#.                            */
  294.  
  295. int do_eq1(shell)
  296.     char *shell;            /* arg is pointer to shell name */
  297.     {
  298.     int ff, pipe_in[2], pipe_out[2];    /* fork result and two pipes */
  299.     FILE *xx_in, *xx_out;                /* std in and out for called process */
  300.     FILE *fdopen();
  301.     union wait status;
  302.  
  303.     ll = line_args(1, &aa);        /* set aa to start of text, ll to number of chars */
  304.     dot += ll;                    /* set pointer at end of text */
  305.     ctrl_s = -ll;                /* set ^S to - # of chars */
  306.  
  307.     if (pipe(pipe_out)) ERROR(E_SYS);    /* make input pipe; failure if can't */
  308.  
  309.     if (win_data[7]) window(WIN_SUSP);    /* disable split screen */
  310.     setup_tty(TTY_SUSP);                /* put console back to original state */
  311.     if ((ff = fork()) == -1)            /* fork first child: if error, quit */
  312.         {
  313.         close(pipe_out[0]);
  314.         close(pipe_out[1]);
  315.         setup_tty(TTY_RESUME);
  316.         if (win_data[7]) window(WIN_RESUME), window(WIN_REDRAW);
  317.         ERROR(E_SYS);
  318.         }
  319.  
  320.     if (ff)                            /* if this is the parent, the task is to read into q# */
  321.         {
  322.         make_buffer(&timbuf);        /* initialize the q# header */
  323.         bb.p = timbuf.f;            /* init bb to point to q# */
  324.         timbuf.z =     bb.c = 0;
  325.  
  326.         close(pipe_out[1]);            /* parent won't write to this pipe */
  327.  
  328.         if ((xx_out = fdopen(pipe_out[0], "r")) == 0)    /* open the "std out" pipe for reading */
  329.             {
  330.             close(pipe_out[0]);        /* if can't open output pipe */
  331.             setup_tty(TTY_RESUME);
  332.             if (win_data[7]) window(WIN_RESUME), window(WIN_REDRAW);
  333.             ERROR(E_SYS);            /* "open" failure */
  334.             }
  335.         read_stream(xx_out, 0, &bb, &timbuf.z, 0, 0, 1);        /* read from pipe to q# */
  336.         close(pipe_out[0]);
  337.  
  338.         while (wait(&status) != ff);        /* wait for children to finish */
  339.         setup_tty(TTY_RESUME);
  340.         if (win_data[7]) window(WIN_RESUME), window(WIN_REDRAW);
  341.         return(status.w_retcode ? -1 : 0);
  342.         }
  343. /* This is the child.  It in turn forks into two processes, of which the "parent"    */
  344. /* (original child) writes the specified part of the buffer to the pipe, and the    */
  345. /* new child (grandchild to the original teco) execl's the Unix command.            */
  346.  
  347.     else                            /* this is the child */
  348.         {
  349.         close(pipe_out[0]);                /* won't read from "output" pipe */
  350.         if (pipe(pipe_in)) exit(1);        /* make the "std in" pipe for the process, quit if can't */
  351.  
  352.         if ((ff = fork()) == -1) exit(1);    /* fork to two processes (total 3), exit if error */
  353.  
  354.         if (ff)                            /* parent - will send chars */
  355.             {
  356.             close(pipe_in[0]);            /* won't read from this pipe */
  357.  
  358.         /* open pipe for writing; exit if open fails */
  359.  
  360.             if ((xx_in = fdopen(pipe_in[1], "w")) == 0) exit(1);
  361.  
  362.             write_stream(xx_in, &aa, ll, 0);        /* write to stream, CRLF becomes LF */
  363.             fclose(xx_in);
  364.  
  365.             while (wait(&status) != ff);    /* wait for child */
  366.             exit(status.w_retcode);            /* exit with child's status */
  367.             }
  368.  
  369.         else                            /* this process is the grandchild */
  370.             {
  371.             close(pipe_in[1]);            /* close "input" for writing */
  372.             dup2(pipe_in[0], fileno(stdin));        /* substitute pipe_in for stdin */
  373.             dup2(pipe_out[1], fileno(stdout));        /* and pipe_out for stdout    */
  374.             close(pipe_in[0]);            /* close original descriptors */
  375.             close(pipe_out[1]);
  376.  
  377.             execl(shell, shell, "-c", &sysbuf.f->ch[0], 0);        /* execute specified routine */
  378.             fputs("execl failed\n", stderr);
  379.             exit(1);
  380.             }
  381.         }
  382.     }
  383. /* Routines to handle EN wild-card file specification    */
  384. /* ENfilespec$ defines file class, leaving 'filespec'    */
  385. /* in filespec buffer and reading expanded list of        */
  386. /* files into local buffer.  EN$ gets next filespec        */
  387. /* into filespec buffer.                                */
  388.  
  389. struct qh en_buf;                /* header for storage for file list */
  390. struct qp en_ptr;                /* pointer to load/read file list    */
  391. static char glob_cmd0[] = { 'g', 'l', 'o', 'b', ' ' } ;
  392.  
  393. int do_en()
  394.     {
  395.     int t;
  396.  
  397.     if (build_string(&fbuf))                    /* if a file string is specified */
  398.         {
  399.         if (fbuf.z > CELLSIZE-2) ERROR(E_STL);        /* upper limit on string length */
  400.         fbuf.f->ch[fbuf.z] = '\0';                /* terminating null */
  401.         t = do_glob(&en_buf);                    /* glob the result */
  402.         en_ptr.p = en_buf.f;                    /* set the buffer pointer to the beginning of the buffer */
  403.         en_ptr.dot = en_ptr.c = 0;
  404.         }
  405.     else                                        /* if no string, get next filename */
  406.         {
  407.         do_en_next();
  408.         t = (fbuf.z) ? -1 : 0;                    /* t zero if no more filespecs */
  409.         if (!t && !colonflag) ERROR(E_NFI);        /* if no colon, end of spec is an error */
  410.         }
  411.     return (t);
  412.     }
  413.  
  414. /* routine to expand the string in the filespec buffer */
  415. /* argument is the address of a qh that gets the expanded string */
  416. /* argument->v gets set to the number of file specs found */
  417.  
  418. int do_glob(buff)
  419.     struct qh *buff;
  420.     {
  421.     char glob_cmd[CELLSIZE+5];            /* "glob filespec" command string */
  422.     int t;
  423.     char c;
  424.     int glob_pipe[2];                    /* pipe to forked shell for expanding filenames */
  425.     struct qp glob_ptr;                    /* pointer for loading result buffer */
  426.     FILE *xx_out;                        /* stream for reading chars from pipe */
  427.     FILE *fdopen();
  428.     union wait status;
  429.  
  430.     make_buffer(buff);                    /* initialize expanded file buffer */
  431.     glob_ptr.p = buff->f;                /* initialize pointer to buffer */
  432.     glob_ptr.c = glob_ptr.dot = buff->z = buff->v = 0;
  433.     for (t = 0; t < 5; t++) glob_cmd[t] = glob_cmd0[t];        /* set up "glob filespec" command */
  434.     for (t = 0; t < fbuf.z +1; t++) glob_cmd[t+5] = fbuf.f->ch[t];
  435.  
  436.     if (pipe(glob_pipe)) ERROR(E_SYS);        /* make a pipe */
  437.     setup_tty(TTY_SUSP);                    /* put console back to normal */
  438.     if ((t = fork()) == -1)                    /* spawn a child... if failure */
  439.         {
  440.         close(glob_pipe[0]);                /* undo the pipe */
  441.         close(glob_pipe[1]);
  442.         setup_tty(TTY_RESUME);
  443.         ERROR(E_SYS);                        /* and exit with failure */
  444.         }
  445.  
  446.     if (t)                                    /* if this is the parent */
  447.         {
  448.         close(glob_pipe[1]);                /* parent won't write */
  449.         if ((xx_out = fdopen(glob_pipe[0], "r")) == 0)    /* open pipe for read */
  450.             {
  451.             close(glob_pipe[0]);                        /* failure to open */
  452.             setup_tty(TTY_RESUME);
  453.             ERROR(E_SYS);
  454.             }
  455.  
  456.         while ((c = getc(xx_out)) != EOF)        /* read characters from pipe */
  457.             {
  458.             if (c == '\0') ++buff->v;            /* count null chars that separate file specs */
  459.             glob_ptr.p->ch[glob_ptr.c] = c;        /* store them in buffer */
  460.             fwdcx(&glob_ptr);
  461.             }
  462.  
  463.         fclose(xx_out);                            /* through with stream */
  464.         buff->z = glob_ptr.dot;                    /* save character count */
  465.         while (wait(&status) != t);                /* wait for child to finish */
  466.         setup_tty(TTY_RESUME);
  467.         return(status.w_retcode ? 0 : -1);        /* return success unless child exited nonzero */
  468.         }
  469.     else                                        /* this is the child */
  470.         {
  471.         close(glob_pipe[0]);                    /* child won't read */
  472.         dup2(glob_pipe[1], fileno(stdout));        /* substitute pipe for standard out */
  473.         close(glob_pipe[1]);                    /* don't need that anymore */
  474.         execl("/bin/csh", "csh", "-fc", glob_cmd, 0);        /* execute the "glob" */
  475.         fputs("execl failed\n", stderr);
  476.         exit(1);
  477.         }
  478.     }
  479.  
  480. /* routine to get next file spec from "EN" list into filespec buffer */
  481.  
  482. do_en_next()
  483.     {
  484.     char c;
  485.  
  486.     make_buffer(&fbuf);                            /* initialize the filespec buffer */
  487.     fbuf.z = 0;
  488.  
  489.     while(en_ptr.dot < en_buf.z)                /* stop at end of string */
  490.         {
  491.         c = en_ptr.p->ch[en_ptr.c];
  492.         fwdc(&en_ptr);
  493.         if (!c) break;                            /* null is terminator between file specs */
  494.         fbuf.f->ch[fbuf.z++] = c;                /* store char */
  495.         if (fbuf.z >= CELLSIZE-1) ERROR(E_STL);        /* limit on filespec size */
  496.         }
  497.  
  498.     fbuf.f->ch[fbuf.z] = '\0';                    /* filespec ends with NULL */
  499.     }
  500.  
  501. /* routine to read a file name                */
  502. /* reads it into fbuf text area                */
  503. /* returns nonzero if a name, 0 if none        */
  504. /* flag nonzero => empty name clears filespec buffer */
  505. /* func is 'r' for ER or EB cmds, 'i' for EI, 'w' for EW */
  506.  
  507. int read_filename(flag, func)
  508.     int flag;
  509.     char func;
  510.     {
  511.     int i, t, expand;
  512.     char c;
  513.     struct qh temp_buff;                        /* temp buffer for globbing filespec */
  514.  
  515.     if (!(t = build_string(&fbuf)))                /* if no name spec'd */
  516.         {
  517.         if (flag) fbuf.z = 0;                    /* if no name spec'd, set length to 0 */
  518.         }
  519.     else
  520.         {
  521.         if (fbuf.z > CELLSIZE-2) ERROR(E_STL);
  522.         fbuf.f->ch[fbuf.z] = '\0';                /* store terminating NULL */
  523.  
  524. /* check for characters to be expanded by the shell */
  525.  
  526.         for (i = 0; i < fbuf.z; i++)
  527.             if ((c = fbuf.f->ch[i]) == '*' || c == '?' || c == '[' || c == 0173) break;
  528.         if ( (expand = (i < fbuf.z)) || fbuf.f->ch[0] == '~')    /* one of these was found, or first char is ~ */
  529.             {
  530.             temp_buff.f = NULL;                    /* make a temp buffer to glob filename into */
  531.             make_buffer(temp_buff);
  532.             do_glob(&temp_buff);                /* expand the file name */
  533.             if (temp_buff.z == 0)                /* no match */
  534.                 {
  535.                 free_blist(temp_buff.f);        /* return the storage */
  536.                 ERROR(func == 'w' ? E_COF : E_FNF);    /* "can't open" or "file not found" */
  537.                 }
  538.             else if (temp_buff.v == 0)            /* if exactly one file name */
  539.                 {
  540.                 free_blist(fbuf.f);                /* return the old file spec */
  541.                 fbuf.f = temp_buff.f;            /* put the temp buffer there instead */
  542.                 fbuf.z = temp_buff.z;
  543.                 if (fbuf.z > CELLSIZE-2) ERROR(E_STL);
  544.                 fbuf.f->ch[fbuf.z] = '\0';
  545.  
  546.                 if (func == 'w' && expand)        /* if this is EW and 'twas from a wildcard expansion */
  547.                     {
  548.                     vt(VT_SETSPEC1);            /* "file XXXX already exists: overwrite? [yn]" */
  549.                     fputs("File ", stdout);
  550.                     fputs(fbuf.f->ch, stdout);
  551.                     fputs(" already exists: overwrite? [ny] ", stdout);
  552.                     vt(VT_CLRSPEC);
  553.                     c = gettty();                /* read user's response */
  554.                     putchar(CR);
  555.                     vt(VT_EEOL);                /* clean up the screen */
  556.                     if (c != 'y') ERROR(E_COF);    /* abort here */
  557.                     }
  558.                 }
  559.  
  560.             else                                /* multiple file specs */
  561.                 {
  562.                 if (func != 'r' || !(ez_val & EZ_MULT))                /* if multiple file specs here aren't allowed */
  563.                     {
  564.                     free_blist(temp_buff.f);            /* return the storage */
  565.                     ERROR(E_AMB);
  566.                     }
  567.                 free_blist(en_buf.f);                    /* substitute the expansion for the "EN" list */
  568.                 en_ptr.p = en_buf.f = temp_buff.f;        /* and initialize the "EN" list pointer */
  569.                 en_buf.z = temp_buff.z;
  570.                 en_ptr.dot = en_ptr.c = 0;
  571.                 do_en_next();                    /* get the first file */
  572.                 }
  573.             }
  574.         }
  575.     return(t);
  576.     }
  577.  
  578.  
  579.  
  580. /* fetch or set variable */
  581.  
  582. set_var(arg)
  583.     int *arg;            /* argument is pointer to variable */
  584.     {
  585.     if (esp->flag1)        /* if an argument, then set the variable */
  586.         {
  587.         if (esp->flag2)                    /* if two arguments, then <clr>, <set> */
  588.             *arg = (*arg & ~esp->val2) | esp->val1;
  589.         else *arg = esp->val1;            /* one arg is new value */
  590.         esp->flag2 = esp->flag1 = 0;    /* consume argument */
  591.         }
  592.     else                /* otherwise fetch the variable's value */
  593.         {
  594.         esp->val1 = *arg;
  595.         esp->flag1 = 1;
  596.         }
  597.     }
  598.  
  599.  
  600.  
  601. /* read from selected input file stream into specified buffer    */
  602. /* terminate on end-of-file or form feed                        */
  603. /* if endsw > 0 terminates after that many lines                */
  604. /* if endsw < 0 stops if z > BUFF_LIMIT                            */
  605. /* returns -1 if read EOF, 0 otherwise                            */
  606.  
  607. int read_file(buff, nchars, endsw)
  608.     struct qp *buff;
  609.     int *nchars, endsw;
  610.     {
  611.     if (!infile->fd) infile->eofsw = -1, ctrl_e = 0;    /* return if no input file open */
  612.     else infile->eofsw = read_stream(infile->fd, &ctrl_e, buff, nchars, endsw, ez_val & EZ_CRLF, ez_val & EZ_READFF);
  613.     return(esp->val1 = infile->eofsw);
  614.     }
  615. /* read from an I/O stream into specified buffer                            */
  616. /* this is used by read_file and by "eq" pipe from other Unix processes        */
  617. /* args buff, nchars, endsw as above; file is stream pointer, ff_found is    */
  618. /* address of a switch to set if read ended with a FF, crlf_sw is lf->crlf    */
  619. /* conversion, ff_sw indicates whether to stop on a form feed.                */
  620.  
  621. int read_stream(file, ff_found, buff, nchars, endsw, crlf_sw, ff_sw)
  622.     FILE *file;
  623.     struct qp *buff;
  624.     int *ff_found, *nchars, endsw, crlf_sw, ff_sw;
  625.     {
  626.     char chr;
  627.     int crflag;
  628.     register struct buffcell *p;
  629.     register int c;
  630.  
  631.     p = (*buff).p;        /* copy pointer locally */
  632.     c = (*buff).c;
  633.     crflag = 0;            /* "last char wasn't CR" */
  634.     while (((chr = getc(file)) != EOF) && ((chr != FF) || ff_sw))
  635.         {
  636.         if ((chr == LF) && !crflag && !crlf_sw)        /* automatic cr before lf */
  637.             {
  638.             p->ch[c] = CR;        /* store a cr */
  639.             ++(*nchars);        /* increment buffer count */
  640.             if (++c > CELLSIZE-1)    /* next cell? */
  641.                 {
  642.                 if (!p->f)            /* need a new cell? */
  643.                     {
  644.                     p->f = get_bcell();
  645.                     p->f->b = p;
  646.                     }
  647.                 p = p->f;
  648.                 c = 0;
  649.                 }
  650.             }
  651.         p->ch[c] = chr;            /* store char */
  652.         ++(*nchars);            /* increment character count */
  653.         if (++c > CELLSIZE-1)    /* next cell? */
  654.             {
  655.             if (!p->f)            /* need a new cell? */
  656.                 {
  657.                 p->f = get_bcell();
  658.                 p->f->b = p;
  659.                 }
  660.             p = p->f;
  661.             c = 0;
  662.             }
  663.         crflag = (chr == CR);    /* flag set if last char was CR */
  664.         if ((chr == LF) && ((endsw < 0 && z > BUFF_LIMIT) || (endsw > 0 && --endsw == 0))) break;    /* term after N lines */
  665.         }
  666.     (*buff).p = p;            /* update pointer */
  667.     (*buff).c = c;
  668.     if (ff_found) *ff_found = (chr == FF) ? -1 : 0;        /* set flag to indicate whether FF found */
  669.     return( (chr == EOF) ? -1 : 0);                    /* and return "eof found" value */
  670.     }
  671. /* routine to write text buffer out to selected output file    */
  672. /* arguments are qp to start of text, number of characters,    */
  673. /* and an "append FF" switch                                */
  674.  
  675. write_file(buff, nchars, ffsw)
  676.     struct qp *buff;
  677.     int nchars, ffsw;
  678.     {
  679.     if (!outfile->fd && (nchars)) ERROR(E_NFO);
  680.     else write_stream(outfile->fd, buff, nchars, ez_val & EZ_CRLF);
  681.     if (outfile->fd && ffsw) putc(FF, outfile->fd);
  682.     }
  683.  
  684.  
  685. /* routine to write text buffer to I/O stream.  Used by    */
  686. /* write_file, above, and by "eq" write to pipe to other    */
  687. /* Unix processes.  Arguments buff, nchars as above; file    */
  688. /* is stream pointer, crlf_sw zero converts CRLF to LF        */
  689.  
  690. write_stream(file, buff, nchars, crlf_sw)
  691.     FILE *file;
  692.     struct qp *buff;
  693.     int nchars, crlf_sw;
  694.     {
  695.     char c;
  696.     int crflag;
  697.  
  698.     crflag = 0;
  699.     for (; nchars > 0; nchars--)
  700.         {
  701.         if ((c = (*buff).p->ch[(*buff).c]) == CR) crflag = 1;    /* set flag if a c.r. */
  702.         else
  703.             {
  704.             if ((crflag) && ((c != LF) || crlf_sw))            /* if c.r. not before lf, or if not in */
  705.                 putc(CR, file);                                /* "no cr" mode, output the c.r. */
  706.             crflag = 0;
  707.             putc(c, file);
  708.             }
  709.         if (++(*buff).c > CELLSIZE-1)
  710.             {
  711.             (*buff).p = (*buff).p->f;
  712.             (*buff).c = 0;
  713.             }
  714.         }
  715.     }
  716.  
  717.  
  718. /* routine to kill output file: argument is pointer to an output file structure */
  719.  
  720. kill_output(outptr)
  721.     struct outfiledata *outptr;
  722.     {
  723.     if (outptr->fd)
  724.         {
  725.         fclose(outptr->fd);
  726.         unlink(outptr->t_name);
  727.         outptr->fd = NULL;
  728.         }
  729.     }
  730. /* "panic" routine called when "hangup" signal occurs */
  731. /* this routine saves the text buffer and closes the output files */
  732.  
  733. char panic_name[] = "TECO_SAVED.tmp";            /* name of file created to save buffer */
  734.  
  735. panic()
  736.     {
  737.     if (!outfile->fd && z) outfile->fd = fopen(panic_name, "w");    /* if buffer nonempty and no file open, make one */
  738.  
  739.     set_pointer(0, &aa);                                /* set a pointer to start of text buffer */
  740.     if (outfile->fd && z) write_file(&aa, z, 0);        /* and write out buffer unless "open" failed */
  741.  
  742.     if (po_file.fd) fclose(po_file.fd), po_file.fd = NULL;        /* close any open output files */
  743.     if (so_file.fd) fclose(so_file.fd), so_file.fd = NULL;
  744.     }
  745. /* do "F" commands */
  746.  
  747. do_f()
  748.     {
  749.     struct buffcell *delete_p;
  750.  
  751.     switch (mapch_l[getcmdc(trace_sw)])         /* read next character and dispatch */
  752.         {
  753.         case '<':            /* back to beginning of current iteration */
  754.             if (cptr.flag & F_ITER)        /* if in iteration */
  755.                 {
  756.                 cptr.p = cptr.il->p;    /* restore */
  757.                 cptr.c = cptr.il->c;
  758.                 cptr.dot = cptr.il->dot;
  759.                 }
  760.             else for (cptr.dot = cptr.c = 0; cptr.p->b->b != NULL; cptr.p = cptr.p->b);    /* else, restart current macro */
  761.             break;
  762.  
  763.         case '>':            /* to end of current iteration */
  764.             find_enditer();    /* find it */
  765.             if ( ( ((esp->flag1) ? esp->val1 : srch_result) >= 0) ? (~colonflag) : colonflag)    /* if exit */
  766.             pop_iteration(0);    /* and exit if appropriate */
  767.             break;
  768.  
  769.         case '\'':                    /* to end of conditional */
  770.         case '|':                    /* to "else," or end */
  771.             find_endcond(cmdc);
  772.             break;
  773.  
  774. /* "F" search commands */
  775.  
  776.         case 'b':                        /* bounded search, alternative args */
  777.             do_fb();
  778.             break;
  779.  
  780.         case 'c':                        /* bounded search, alternative args, then "FR" */
  781.             if (do_fb()) goto do_fr;
  782.             while (getcmdc(trace_sw) != term_char);        /* otherwise skip insert string */
  783.             break;
  784.  
  785.         case 'n':                        /* do "N" and then "FR" */
  786.             if (do_nsearch('n')) goto do_fr;
  787.             while (getcmdc(trace_sw) != term_char);        /* otherwise skip insert string */
  788.             break;
  789.  
  790.         case '_':                        /* do "_" and then "FR" */
  791.             if (do_nsearch('_')) goto do_fr;
  792.             while (getcmdc(trace_sw) != term_char);        /* otherwise skip insert string */
  793.             break;
  794.  
  795.         case 's':                        /* search and replace: search, then do "FR" */
  796.             build_string(&sbuf);        /* read search string and terminator */
  797.             if (end_search(  do_search( setup_search() )  )) goto do_fr;    /* if search passed, do "FR" */
  798.             while (getcmdc(trace_sw) != term_char);        /* otherwise skip insert string */
  799.             break;
  800.         case 'r':                        /* replace last insert, search, etc. */
  801.             if (esp->flag1) ERROR(E_NFR);    /* shouldn't have argument */
  802.             term_char = (atflag) ? getcmdc(trace_sw) : ESC;        /* set terminator */
  803.             atflag = 0;
  804.           do_fr:                    /* entry from FN, F_, and FC */
  805.             set_pointer(dot, &cc);        /* save a pointer to the current spot */
  806.             dot += ctrl_s;                /* back dot up over the string */
  807.             set_pointer(dot, &aa);        /* code from "insert1": convert dot to a qp */
  808.             delete_p = aa.p;            /* save beginning of original cell */
  809.             if (dot < buff_mod) buff_mod = dot;        /* update earliest char loc touched */
  810.             insert_p = bb.p = get_bcell();            /* get a new cell */
  811.             bb.c = 0;
  812.             ins_count = aa.c;        /* save char position of dot in cell */
  813.             aa.c = 0;
  814.  
  815.             movenchars(&aa, &bb, ins_count);    /* copy cell up to dot */
  816.             moveuntil(&cptr, &bb, term_char, &ins_count, cptr.z-cptr.dot, trace_sw);    /* insert */
  817.             cptr.dot += ins_count;        /* advance command-string pointer */
  818.             getcmdc(trace_sw);            /* skip terminator */
  819.  
  820.             z += ctrl_s;                /* subtract delete length from buffer count */
  821.             delete_p->b->f = insert_p;    /* put the new cell where the old one was */
  822.             insert_p->b = delete_p->b;    /* code borrowed from "insert2" */
  823.             insert_p = NULL;
  824.  
  825.             if (bb.c == cc.c)            /* if replacement text was same length, we can save time */
  826.                 {
  827.                 for (; bb.c < CELLSIZE; bb.c++) bb.p->ch[bb.c] = cc.p->ch[bb.c];    /* copy rest of cell */
  828.                 bb.p->f = cc.p->f;        /* replace orig cell's place in chain with end of new list */
  829.                 if (bb.p->f) bb.p->f->b = bb.p;
  830.                 cc.p->f = NULL;            /* terminate the part snipped out */
  831.                 free_blist(delete_p);    /* return the old part */
  832.                 }
  833.  
  834.             else                        /* different positions: shift the remainder of the buffer */
  835.                 {
  836.                 bb.p->f = delete_p;        /* splice rest of buffer to end */
  837.                 delete_p->b = bb.p;
  838.                 movenchars(&cc, &bb, z-dot);    /* squeeze buffer */
  839.                 free_blist(bb.p->f);        /* return unused cells */
  840.                 bb.p->f = NULL;                /* and end the buffer */
  841.                 }
  842.  
  843.             z += ins_count;                /* add # of chars inserted */
  844.             dot += ins_count;
  845.             ctrl_s = -ins_count;        /* save string length */
  846.             esp->flag1 = esp->flag2 = 0;    /* and consume arguments */
  847.             esp->op = OP_START;
  848.             break;
  849.  
  850.         default:
  851.             ERROR(E_IFC);
  852.         }
  853.     }
  854. /* routines for macro iteration */
  855. /* pop iteration: if arg nonzero, exit unconditionally */
  856. /* else check exit conditions and exit or reiterate */
  857.  
  858. pop_iteration(arg)
  859.     int arg;
  860.     {
  861.     if (!arg && (!cptr.il->dflag || (--(cptr.il->count) > 0)) )        /* if reiteration */
  862.         {
  863.         cptr.p = cptr.il->p;        /* restore */
  864.         cptr.c = cptr.il->c;
  865.         cptr.dot = cptr.il->dot;
  866.         }
  867.     else
  868.         {
  869.         if (cptr.il->b) cptr.il = cptr.il->b;    /* if not last thing on stack, back up */
  870.         else cptr.flag &= ~F_ITER;                /* else clear "iteration" flag */
  871.         }
  872.     }
  873.  
  874.  
  875. /* find end of iteration - read over arbitrary <> and one > */
  876.  
  877. find_enditer()
  878.     {
  879.     register int icnt;
  880.  
  881.     for (icnt = 1; icnt > 0;)        /* scan for matching > */
  882.         {
  883.         while ((skipto(0) != '<') && (skipc != '>'));    /* scan for next < or > */
  884.         if (skipc == '<') ++icnt;        /* and keep track of macro level */
  885.         else --icnt;
  886.         }
  887.     }
  888.  
  889.  
  890.  
  891. /* find end of conditional */
  892. char find_endcond(arg)
  893.     char arg;
  894.     {
  895.     register int icnt;
  896.  
  897.     for (icnt = 1; icnt > 0;)
  898.         {
  899.         while ((skipto(0) != '"') && (skipc != '\'') && (skipc != '|'));
  900.         if (skipc == '"') ++icnt;
  901.         else if (skipc == '\'') -- icnt;
  902.         else if ((icnt == 1) && (arg == '|')) break;
  903.         }
  904.     }
  905.  
  906.