Subject: v09i030: A TECO text editor, Part03/04 Newsgroups: mod.sources Approved: rs@mirror.TMC.COM Submitted by: genrad!mlf Mod.sources: Volume 9, Issue 30 Archive-name: teco/Part03 #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If this archive is complete, you will see the message: # "End of archive 3 (of 4)." # Contents: te_exec1.c te_exec2.c # Wrapped by rs@mirror on Thu Mar 12 19:54:32 1987 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo shar: Extracting \"te_exec1.c\" \(21012 characters\) if test -f te_exec1.c ; then echo shar: Will not over-write existing file \"te_exec1.c\" else sed "s/^X//" >te_exec1.c <<'END_OF_te_exec1.c' X/* TECO for Ultrix Copyright 1986 Matt Fichtenbaum */ X/* This program and its components belong to GenRad Inc, Concord MA 01742 */ X/* They may be copied if this copyright notice is included */ X X/* te_exec1.c continue executing commands 1/8/87 */ X#include "te_defs.h" X Xexec_cmds1() X { X char command; /* command character */ X int cond; /* conditional in progress */ X X switch (command = mapch_l[cmdc]) X { X/* operators */ X X case '+': X esp->exp = (esp->flag1) ? esp->val1 : 0; X esp->flag1 = 0; X esp->op = OP_ADD; X break; X X case '-': X esp->exp = (esp->flag1) ? esp->val1 : 0; X esp->flag1 = 0; X esp->op = OP_SUB; X break; X X case '*': X esp->exp = (esp->flag1) ? esp->val1 : 0; X esp->flag1 = 0; X esp->op = OP_MULT; X break; X X case '/': X esp->exp = (esp->flag1) ? esp->val1 : 0; X esp->flag1 = 0; X esp->op = OP_DIV; X break; X X case '&': X esp->exp = (esp->flag1) ? esp->val1 : 0; X esp->flag1 = 0; X esp->op = OP_AND; X break; X X case '#': X esp->exp = (esp->flag1) ? esp->val1 : 0; X esp->flag1 = 0; X esp->op = OP_OR; X break; X X case ')': X if ((!esp->flag1) || (esp <= &estack[0])) ERROR(E_NAP); X --esp; X esp->val1 = (esp+1)->val1; /* carry value from inside () */ X esp->flag1 = 1; X break; X case ',': X if (!esp->flag1) ERROR(E_NAC); X else esp->val2 = esp->val1; X esp->flag2 = 1; X esp->flag1 = 0; X break; X X case CTL (_): X if (!esp->flag1) ERROR(E_NAB); X else esp->val1 = ~esp->val1; X break; X X/* radix control */ X X case CTL (D): X ctrl_r = 10; X esp->flag1 = 0; X esp->op = OP_START; X break; X X case CTL (O): X ctrl_r = 8; X esp->flag1 = 0; X esp->op = OP_START; X break; X X case CTL (R): X if (!esp->flag1) /* fetch it */ X { X esp->val1 = ctrl_r; X esp->flag1 = 1; X } X else X { X if ((esp->val1 != 8) && (esp->val1 != 10) && (esp->val1 != 16)) ERROR(E_IRA); X ctrl_r = esp->val1; X esp->flag1 = 0; X esp->op = OP_START; X } X break; X X/* other commands */ X X case CTL (C): /* 1 ^C stops macro execution, 2 exit */ X if (peekcmdc(CTL (C))) exitflag = -1; /* 2 ^C: stop execution and exit */ X else if (msp <= &mstack[0]) exitflag = 1; /* 1 ^C: in command string: stop execution */ X else --msp; /* in a macro - pop it */ X break; X X case CTL (X): /* search mode flag */ X set_var(&ctrl_x); X break; X X case 'e': X do_e(); X break; X X case 'f': X do_f(); X break; X /* macro call, iteration, conditional */ X X case 'm': /* macro call */ X mm = getqspec(0, getcmdc(trace_sw)); /* read the macro name */ X if (msp > &mstack[MSTACKSIZE-1]) ERROR(E_PDO); /* check room for another level */ X ++msp; /* push stack */ X cptr.p = qreg[mm].f; /* to stack entry, put q-reg text start */ X cptr.flag = cptr.c = cptr.dot = 0; /* initial char position, iteration flag */ X cptr.z = qreg[mm].z; /* number of chars in macro */ X break; X X case '<': /* begin iteration */ X if ((esp->flag1) && (esp->val1 <= 0)) /* if this is not to be executed */ X find_enditer(); /* just skip the intervening stuff */ X else X { X if (!cptr.il) /* does this macro have an iteration list? */ X { X cptr.il = (struct is *) get_dcell(); /* no, make one for it */ X cptr.il->b = NULL; /* with NULL reverse pointer */ X } X else if (cptr.flag & F_ITER) /* is there an iteration in process? */ X { X if (!cptr.il->f) /* yes, if it has no forward pointer */ X { X cptr.il->f = (struct is *) get_dcell(); /* append a cell to the iteration list */ X cptr.il->f->b = cptr.il; /* and link it in */ X } X cptr.il = cptr.il->f; /* and advance the iteration list pointer to it */ X } X cptr.flag |= F_ITER; /* set iteration flag */ X cptr.il->p = cptr.p; /* save start of iteration */ X cptr.il->c = cptr.c; X cptr.il->dot = cptr.dot; X if (cptr.il->dflag = esp->flag1) /* if there is an argument, set the "def iter" flag */ X { X cptr.il->count = esp->val1; /* save the count */ X esp->flag1 = 0; /* and consume the arg */ X } X } X break; X X case '>': /* end iteration */ X if (!(cptr.flag & F_ITER)) ERROR(E_BNI); /* error if > not in iteration */ X pop_iteration(0); /* decrement count and pop conditionally */ X esp->flag1 = esp->flag2 = 0; /* consume arguments */ X esp->op = OP_START; X break; X X case ';': /* semicolon iteration exit */ X if (!(cptr.flag &F_ITER)) ERROR(E_SNI); /* error if ; not in iteration */ X if ( ( ((esp->flag1) ? esp->val1 : srch_result) >= 0) ? (!colonflag) : colonflag) /* if exit */ X { X find_enditer(); /* get to end of iteration */ X pop_iteration(1); /* and pop unconditionally */ X } X esp->flag1 = colonflag = 0; /* consume arg and colon */ X esp->op = OP_START; X break; X /* conditionals */ X X case '"': X if (!esp->flag1) ERROR(E_NAQ); /* must be an argument */ X esp->flag1 = 0; /* consume argument */ X esp->op = OP_START; X switch (mapch_l[getcmdc(trace_sw)]) X { X case 'a': X cond = isalpha(esp->val1); X break; X X case 'c': X cond = isalpha(esp->val1) | (esp->val1 == '$') | (esp->val1 == '.'); X break; X X case 'd': X cond = isdigit(esp->val1); X break; X X case 'e': X case 'f': X case 'u': X case '=': X cond = !(esp->val1); X break; X X case 'g': X case '>': X cond = (esp->val1 > 0); X break; X X case 'l': X case 's': X case 't': X case '<': X cond = (esp->val1 < 0); X break; X X case 'n': X cond = esp->val1; X break; X X case 'r': X cond = isalnum(esp->val1); X break; X X case 'v': X cond = islower(esp->val1); X break; X X case 'w': X cond = isupper(esp->val1); X break; X X default: X ERROR(E_IQC); X } X if (!cond) /* if this conditional isn't satisfied */ X { X for (ll = 1; ll > 0;) /* read to matching | or ' */ X { X while ((skipto(0) != '"') && (skipc != '|') && (skipc != '\'')); /* skip chars */ X if (skipc == '"') ++ll; /* start another level */ X else if (skipc == '\'') --ll; /* end a level */ X else if (ll == 1) break; /* "else" (|): if on this level, start executing */ X } X } X break; X X case '\'': /* end of conditional */ X break; /* ignore it if executing */ X X case '|': /* "else" clause */ X for (ll = 1; ll > 0;) /* skip to matching ' */ X { X while ((skipto(0) != '"') && (skipc != '\'')); /* skip chars */ X if (skipc == '"') ++ll; /* start another level */ X else --ll; /* end a level */ X } X break; X /* q-register numeric operations */ X X case 'u': X if (!esp->flag1) ERROR(E_NAU); /* error if no arg */ X else qreg[getqspec(0, getcmdc(trace_sw))].v = esp->val1; X esp->flag1 = esp->flag2; /* command's "value" is 2nd arg */ X esp->val1 = esp->val2; X esp->flag2 = 0; /* clear 2nd arg */ X esp->op = OP_START; X break; X X case 'q': /* Qn is numeric val, :Qn is # of chars, mQn is mth char */ X mm = getqspec((colonflag || esp->flag1), getcmdc(trace_sw)); /* read register name */ X if (!(esp->flag1)) X { X esp->val1 = (colonflag) ? qreg[mm].z : qreg[mm].v; X esp->flag1 = 1; X } X else /* esp->flag1 is already set */ X { X if ((esp->val1 >= 0) && (esp->val1 < qreg[mm].z)) /* char subscript within range? */ X { X for (ll = 0, aa.p = qreg[mm].f; ll < (esp->val1 / CELLSIZE); ll++) aa.p = aa.p->f; X esp->val1 = (int) aa.p->ch[esp->val1 % CELLSIZE]; X } X else esp->val1 = -1; /* char position out of range */ X esp->op = OP_START; /* consume argument */ X } X colonflag = 0; X break; X X case '%': X esp->val1 = (qreg[getqspec(0, getcmdc(trace_sw))].v += get_value(1)); /* add to q reg */ X esp->flag1 = 1; X break; X /* move pointer */ X X case 'c': X if (((tdot = dot + get_value(1)) < 0) || (tdot > z)) X ERROR(E_POP); /* add arg to dot, default 1 */ X else dot = tdot; X esp->flag2 = 0; X break; X X case 'r': X if (((tdot = dot - get_value(1)) < 0) || (tdot > z)) X ERROR(E_POP); /* add arg to dot, default 1 */ X else dot = tdot; X esp->flag2 = 0; X break; X X case 'j': X if (((tdot = get_value(0)) < 0) || (tdot > z)) X ERROR(E_POP); /* add arg to dot, default 1 */ X else dot = tdot; X esp->flag2 = 0; X break; X X case 'l': X dot += lines(get_value(1)); X break; X X/* number of chars until nth line feed */ X X case CTL (Q): X esp->val1 = lines(get_value(1)); X esp->flag1 = 1; X break; X X/* print numeric value */ X X case '=': X if (!esp->flag1) ERROR(E_NAE); /* error if no arg */ X else X { X if (peekcmdc('=')) /* at least one more '=' */ X { X getcmdc(trace_sw); /* read past it */ X if (peekcmdc('=')) /* another? */ X { X getcmdc(trace_sw); /* yes, read it too */ X printf("%x", esp->val1); /* print in hex */ X } X else printf("%o", esp->val1); /* print in octal */ X } X else printf("%d", esp->val1); X if (!colonflag) crlf(); X esp->flag1 = esp->flag2 = colonflag = 0; X esp->op = OP_START; X if (!WN_scroll) window(WIN_REDRAW); /* if not in scroll mode, force full redraw on next refresh */ X } X break; X /* insert text */ X X case TAB: /* insert tab, then text */ X if (ez_val & EZ_NOTABI) break; /* tab disabled */ X if (esp->flag1) ERROR(E_IIA); /* can't have arg */ X X case 'i': /* insert text at pointer */ X term_char = (atflag) ? getcmdc(trace_sw) : ESC; /* set terminator */ X if (esp->flag1) /* if a nI$ command */ X { X if (!peekcmdc(term_char)) ERROR(E_IIA); /* next char must be term */ X insert1(); /* first part of insert */ X bb.p->ch[bb.c] = esp->val1 & 0177; /* insert character */ X fwdcx(&bb); /* advance pointer and extend buffer if necessary */ X ins_count = 1; /* save string length */ X esp->op = OP_START; /* consume argument */ X } X else /* not a nI command: insert text */ X { X insert1(); /* initial insert operations */ X X if (command == TAB) /* TAB insert puts in a tab first */ X { X bb.p->ch[bb.c] = TAB; /* insert a tab */ X fwdcx(&bb); /* advance pointer and maybe extend buffer */ X } X moveuntil(&cptr, &bb, term_char, &ins_count, cptr.z - cptr.dot, trace_sw); /* copy cmd str -> buffer */ X if (command == TAB) ++ins_count; /* add 1 if a tab inserted */ X cptr.dot += ins_count; /* indicate advance over that many chars */ X } X insert2(ins_count); /* finish insert */ X getcmdc(trace_sw); /* flush terminating char */ X colonflag = atflag = esp->flag1 = esp->flag2 = 0; /* clear args */ X break; X X/* type text from text buffer */ X X case 't': X for (ll = line_args(0, &aa); ll > 0; ll--) /* while there are chars to type */ X { X type_char(aa.p->ch[aa.c]); X fwdc(&aa); X } X if (!WN_scroll) window(WIN_REDRAW); /* if not in scroll mode, force full redraw on next refresh */ X break; X X case 'v': X if ((ll = get_value(1)) > 0) /* arg must be positive */ X { X mm = lines(1 - ll); /* find start */ X nn = lines(ll) - mm; /* and number of chars */ X set_pointer(dot + mm, &aa); /* pointer to start of text */ X for (; nn > 0; nn--) /* as above */ X { X type_char(aa.p->ch[aa.c]); X fwdc(&aa); X } X } X if (!WN_scroll) window(WIN_REDRAW); /* if not in scroll mode, force full redraw on next refresh */ X break; X /* type text from command string */ X X case CTL (A): X term_char = (atflag) ? getcmdc(trace_sw) : CTL(A); /* set terminator */ X while (getcmdc(0) != term_char) type_char(cmdc); /* output chars */ X atflag = colonflag = esp->flag2 = esp->flag1 = 0; X esp->op = OP_START; X if (!WN_scroll) window(WIN_REDRAW); /* if not in scroll mode, force full redraw on next refresh */ X break; X X /* delete text */ X X case 'd': X if (!esp->flag2) /* if only one argument */ X { X delete1(get_value(1)); /* delete chars (default is 1) */ X break; X } /* if two args, fall through to treat m,nD as m,nK */ X X case 'k': /* delete lines or chars */ X ll = line_args(1, &aa); /* aa points to start, ll chars, leave dot at beginning */ X delete1(ll); /* delete ll chars */ X break; X X/* q-register text loading commands */ X X case CTL (U): X mm = getqspec(0, getcmdc(trace_sw)); X if (!colonflag) /* X, ^U commands destroy previous contents */ X { X dly_free_blist(qreg[mm].f); X qreg[mm].f = NULL; X qreg[mm].z = 0; X } X term_char = (atflag) ? getcmdc(trace_sw) : ESC; /* set terminator */ X atflag = 0; /* clear flag */ X X if ((esp->flag1) || (!peekcmdc(term_char))) /* if an arg or a nonzero insert, find register */ X { X make_buffer(&qreg[mm]); /* attach a text buffer to the q register */ X for (bb.p = qreg[mm].f; bb.p->f != NULL; bb.p = bb.p->f); /* find end of reg */ X bb.c = (colonflag) ? qreg[mm].z % CELLSIZE : 0; X } X if (!(esp->flag1)) X { X moveuntil(&cptr, &bb, term_char, &ll, cptr.z - cptr.dot, trace_sw); X cptr.dot += ll; /* indicate advance over that many chars */ X qreg[mm].z += ll; /* update q-reg char count */ X getcmdc(trace_sw); /* skip terminator */ X } X else X { X if (getcmdc(trace_sw) != term_char) ERROR(E_IIA); /* must be zero length string */ X bb.p->ch[bb.c] = esp->val1; /* store char */ X fwdcx(&bb); /* extend the register */ X ++qreg[mm].z; X esp->flag1 = 0; /* consume argument */ X } X colonflag = 0; X break; X case 'x': X mm = getqspec(0, getcmdc(trace_sw)); X if (!colonflag) /* X, ^U commands destroy previous contents */ X { X dly_free_blist(qreg[mm].f); /* return, but delayed (in case executing now) */ X qreg[mm].f = NULL; X qreg[mm].z = 0; X } X X if (ll = line_args(0, &aa)) /* read args and move chars, if any */ X { X make_buffer(&qreg[mm]); /* attach a text buffer to the q register */ X for (bb.p = qreg[mm].f; bb.p->f != NULL; bb.p = bb.p->f); /* find end of reg */ X bb.c = (colonflag) ? qreg[mm].z % CELLSIZE : 0; X X movenchars(&aa, &bb, ll); X qreg[mm].z += ll; /* update q-reg char count */ X } X colonflag = 0; X break; X X case 'g': /* get q register */ X if (qreg[mm = getqspec(1, getcmdc(trace_sw))].z) /* if any chars in it */ X { X cc.p = qreg[mm].f; /* point cc to start of reg */ X cc.c = 0; X if (colonflag) /* :Gx types q-reg */ X { X for (ll = qreg[mm].z; ll > 0; ll--) X { X type_char(cc.p->ch[cc.c]); /* type char */ X fwdc(&cc); X } X } X else X { X insert1(); /* set up for insert */ X movenchars(&cc, &bb, qreg[mm].z); /* copy q reg text */ X insert2(qreg[mm].z); /* finish insert */ X } X } X colonflag = 0; X break; X /* q-register push and pop */ X X case '[': X if (qsp > &qstack[QSTACKSIZE-1]) ERROR(E_PDO); /* stack full */ X else X { X make_buffer(++qsp); /* increment stack ptr and put a text buffer there */ X mm = getqspec(1, getcmdc(trace_sw)); /* get the q reg name */ X X aa.p = qreg[mm].f; /* point to the q register */ X aa.c = 0; X bb.p = qsp->f; /* point to the new list */ X bb.c = 0; X movenchars(&aa, &bb, qreg[mm].z); /* copy the text */ X qsp->z = qreg[mm].z; /* and the length */ X qsp->v = qreg[mm].v; /* and the value */ X } X break; X X case ']': X mm = getqspec(1, getcmdc(trace_sw)); /* get reg name */ X if (qsp < &qstack[0]) /* if stack empty */ X { X if (colonflag) /* :] returns 0 */ X { X esp->flag1 = 1; X esp->val1 = 0; X colonflag = 0; X } X else ERROR(E_CPQ); /* ] makes an error */ X } X else /* stack not empty */ X { X free_blist(qreg[mm].f); /* return orig contents of reg */ X qreg[mm].f = qsp->f; /* substitute stack entry */ X qsp->f->b = (struct buffcell *) &qreg[mm]; X qsp->f = NULL; /* null out stack entry */ X qreg[mm].z = qsp->z; X qreg[mm].v = qsp->v; X if (colonflag) X { X esp->flag1 = 1; /* :] returns -1 */ X esp->val1 = -1; X colonflag = 0; X } X --qsp; X } X break; X case '\\': X if (!(esp->flag1)) /* no argument; read number */ X { X ll = esp->val1 = 0; /* sign flag and initial value */ X for (ctrl_s = 0; dot <= z; dot++, ctrl_s--) /* count digits; don't read beyond buffer */ X { X set_pointer(dot, &aa); /* point to dot */ X if ((aa.p->ch[aa.c] == '+') || (aa.p->ch[aa.c] == '-')) X { X if (ll) break; /* second sign: quit */ X else ll = aa.p->ch[aa.c]; /* first sign: save it */ X } X else X { X if (ctrl_r != 16) /* octal or decimal */ X { /* stop if not a valid digit */ X if ((!isdigit(aa.p->ch[aa.c])) || (aa.p->ch[aa.c] - '0' >= ctrl_r)) break; X esp->val1 = esp->val1 * ctrl_r + (aa.p->ch[aa.c] - '0'); X } X else X { X if (!isxdigit(aa.p->ch[aa.c])) break; X esp->val1 = esp->val1 * 16 + ( (isdigit(aa.p->ch[aa.c])) ? X aa.p->ch[aa.c] - '0' : mapch_l[aa.p->ch[aa.c]] - 'a' + 10); X } /* end of hex */ X } /* end of digit processing */ X } /* end of "for each char" */ X if (ll == '-') esp->val1 = -(esp->val1); /* if minus sign */ X esp->flag1 = 1; /* always returns a value */ X } X X else /* argument: insert it as a digit string */ X { X if (ctrl_r == 8) sprintf(t_bcell.ch, "%o", esp->val1); /* print as digits */ X else if (ctrl_r == 10) sprintf(t_bcell.ch, "%d", esp->val1); X else sprintf(t_bcell.ch, "%x", esp->val1); X insert1(); /* start insert */ X cc.p = &t_bcell; /* point cc to the temp cell */ X cc.c = 0; X moveuntil(&cc, &bb, '\0', &ins_count, CELLSIZE-1, 0); /* copy the char string */ X insert2(ins_count); /* finish the insert */ X esp->flag1 = 0; /* consume argument */ X esp->op = OP_START; X } X break; X case CTL (T): /* type or input character */ X if (esp->flag1) /* type */ X { X type_char(esp->val1); X esp->flag1 = 0; X if (!WN_scroll) window(WIN_REDRAW); /* if not in scroll mode, force full redraw on next refresh */ X } X else X { X esp->val1 = (et_val & ET_NOWAIT) ? gettty_nowait() : gettty(); X if (!(et_val & ET_NOECHO) && (esp->val1 > 0) && !inp_noterm) type_char(esp->val1); /* echo */ X esp->flag1 = 1; X } X break; X X/* search commands */ X X case 's': /* search within buffer */ X build_string(&sbuf); /* read the search string */ X end_search ( do_search( setup_search() ) ); /* search */ X break; X X case 'n': /* search through rest of file */ X case '_': X do_nsearch(command); /* call routine for N, _, E_ */ X break; X X case 'o': /* branch to tag */ X do_o(); X break; X /* file I/O commands */ X X case 'p': /* write a page, get next (ignore args for now) */ X if (esp->flag1 && esp->flag2) /* if two args */ X write_file(&aa, line_args(0, &aa), 0); /* write spec'd buffer with no FF */ X else /* one arg */ X { X for (ll = get_value(1); ll > 0; ll--) /* get count and loop */ X { X set_pointer(0, &aa); X if (peekcmdc('w')) write_file(&aa, z, 1); /* PW writes buffer, then FF */ X else X { X write_file(&aa, z, ctrl_e); /* P writes buffer, FF if read in, then gets next page */ X dot = z = 0; /* empty the buffer */ X set_pointer(0, &aa); /* set a pointer to the beginning of the buffer */ X buff_mod = 0; /* mark where new buffer starts */ X esp->val1 = read_file(&aa, &z, (ed_val & ED_EXPMEM ? -1 : 0) ); /* read a page */ X esp->flag1 = colonflag; X } X } X } X if (peekcmdc('w')) getcmdc(trace_sw); /* if a PW command, consume the W */ X colonflag = 0; X break; X X case 'y': /* get a page into buffer */ X if (esp->flag1) ERROR(E_NYA); X if ((z) && (!(ed_val & ED_YPROT))) ERROR(E_YCA); /* don't lose text */ X dot = z = 0; /* clear buffer */ X set_pointer(0, &aa); /* set a pointer to the beginning of the buffer */ X buff_mod = 0; /* mark where new buffer starts */ X read_file(&aa, &z, (ed_val & ED_EXPMEM ? -1 : 0) ); /* read a page */ X esp->flag1 = colonflag; X esp->op = OP_START; X colonflag = 0; X break; X case 'a': /* append, or ascii value */ X if (esp->flag1 && !colonflag) /* ascii value */ X { X ll = dot + esp->val1; /* set a pointer before addr'd char */ X if ((ll >= 0) && (ll < z)) /* if character lies within buffer */ X { X set_pointer(ll, &aa); X esp->val1 = (int) aa.p->ch[aa.c]; /* get char (flag already set) */ X } X else esp->val1 = -1; /* otherwise return -1 */ X } X else X { X set_pointer(z, &aa); /* set pointer to end of buffer */ X if (z < buff_mod) buff_mod = z; /* mark where new buffer starts */ X if (esp->flag1 && (esp->val1 <= 0)) ERROR(E_IAA); /* neg or 0 arg to :A */ X read_file(&aa, &z, (esp->flag1 ? esp->val1 : 0) ); /* read a page */ X esp->flag1 = colonflag; X colonflag = 0; X } X esp->op = OP_START; X break; X X/* window commands */ X X case 'w': X do_window(0); /* this stuff is with the window driver */ X break; X X case CTL (W): X do_window(1); /* this is, too */ X break; X X default: X ERROR(E_ILL); /* invalid command */ X X } /* end of "switch" */ X return; /* normal exit */ X } /* end of exec_cmds1 */ X END_OF_te_exec1.c if test 21012 -ne `wc -c te_exec2.c <<'END_OF_te_exec2.c' X/* TECO for Ultrix Copyright 1986 Matt Fichtenbaum */ X/* This program and its components belong to GenRad Inc, Concord MA 01742 */ X/* They may be copied if this copyright notice is included */ X X/* te_exec2.c process "E" and "F" commands 2/26/87 */ X#include "te_defs.h" X#include X Xstruct qh oldcstring; /* hold command string during ei */ X X/* file stuff for input/output files */ X Xstruct infiledata pi_file = { NULL, -1 }; /* input files */ Xstruct infiledata si_file = { NULL, -1 }; Xstruct infiledata *infile = &pi_file; /* pointer to currently active input file structure */ Xstruct outfiledata po_file, so_file; /* output files */ Xstruct outfiledata *outfile = &po_file; /* pointer to currently active output file structure */ XFILE *eisw; /* indirect command file pointer */ X X/* process E commands */ X Xdo_e() X { X char c; /* temps */ X int old_var; X FILE *t_eisw; X X switch (mapch_l[getcmdc(trace_sw)]) /* read next character and dispatch */ X { X X/* numeric values */ X X case 'd': /* ED */ X set_var(&ed_val); X break; X X case 's': /* ES */ X set_var(&es_val); X break; X X case 't': /* ET */ X old_var = et_val; X set_var(&et_val); X et_val = (et_val & 0100651) | 001006; /* force read_only bits */ X if ((et_val ^ old_var) & ET_TRUNC) window(WIN_REDRAW); /* redraw if ET & 256 changes */ X break; X X case 'u': /* EU */ X set_var(&eu_val); X break; X X case 'v': /* EV */ X set_var(&ev_val); X break; X X case 'z': /* EZ */ X old_var = ez_val; X set_var(&ez_val); X tabmask = (ez_val & EZ_TAB4) ? 3 : 7; /* set tab mask */ X if ((ez_val ^ old_var) & EZ_TAB4) window(WIN_REDRAW); /* force window redisplay if EZ_TAB4 changes */ X break; X /* E_ search */ X X case '_': X do_nsearch('e'); X break; X X/* file I/O commands */ X X case 'a': /* set secondary output */ X outfile = &so_file; X break; X X case 'b': /* open read/write with backup */ X if (!read_filename(1, 'r')) ERROR(E_NFI); /* read the name */ X if (infile->fd) fclose(infile->fd); /* close previously-open file */ X if (!(infile->fd = fopen(fbuf.f->ch, "r"))) X { X if (!colonflag) ERROR(E_FNF); X } X else X { X if (outfile->fd) ERROR(E_OFO); /* output file already open */ X for (ll = 0; ll < CELLSIZE; ll++) /* save file string */ X if ((outfile->t_name[ll] = outfile->f_name[ll] = fbuf.f->ch[ll]) == '\0') break; X outfile->name_size = ll; X outfile->t_name[ll++] = '.'; X outfile->t_name[ll++] = 't'; X outfile->t_name[ll++] = 'm'; X outfile->t_name[ll++] = 'p'; X outfile->t_name[ll] = '\0'; X if (!(outfile->fd = fopen(outfile->t_name, "w"))) ERROR(E_COF); X outfile->bak = 1; /* set backup mode */ X } X infile->eofsw = -1 - (esp->val1 = (infile->fd) ? -1 : 0); X esp->flag1 = colonflag; X colonflag = 0; X break; X X case 'x': /* finish edit and exit */ X exitflag = -1; X X /* --- fall through to "EC" --- */ X X case 'c': /* finish edit */ X set_pointer(0, &aa); /* set a pointer to start of text buffer */ X write_file(&aa, z, ctrl_e); /* write the current buffer */ X dot = z = 0; /* empty the buffer */ X window(WIN_REDRAW); /* force window redraw */ X if ((outfile->fd) && (infile->fd) && !(infile->eofsw)) /* if any input remaining, copy it to output */ X while ((c = getc(infile->fd)) != EOF) putc(c, outfile->fd); X X /* --- fall through to "EF" --- */ X case 'f': /* close output file */ X if (outfile->fd) /* only if one is open */ X { X fclose(outfile->fd); X if (outfile->bak & 1) /* if this is "backup" mode */ X { X outfile->f_name[outfile->name_size] = '.'; X outfile->f_name[outfile->name_size+1] = 'b'; X outfile->f_name[outfile->name_size+2] = 'a'; X outfile->f_name[outfile->name_size+3] = 'k'; X outfile->f_name[outfile->name_size+4] = '\0'; X outfile->t_name[outfile->name_size] = '\0'; X rename(outfile->t_name, outfile->f_name); /* rename orig file */ X } X X if (!(outfile->bak & 8)) /* if output file had ".tmp" extension */ X { /* remove it */ X outfile->t_name[outfile->name_size] = '.'; X outfile->f_name[outfile->name_size] = '\0'; X rename(outfile->t_name, outfile->f_name); /* rename output */ X } X } X outfile->fd = NULL; /* mark "no output file open" */ X break; X X case 'i': /* indirect file execution */ X if (!read_filename(1, 'i')) /* if no filename specified, reset command input */ X { X if (eisw) /* if ending a file execute, restore the previous "old command string" */ X { X fclose(eisw); /* return the file descriptor */ X dly_free_blist(cbuf.f); /* return the command string used by the file (after execution done) */ X cbuf.f = oldcstring.f; X cbuf.z = oldcstring.z; X } X t_eisw = 0; X } X else if (!(t_eisw = fopen(fbuf.f->ch, "r"))) X { X if (!colonflag) ERROR(E_FNF); X } X else if (!eisw) /* if this "ei" came from the command string */ X { X oldcstring.f = cbuf.f; /* save current command string */ X oldcstring.z = cbuf.z; X cbuf.f = NULL; /* and make it inaccessible to "rdcmd" */ X } X if (eisw) fclose(eisw); /* if a command file had been open, close it */ X esp->val1 = (eisw = t_eisw) ? -1 : 0; X esp->flag1 = colonflag; X colonflag = 0; X esp->op = OP_START; X break; X case 'k': /* kill output file */ X kill_output(outfile); X break; X X case 'p': /* switch to secondary input */ X infile = &si_file; X break; X X case 'r': /* specify input file, or switch to primary input */ X if (!read_filename(0, 'r')) infile = &pi_file; /* no name, switch to primary input */ X else X { X if (infile->fd) fclose(infile->fd); /* close previously-open file */ X if (!(infile->fd = fopen(fbuf.f->ch, "r"))) X { X if (!colonflag) ERROR(E_FNF); X } X } X infile->eofsw = -1 - (esp->val1 = (infile->fd) ? -1 : 0); X esp->flag1 = colonflag; X colonflag = 0; X esp->op = OP_START; X break; X X case 'w': /* specify output file, or switch to primary output */ X if(!read_filename(0, 'w')) outfile = &po_file; X else X { X if (outfile->fd) ERROR(E_OFO); /* output file already open */ X for (ll = 0; ll < CELLSIZE; ll++) /* save file string */ X if ((outfile->t_name[ll] = outfile->f_name[ll] = fbuf.f->ch[ll]) == '\0') break; X outfile->name_size = ll; X if (!(ez_val & EZ_NOTMPFIL)) /* if not using literal output name */ X { X outfile->t_name[ll++] = '.'; /* use provisional suffix ".tmp" */ X outfile->t_name[ll++] = 't'; X outfile->t_name[ll++] = 'm'; X outfile->t_name[ll++] = 'p'; X outfile->t_name[ll] = '\0'; X } X if (!(outfile->fd = fopen(outfile->t_name, "w"))) ERROR(E_COF); X outfile->bak = ez_val & EZ_NOTMPFIL; /* save "temp suffix" status */ X } X break; X X case 'y': /* EY is Y without protection */ X if (esp->flag1) ERROR(E_NYA); X dot = z = 0; /* clear buffer */ X set_pointer(0, &aa); X read_file(&aa, &z, (ed_val & ED_EXPMEM ? -1 : 0) ); X esp->flag1 = colonflag; X colonflag = 0; X esp->op = OP_START; X break; X case 'n': /* wildcard filespec */ X esp->val1 = do_en(); X esp->flag1 = colonflag; X colonflag = 0; X esp->op = OP_START; X break; X X case 'q': /* system command */ X esp->val1 = do_eq(); /* do this as a separate routine */ X esp->flag1 = colonflag; X colonflag = 0; X esp->op = OP_START; X break; X X default: X ERROR(E_IEC); X } X } X /* routine to execute a system command */ X/* this is done by forking off another process */ X/* to execute a shell via 'execl' */ X/* routine returns -1 if success, 0 if error in fork */ X Xint do_eq() X { X int t; X union wait status; X char *pname; /* pointer to name of shell */ X extern char *getenv(); X X build_string(&sysbuf); X if (sysbuf.z > CELLSIZE-2) ERROR(E_STL); /* command must fit within one cell */ X sysbuf.f->ch[sysbuf.z] = '\0'; /* store terminating null */ X if (!(pname = getenv("SHELL"))) ERROR(E_SYS); /* read shell name */ X X if (!esp->flag1) /* if not m,nEQ command */ X { X if (win_data[7]) window(WIN_SUSP); /* restore full screen */ X crlf(); /* force characters out */ X setup_tty(TTY_SUSP); /* restore terminal to normal mode */ X X t = vfork(); /* fork a new process */ X if (t == 0) /* if this is the child */ X { X execl(pname, pname, "-c", &sysbuf.f->ch[0], 0); /* call the named Unix routine */ X printf("Error in 'execl'\n"); /* normally shouldn't get here */ X exit(1); X } X X while (wait(&status) != t); /* if parent, wait for child to finish */ X if (status.w_retcode) t = -1; /* keep failure indication from child */ X X setup_tty(TTY_RESUME); /* restore terminal to teco mode */ X if (win_data[7]) /* if window was enabled */ X { X vt(VT_SETSPEC1); /* set reverse video */ X fputs("Type RETURN to continue", stdout); /* require CR before continuing */ X vt(VT_CLRSPEC); /* reverse video off */ X while (gettty() != LF); X putchar(CR); /* back to beginning of line */ X vt(VT_EEOL); /* and erase the message */ X window(WIN_RESUME); /* re-enable window */ X window(WIN_REDRAW); /* and make it redraw afterwards */ X } X } X X else t = do_eq1(pname); /* m,nEQ command */ X X return( (t == -1) ? 0 : -1); /* return failure if fork failed or proc status nonzero */ X } X /* Execute m,nEQtext$ command. Run "text" as a Unix command that */ X/* receives its std input from chars m through n of teco's buffer. */ X/* Output from the command is placed in Q#. */ X Xint do_eq1(shell) X char *shell; /* arg is pointer to shell name */ X { X int ff, pipe_in[2], pipe_out[2]; /* fork result and two pipes */ X FILE *xx_in, *xx_out; /* std in and out for called process */ X FILE *fdopen(); X union wait status; X X ll = line_args(1, &aa); /* set aa to start of text, ll to number of chars */ X dot += ll; /* set pointer at end of text */ X ctrl_s = -ll; /* set ^S to - # of chars */ X X if (pipe(pipe_out)) ERROR(E_SYS); /* make input pipe; failure if can't */ X X if (win_data[7]) window(WIN_SUSP); /* disable split screen */ X setup_tty(TTY_SUSP); /* put console back to original state */ X if ((ff = fork()) == -1) /* fork first child: if error, quit */ X { X close(pipe_out[0]); X close(pipe_out[1]); X setup_tty(TTY_RESUME); X if (win_data[7]) window(WIN_RESUME), window(WIN_REDRAW); X ERROR(E_SYS); X } X X if (ff) /* if this is the parent, the task is to read into q# */ X { X make_buffer(&timbuf); /* initialize the q# header */ X bb.p = timbuf.f; /* init bb to point to q# */ X timbuf.z = bb.c = 0; X X close(pipe_out[1]); /* parent won't write to this pipe */ X X if ((xx_out = fdopen(pipe_out[0], "r")) == 0) /* open the "std out" pipe for reading */ X { X close(pipe_out[0]); /* if can't open output pipe */ X setup_tty(TTY_RESUME); X if (win_data[7]) window(WIN_RESUME), window(WIN_REDRAW); X ERROR(E_SYS); /* "open" failure */ X } X read_stream(xx_out, 0, &bb, &timbuf.z, 0, 0, 1); /* read from pipe to q# */ X close(pipe_out[0]); X X while (wait(&status) != ff); /* wait for children to finish */ X setup_tty(TTY_RESUME); X if (win_data[7]) window(WIN_RESUME), window(WIN_REDRAW); X return(status.w_retcode ? -1 : 0); X } X /* This is the child. It in turn forks into two processes, of which the "parent" */ X/* (original child) writes the specified part of the buffer to the pipe, and the */ X/* new child (grandchild to the original teco) execl's the Unix command. */ X X else /* this is the child */ X { X close(pipe_out[0]); /* won't read from "output" pipe */ X if (pipe(pipe_in)) exit(1); /* make the "std in" pipe for the process, quit if can't */ X X if ((ff = fork()) == -1) exit(1); /* fork to two processes (total 3), exit if error */ X X if (ff) /* parent - will send chars */ X { X close(pipe_in[0]); /* won't read from this pipe */ X X /* open pipe for writing; exit if open fails */ X X if ((xx_in = fdopen(pipe_in[1], "w")) == 0) exit(1); X X write_stream(xx_in, &aa, ll, 0); /* write to stream, CRLF becomes LF */ X fclose(xx_in); X X while (wait(&status) != ff); /* wait for child */ X exit(status.w_retcode); /* exit with child's status */ X } X X else /* this process is the grandchild */ X { X close(pipe_in[1]); /* close "input" for writing */ X dup2(pipe_in[0], fileno(stdin)); /* substitute pipe_in for stdin */ X dup2(pipe_out[1], fileno(stdout)); /* and pipe_out for stdout */ X close(pipe_in[0]); /* close original descriptors */ X close(pipe_out[1]); X X execl(shell, shell, "-c", &sysbuf.f->ch[0], 0); /* execute specified routine */ X fputs("execl failed\n", stderr); X exit(1); X } X } X } X /* Routines to handle EN wild-card file specification */ X/* ENfilespec$ defines file class, leaving 'filespec' */ X/* in filespec buffer and reading expanded list of */ X/* files into local buffer. EN$ gets next filespec */ X/* into filespec buffer. */ X Xstruct qh en_buf; /* header for storage for file list */ Xstruct qp en_ptr; /* pointer to load/read file list */ Xstatic char glob_cmd0[] = { 'g', 'l', 'o', 'b', ' ' } ; X Xint do_en() X { X int t; X X if (build_string(&fbuf)) /* if a file string is specified */ X { X if (fbuf.z > CELLSIZE-2) ERROR(E_STL); /* upper limit on string length */ X fbuf.f->ch[fbuf.z] = '\0'; /* terminating null */ X t = do_glob(&en_buf); /* glob the result */ X en_ptr.p = en_buf.f; /* set the buffer pointer to the beginning of the buffer */ X en_ptr.dot = en_ptr.c = 0; X } X else /* if no string, get next filename */ X { X do_en_next(); X t = (fbuf.z) ? -1 : 0; /* t zero if no more filespecs */ X if (!t && !colonflag) ERROR(E_NFI); /* if no colon, end of spec is an error */ X } X return (t); X } X X/* routine to expand the string in the filespec buffer */ X/* argument is the address of a qh that gets the expanded string */ X/* argument->v gets set to the number of file specs found */ X Xint do_glob(buff) X struct qh *buff; X { X char glob_cmd[CELLSIZE+5]; /* "glob filespec" command string */ X int t; X char c; X int glob_pipe[2]; /* pipe to forked shell for expanding filenames */ X struct qp glob_ptr; /* pointer for loading result buffer */ X FILE *xx_out; /* stream for reading chars from pipe */ X FILE *fdopen(); X union wait status; X X make_buffer(buff); /* initialize expanded file buffer */ X glob_ptr.p = buff->f; /* initialize pointer to buffer */ X glob_ptr.c = glob_ptr.dot = buff->z = buff->v = 0; X for (t = 0; t < 5; t++) glob_cmd[t] = glob_cmd0[t]; /* set up "glob filespec" command */ X for (t = 0; t < fbuf.z +1; t++) glob_cmd[t+5] = fbuf.f->ch[t]; X X if (pipe(glob_pipe)) ERROR(E_SYS); /* make a pipe */ X setup_tty(TTY_SUSP); /* put console back to normal */ X if ((t = fork()) == -1) /* spawn a child... if failure */ X { X close(glob_pipe[0]); /* undo the pipe */ X close(glob_pipe[1]); X setup_tty(TTY_RESUME); X ERROR(E_SYS); /* and exit with failure */ X } X X if (t) /* if this is the parent */ X { X close(glob_pipe[1]); /* parent won't write */ X if ((xx_out = fdopen(glob_pipe[0], "r")) == 0) /* open pipe for read */ X { X close(glob_pipe[0]); /* failure to open */ X setup_tty(TTY_RESUME); X ERROR(E_SYS); X } X X while ((c = getc(xx_out)) != EOF) /* read characters from pipe */ X { X if (c == '\0') ++buff->v; /* count null chars that separate file specs */ X glob_ptr.p->ch[glob_ptr.c] = c; /* store them in buffer */ X fwdcx(&glob_ptr); X } X X fclose(xx_out); /* through with stream */ X buff->z = glob_ptr.dot; /* save character count */ X while (wait(&status) != t); /* wait for child to finish */ X setup_tty(TTY_RESUME); X return(status.w_retcode ? 0 : -1); /* return success unless child exited nonzero */ X } X else /* this is the child */ X { X close(glob_pipe[0]); /* child won't read */ X dup2(glob_pipe[1], fileno(stdout)); /* substitute pipe for standard out */ X close(glob_pipe[1]); /* don't need that anymore */ X execl("/bin/csh", "csh", "-fc", glob_cmd, 0); /* execute the "glob" */ X fputs("execl failed\n", stderr); X exit(1); X } X } X X/* routine to get next file spec from "EN" list into filespec buffer */ X Xdo_en_next() X { X char c; X X make_buffer(&fbuf); /* initialize the filespec buffer */ X fbuf.z = 0; X X while(en_ptr.dot < en_buf.z) /* stop at end of string */ X { X c = en_ptr.p->ch[en_ptr.c]; X fwdc(&en_ptr); X if (!c) break; /* null is terminator between file specs */ X fbuf.f->ch[fbuf.z++] = c; /* store char */ X if (fbuf.z >= CELLSIZE-1) ERROR(E_STL); /* limit on filespec size */ X } X X fbuf.f->ch[fbuf.z] = '\0'; /* filespec ends with NULL */ X } X X/* routine to read a file name */ X/* reads it into fbuf text area */ X/* returns nonzero if a name, 0 if none */ X/* flag nonzero => empty name clears filespec buffer */ X/* func is 'r' for ER or EB cmds, 'i' for EI, 'w' for EW */ X Xint read_filename(flag, func) X int flag; X char func; X { X int i, t, expand; X char c; X struct qh temp_buff; /* temp buffer for globbing filespec */ X X if (!(t = build_string(&fbuf))) /* if no name spec'd */ X { X if (flag) fbuf.z = 0; /* if no name spec'd, set length to 0 */ X } X else X { X if (fbuf.z > CELLSIZE-2) ERROR(E_STL); X fbuf.f->ch[fbuf.z] = '\0'; /* store terminating NULL */ X X/* check for characters to be expanded by the shell */ X X for (i = 0; i < fbuf.z; i++) X if ((c = fbuf.f->ch[i]) == '*' || c == '?' || c == '[' || c == 0173) break; X if ( (expand = (i < fbuf.z)) || fbuf.f->ch[0] == '~') /* one of these was found, or first char is ~ */ X { X temp_buff.f = NULL; /* make a temp buffer to glob filename into */ X make_buffer(temp_buff); X do_glob(&temp_buff); /* expand the file name */ X if (temp_buff.z == 0) /* no match */ X { X free_blist(temp_buff.f); /* return the storage */ X ERROR(func == 'w' ? E_COF : E_FNF); /* "can't open" or "file not found" */ X } X else if (temp_buff.v == 0) /* if exactly one file name */ X { X free_blist(fbuf.f); /* return the old file spec */ X fbuf.f = temp_buff.f; /* put the temp buffer there instead */ X fbuf.z = temp_buff.z; X if (fbuf.z > CELLSIZE-2) ERROR(E_STL); X fbuf.f->ch[fbuf.z] = '\0'; X X if (func == 'w' && expand) /* if this is EW and 'twas from a wildcard expansion */ X { X vt(VT_SETSPEC1); /* "file XXXX already exists: overwrite? [yn]" */ X fputs("File ", stdout); X fputs(fbuf.f->ch, stdout); X fputs(" already exists: overwrite? [ny] ", stdout); X vt(VT_CLRSPEC); X c = gettty(); /* read user's response */ X putchar(CR); X vt(VT_EEOL); /* clean up the screen */ X if (c != 'y') ERROR(E_COF); /* abort here */ X } X } X X else /* multiple file specs */ X { X if (func != 'r' || !(ez_val & EZ_MULT)) /* if multiple file specs here aren't allowed */ X { X free_blist(temp_buff.f); /* return the storage */ X ERROR(E_AMB); X } X free_blist(en_buf.f); /* substitute the expansion for the "EN" list */ X en_ptr.p = en_buf.f = temp_buff.f; /* and initialize the "EN" list pointer */ X en_buf.z = temp_buff.z; X en_ptr.dot = en_ptr.c = 0; X do_en_next(); /* get the first file */ X } X } X } X return(t); X } X X X X/* fetch or set variable */ X Xset_var(arg) X int *arg; /* argument is pointer to variable */ X { X if (esp->flag1) /* if an argument, then set the variable */ X { X if (esp->flag2) /* if two arguments, then , */ X *arg = (*arg & ~esp->val2) | esp->val1; X else *arg = esp->val1; /* one arg is new value */ X esp->flag2 = esp->flag1 = 0; /* consume argument */ X } X else /* otherwise fetch the variable's value */ X { X esp->val1 = *arg; X esp->flag1 = 1; X } X } X X X X/* read from selected input file stream into specified buffer */ X/* terminate on end-of-file or form feed */ X/* if endsw > 0 terminates after that many lines */ X/* if endsw < 0 stops if z > BUFF_LIMIT */ X/* returns -1 if read EOF, 0 otherwise */ X Xint read_file(buff, nchars, endsw) X struct qp *buff; X int *nchars, endsw; X { X if (!infile->fd) infile->eofsw = -1, ctrl_e = 0; /* return if no input file open */ X else infile->eofsw = read_stream(infile->fd, &ctrl_e, buff, nchars, endsw, ez_val & EZ_CRLF, ez_val & EZ_READFF); X return(esp->val1 = infile->eofsw); X } X /* read from an I/O stream into specified buffer */ X/* this is used by read_file and by "eq" pipe from other Unix processes */ X/* args buff, nchars, endsw as above; file is stream pointer, ff_found is */ X/* address of a switch to set if read ended with a FF, crlf_sw is lf->crlf */ X/* conversion, ff_sw indicates whether to stop on a form feed. */ X Xint read_stream(file, ff_found, buff, nchars, endsw, crlf_sw, ff_sw) X FILE *file; X struct qp *buff; X int *ff_found, *nchars, endsw, crlf_sw, ff_sw; X { X char chr; X int crflag; X register struct buffcell *p; X register int c; X X p = (*buff).p; /* copy pointer locally */ X c = (*buff).c; X crflag = 0; /* "last char wasn't CR" */ X while (((chr = getc(file)) != EOF) && ((chr != FF) || ff_sw)) X { X if ((chr == LF) && !crflag && !crlf_sw) /* automatic cr before lf */ X { X p->ch[c] = CR; /* store a cr */ X ++(*nchars); /* increment buffer count */ X if (++c > CELLSIZE-1) /* next cell? */ X { X if (!p->f) /* need a new cell? */ X { X p->f = get_bcell(); X p->f->b = p; X } X p = p->f; X c = 0; X } X } X p->ch[c] = chr; /* store char */ X ++(*nchars); /* increment character count */ X if (++c > CELLSIZE-1) /* next cell? */ X { X if (!p->f) /* need a new cell? */ X { X p->f = get_bcell(); X p->f->b = p; X } X p = p->f; X c = 0; X } X crflag = (chr == CR); /* flag set if last char was CR */ X if ((chr == LF) && ((endsw < 0 && z > BUFF_LIMIT) || (endsw > 0 && --endsw == 0))) break; /* term after N lines */ X } X (*buff).p = p; /* update pointer */ X (*buff).c = c; X if (ff_found) *ff_found = (chr == FF) ? -1 : 0; /* set flag to indicate whether FF found */ X return( (chr == EOF) ? -1 : 0); /* and return "eof found" value */ X } X /* routine to write text buffer out to selected output file */ X/* arguments are qp to start of text, number of characters, */ X/* and an "append FF" switch */ X Xwrite_file(buff, nchars, ffsw) X struct qp *buff; X int nchars, ffsw; X { X if (!outfile->fd && (nchars)) ERROR(E_NFO); X else write_stream(outfile->fd, buff, nchars, ez_val & EZ_CRLF); X if (outfile->fd && ffsw) putc(FF, outfile->fd); X } X X X/* routine to write text buffer to I/O stream. Used by */ X/* write_file, above, and by "eq" write to pipe to other */ X/* Unix processes. Arguments buff, nchars as above; file */ X/* is stream pointer, crlf_sw zero converts CRLF to LF */ X Xwrite_stream(file, buff, nchars, crlf_sw) X FILE *file; X struct qp *buff; X int nchars, crlf_sw; X { X char c; X int crflag; X X crflag = 0; X for (; nchars > 0; nchars--) X { X if ((c = (*buff).p->ch[(*buff).c]) == CR) crflag = 1; /* set flag if a c.r. */ X else X { X if ((crflag) && ((c != LF) || crlf_sw)) /* if c.r. not before lf, or if not in */ X putc(CR, file); /* "no cr" mode, output the c.r. */ X crflag = 0; X putc(c, file); X } X if (++(*buff).c > CELLSIZE-1) X { X (*buff).p = (*buff).p->f; X (*buff).c = 0; X } X } X } X X X/* routine to kill output file: argument is pointer to an output file structure */ X Xkill_output(outptr) X struct outfiledata *outptr; X { X if (outptr->fd) X { X fclose(outptr->fd); X unlink(outptr->t_name); X outptr->fd = NULL; X } X } X /* "panic" routine called when "hangup" signal occurs */ X/* this routine saves the text buffer and closes the output files */ X Xchar panic_name[] = "TECO_SAVED.tmp"; /* name of file created to save buffer */ X Xpanic() X { X if (!outfile->fd && z) outfile->fd = fopen(panic_name, "w"); /* if buffer nonempty and no file open, make one */ X X set_pointer(0, &aa); /* set a pointer to start of text buffer */ X if (outfile->fd && z) write_file(&aa, z, 0); /* and write out buffer unless "open" failed */ X X if (po_file.fd) fclose(po_file.fd), po_file.fd = NULL; /* close any open output files */ X if (so_file.fd) fclose(so_file.fd), so_file.fd = NULL; X } X /* do "F" commands */ X Xdo_f() X { X struct buffcell *delete_p; X X switch (mapch_l[getcmdc(trace_sw)]) /* read next character and dispatch */ X { X case '<': /* back to beginning of current iteration */ X if (cptr.flag & F_ITER) /* if in iteration */ X { X cptr.p = cptr.il->p; /* restore */ X cptr.c = cptr.il->c; X cptr.dot = cptr.il->dot; X } X else for (cptr.dot = cptr.c = 0; cptr.p->b->b != NULL; cptr.p = cptr.p->b); /* else, restart current macro */ X break; X X case '>': /* to end of current iteration */ X find_enditer(); /* find it */ X if ( ( ((esp->flag1) ? esp->val1 : srch_result) >= 0) ? (~colonflag) : colonflag) /* if exit */ X pop_iteration(0); /* and exit if appropriate */ X break; X X case '\'': /* to end of conditional */ X case '|': /* to "else," or end */ X find_endcond(cmdc); X break; X X/* "F" search commands */ X X case 'b': /* bounded search, alternative args */ X do_fb(); X break; X X case 'c': /* bounded search, alternative args, then "FR" */ X if (do_fb()) goto do_fr; X while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ X break; X X case 'n': /* do "N" and then "FR" */ X if (do_nsearch('n')) goto do_fr; X while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ X break; X X case '_': /* do "_" and then "FR" */ X if (do_nsearch('_')) goto do_fr; X while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ X break; X X case 's': /* search and replace: search, then do "FR" */ X build_string(&sbuf); /* read search string and terminator */ X if (end_search( do_search( setup_search() ) )) goto do_fr; /* if search passed, do "FR" */ X while (getcmdc(trace_sw) != term_char); /* otherwise skip insert string */ X break; X case 'r': /* replace last insert, search, etc. */ X if (esp->flag1) ERROR(E_NFR); /* shouldn't have argument */ X term_char = (atflag) ? getcmdc(trace_sw) : ESC; /* set terminator */ X atflag = 0; X do_fr: /* entry from FN, F_, and FC */ X set_pointer(dot, &cc); /* save a pointer to the current spot */ X dot += ctrl_s; /* back dot up over the string */ X set_pointer(dot, &aa); /* code from "insert1": convert dot to a qp */ X delete_p = aa.p; /* save beginning of original cell */ X if (dot < buff_mod) buff_mod = dot; /* update earliest char loc touched */ X insert_p = bb.p = get_bcell(); /* get a new cell */ X bb.c = 0; X ins_count = aa.c; /* save char position of dot in cell */ X aa.c = 0; X X movenchars(&aa, &bb, ins_count); /* copy cell up to dot */ X moveuntil(&cptr, &bb, term_char, &ins_count, cptr.z-cptr.dot, trace_sw); /* insert */ X cptr.dot += ins_count; /* advance command-string pointer */ X getcmdc(trace_sw); /* skip terminator */ X X z += ctrl_s; /* subtract delete length from buffer count */ X delete_p->b->f = insert_p; /* put the new cell where the old one was */ X insert_p->b = delete_p->b; /* code borrowed from "insert2" */ X insert_p = NULL; X X if (bb.c == cc.c) /* if replacement text was same length, we can save time */ X { X for (; bb.c < CELLSIZE; bb.c++) bb.p->ch[bb.c] = cc.p->ch[bb.c]; /* copy rest of cell */ X bb.p->f = cc.p->f; /* replace orig cell's place in chain with end of new list */ X if (bb.p->f) bb.p->f->b = bb.p; X cc.p->f = NULL; /* terminate the part snipped out */ X free_blist(delete_p); /* return the old part */ X } X X else /* different positions: shift the remainder of the buffer */ X { X bb.p->f = delete_p; /* splice rest of buffer to end */ X delete_p->b = bb.p; X movenchars(&cc, &bb, z-dot); /* squeeze buffer */ X free_blist(bb.p->f); /* return unused cells */ X bb.p->f = NULL; /* and end the buffer */ X } X X z += ins_count; /* add # of chars inserted */ X dot += ins_count; X ctrl_s = -ins_count; /* save string length */ X esp->flag1 = esp->flag2 = 0; /* and consume arguments */ X esp->op = OP_START; X break; X X default: X ERROR(E_IFC); X } X } X /* routines for macro iteration */ X/* pop iteration: if arg nonzero, exit unconditionally */ X/* else check exit conditions and exit or reiterate */ X Xpop_iteration(arg) X int arg; X { X if (!arg && (!cptr.il->dflag || (--(cptr.il->count) > 0)) ) /* if reiteration */ X { X cptr.p = cptr.il->p; /* restore */ X cptr.c = cptr.il->c; X cptr.dot = cptr.il->dot; X } X else X { X if (cptr.il->b) cptr.il = cptr.il->b; /* if not last thing on stack, back up */ X else cptr.flag &= ~F_ITER; /* else clear "iteration" flag */ X } X } X X X/* find end of iteration - read over arbitrary <> and one > */ X Xfind_enditer() X { X register int icnt; X X for (icnt = 1; icnt > 0;) /* scan for matching > */ X { X while ((skipto(0) != '<') && (skipc != '>')); /* scan for next < or > */ X if (skipc == '<') ++icnt; /* and keep track of macro level */ X else --icnt; X } X } X X X X/* find end of conditional */ Xchar find_endcond(arg) X char arg; X { X register int icnt; X X for (icnt = 1; icnt > 0;) X { X while ((skipto(0) != '"') && (skipc != '\'') && (skipc != '|')); X if (skipc == '"') ++icnt; X else if (skipc == '\'') -- icnt; X else if ((icnt == 1) && (arg == '|')) break; X } X } X END_OF_te_exec2.c if test 29256 -ne `wc -c