Subject: v09i028: A TECO text editor, Part01/04 Newsgroups: mod.sources Approved: rs@mirror.TMC.COM Submitted by: genrad!mlf Mod.sources: Volume 9, Issue 28 Archive-name: teco/Part01 This is TECO for Ultrix, part 1. It consists of nine files: manpage.doc (the "man" page), teco.doc (an overview of what it is), maketeco.doc (how to build it), teco_data.doc (once through TECO's data storage), makefile, .tecorc (an example of the startup file) te_defs.h (header file), te_data.c (global data), te_main.c (main). [ I diddled with the Makefile a bit, renamed .tecorc to dot.tecorc, and packed things up into shell archives. Better documentation is encouraged -- any "old-timers" wanna write it? -- as would be support for TERMCAP or CURSES; send changes to genrad!mlf --r$ ] #! /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 1 (of 4)." # Contents: MANIFEST Makefile dot.tecorc maketeco.doc manpage.doc # te_data.c te_exec0.c te_main.c te_rdcmd.c te_utils.c teco.doc # teco_data.doc # Wrapped by rs@mirror on Thu Mar 12 19:54:25 1987 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo shar: Extracting \"MANIFEST\" \(684 characters\) if test -f MANIFEST ; then echo shar: Will not over-write existing file \"MANIFEST\" else sed "s/^X//" >MANIFEST <<'END_OF_MANIFEST' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 This shipping list X Makefile 1 X dot.tecorc 1 X maketeco.doc 1 X manpage.doc 1 X te_chario.c 2 X te_data.c 1 X te_defs.h 2 X te_exec0.c 1 X te_exec1.c 3 X te_exec2.c 3 X te_main.c 1 X te_rdcmd.c 1 X te_srch.c 2 X te_subs.c 2 X te_utils.c 1 X te_window.c 4 X teco.doc 1 X teco_data.doc 1 END_OF_MANIFEST if test 684 -ne `wc -c Makefile <<'END_OF_Makefile' X## X## TECO MAKEFILE X## X X# Debugging or optimizing? X#CFLAGS = -g XCFLAGS = -O XTERMCAP = -ltermcap X XOBJECTS = te_data.o te_exec0.o te_exec1.o te_exec2.o te_main.o te_rdcmd.o \ X te_srch.o te_subs.o te_utils.o te_window.o X Xte: $(OBJECTS) te_chario.o X $(CC) -o te $(CFLAGS) $(OBJECTS) te_chario.o $(TERMCAP) X Xtex: $(OBJECTS) te_chx.o X $(CC) -o tex $(CFLAGS) $(OBJECTS) te_chx.o $(TERMCAP) X X$(OBJECTS) te_chario.o te_chx.o: te_defs.h Xte_chx.o: te_chario.c X cp te_chario.c te_chx.c X $(CC) -DDEBUG $(CFLAGS) -c te_chx.c X END_OF_Makefile if test 518 -ne `wc -c dot.tecorc <<'END_OF_dot.tecorc' X!!!!!! THIS IS A SAMPLE FILE. REPLACE ^[ with ESCAPE, and ^A/^E with X!!!!!! THEIR REAL CONTROL CHARACTERS, AND DELETE THESE TWO LINES. X@ei// hk 64ez :qz-3"g gz j:s ^["s X0k hxz hk X:eb^Eqz^[ "e X^AFile not found: ^A :g* ^A X^A hkekex ' Xy'' 5,7:w 0,0xz X^[^[ END_OF_dot.tecorc if test 259 -ne `wc -c maketeco.doc <<'END_OF_maketeco.doc' XTECO for Ultrix Matt Fichtenbaum 3/27/86 revised 7/25/86 X X These notes describe briefly what it is and how to make it. X X Ultrix TECO is an editor that is mostly compatible with DEC XStandard TECO. It is written in C to run under Ultrix (and other XUnix (tm) implementations, e.g., it has compiled and run on a Sun Xworkstation). It implements all the editor functions of DEC TECO Xand it has some additional features suited to the Unix environment, Xsuch as the ability to execute Unix commands or to pass text from XTECO's buffer through a Unix process and incorporate the result Xinto the text buffer. It lacks some of the display support features Xof Standard TECO, but includes the split-screen "window" mode Xsuitable for using "raw" TECO. X X Note that TECO's CRT routines are hard-coded for a VT100-type Xterminal. Using another type of terminal requires changing these Xroutines. X X Ultrix TECO consists of twelve source files: X X te_defs.h definitions file #include'd by the .c files X te_data.c "common" data X te_main.c main program X te_rdcmd.c read commands and build the command string X te_exec0.c exec commands 1 (initial dispatch & expressions) X te_exec1.c exec commands 2 (most commands) X te_exec2.c exec commands 3 (E, F commands, file I/O, Unix extensions) X te_srch.c search operations X te_window.c CRT display routines X te_chario.c terminal I/O, interaction with tty driver, signals X te_subs.c higher level subroutines X te_utils.c lower level subroutines X XTECO is built as follows: X X 1. Compile all the .c files with cc -c -O .c X X 2. Link all the object files with cc -o te *.o X XNote that TECO catches the "interrupt" (^C) signal; this may cause problems when Xusing dbx. This can be circumvented by compiling te_chario.c with the symbol XDEBUG defined, i.e., X X cc -c -g -DDEBUG te_chario.c X X(naturally, the -g flag should be specified to all the cc commands Xif dbx is to be used). X X XThere are two 'makefiles.' 'makefile' makes 'te' as above; 'makefile.x' Xmakes 'te' and 'tex' for use with dbx. 'tex' compiles te_chario.c with Xthe DEBUG flag set. Because 'makefile.x' compiles with -g and 'makefile' Xdoesn't, the two ought not to share the same directory. X XNOTE: makefile.x is set up to make the -DDEBUG version of te_chario.o Xunder the name te_chx.o. The source directory should contain te_chx.c Xas a symbolic link to te_chario.c. X X The file .tecorc illustrates one possible startup. It implements X'te file' to mean X X %te X *ebfile$y5,7:w$$ X END_OF_maketeco.doc if test 2478 -ne `wc -c manpage.doc <<'END_OF_manpage.doc' XNAME X te - TECO text editor X XSYNOPSIS X te [filename] X XDESCRIPTION X te is an implementation of TECO written in C to run under X Ultrix 1.2. X X te implements DEC standard TECO, with some exceptions X described below, plus some extensions for the Ultrix X environment. X X te assumes a VT100-type terminal, and its display X driver is hard-coded for such. X XDOCUMENTATION X The DEC Standard TECO book describes the basic editing X functions of TECO. teco.doc describes the differences X between te and standard TECO, including the extensions X for the Ultrix environment. maketeco.doc describes the X files that comprise te, and the build procedure. X XFILES X ~/.tecorc editor startup file X .tmp temporary output file(s) X .bak previous version of edited file X /etc/termcap describes terminal (used only to find width of VT100) X XAUTHOR X Matt Fichtenbaum, GenRad Inc., Concord, Mass. X XBUGS X te acquires memory for its buffer using malloc(), but X never returns the space. This can cause the task in X memory to appear to be larger than it is. X X Several of the standard display features of TECO, most X notably the -256+n:W command used by interpretive editing X macros, are unimplemented. There are undoubtedly some X subtle bugs in the display handler for the features that X are implemented. X END_OF_manpage.doc if test 1285 -ne `wc -c te_data.c <<'END_OF_te_data.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_data.c global variables 12/31/85 */ X X#include "te_defs.h" X X/* error message text */ Xchar *errors[] = X { X "> not in iteration", X "Can't pop Q register", X "Can't open output file ", X "File not found ", X "Invalid E character", X "Invalid F character", X "Invalid insert arg", X "Invalid command", X "Invalid number", X "Invalid P arg", X "Invalid \" character", X "Invalid Q-reg name", X "Invalid radix arg", X "Invalid search arg", X "Invalid search string", X "Invalid ^ character", X "Insufficient memory available", X "Missing )", X "No arg before ^_", X "No arg before ,", X "No arg before =", X "No arg before )", X "No arg before \"", X "No arg before ;", X "No arg before U", X "No file for input", X "No file for output", X "Numeric arg with Y", X "Output file already open", X "Pushdown list overflow", X "Pointer off page", X "; not in iteration", X "Search failure ", X "String too long", X "Unterminated command", X "Unterminated macro", X "Execution interrupted", X "Y command suppressed", X "Invalid W arg", X "Numeric arg with FR", X "Internal error", X "EOF read from std input", X "Invalid A arg", X "Ambiguous file specification ", X "System fork or pipe error" X } ; X /* declare global variables */ X struct buffcell *freebuff = NULL; /* buffcell free-list pointer */ X struct buffcell *dly_freebuff = NULL; /* delayed free-list pointer */ X struct qp *freedcell = NULL; /* cell free-list pointer */ X X/* the text buffer header */ X struct qh buff = { NULL, NULL, 0, 0 } ; X X/* the q-register headers point to the start of the buffer and registers */ X struct qh qreg[NQREGS+5]; /* for q regs, command, search, file, sys-command, time/date */ X X/* the q-register stack contains temporary copies of q-register contents */ X struct qh qstack[QSTACKSIZE]; /* q-reg stack */ X struct qh *qsp; /* q-reg stack pointer */ X X/* the macro stack contains pointers to the currently active macros. */ X/* the top of the stack is the command pointer */ X struct qp mstack[MSTACKSIZE]; /* macro stack */ X struct qp *msp; /* macro stack pointer */ X X/* the expression stack */ X struct exp_entry estack[ESTACKSIZE]; /* expression stack */ X struct exp_entry *esp; /* expression stack pointer */ X X/* global variables, etc. */ X Xint char_count = 0; /* char count for tab typer */ Xchar lastc = ' '; /* last char read */ Xint ttyerr; /* error return from ioctl */ Xextern int errno; /* system error code */ Xstruct sgttyb ttybuf; /* local copy of tty control data */ Xjmp_buf xxx; /* preserved environment for error restart */ Xint err; /* local error code */ Xstruct qp t_qp; /* temporary buffer pointer */ Xstruct qp aa, bb, cc; /* more temporaries */ Xstruct buffcell t_bcell; /* temporary bcell */ Xint tabmask = 7; /* mask for typing tabs */ Xint exitflag; /* flag for ending command str exec */ Xchar term_char = ESC; /* terminator for insert, search, etc. */ Xchar cmdc; /* current command character */ Xchar skipc; /* char found by "skipto()" */ Xint dot, z, tdot; /* current, last, temp buffer position */ Xint ins_count; /* count of chars inserted */ Xint ll, mm, nn; /* general temps */ Xint ctrl_e = 0; /* form feed flag */ Xint ctrl_r = 10; /* numeric radix (8, 10, 16) */ Xint ctrl_s = 0; /* string length for S, I, G */ Xint ctrl_x = 0; /* search mode flag */ Xint ed_val = 0; /* ED value */ Xint es_val = 0; /* ES value */ Xint et_val = 518; /* ET value */ Xint eu_val = -1; /* EU value */ Xint ev_val = 0; /* EV value */ Xint ez_val = 0; /* EZ value */ Xint srch_result = 0; /* result of last :S executed */ Xint atflag = 0; /* flag for @ char typed */ Xint colonflag = 0; /* flag for : char typed */ Xint trace_sw = 0; /* nonzero if tracing command exec */ X Xstruct buffcell *insert_p; /* pointer to temp buffer segment during insert */ Xint buff_mod; /* set to earliest buffer change */ Xint search_flag; /* set nonzero by search */ X /* character mapping table (direct) */ Xchar mapch[] = X{ X 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* ^@ - ^M */ X16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* ^N - ^_ */ X' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', /* sp - / */ X'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', /* 0 - ? */ X'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* @ - O */ X'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',']', '^', '_', /* P - _ */ X'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* ` - o */ X'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|','}', '~', 0177 /* p - del */ X} ; X X/* character table (mapped to lower case) */ Xchar mapch_l[] = X{ X 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* ^@ - ^M */ X16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* ^N - ^_ */ X' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/', /* sp - / */ X'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', /* 0 - ? */ X'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* @ - O */ X'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\',']', '^', '_', /* P - _ */ X'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', /* ` - o */ X'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|','}', '~', 0177 /* p - del */ X} ; X /* table of special characters for "search," "skipto()," and "lines()" */ X/* see "te_defs.h for meaning of bits */ X Xchar spec_chars[] = X{ X0, A_S, 0, 0, /* ^@ ^A ^B ^C */ X0, A_A, 0, 0, /* ^D ^E ^F ^G */ X0, A_T, A_L, A_L, /* ^H ^I ^J ^K */ XA_L, 0, A_A, 0, /* ^L ^M ^N ^O */ X0, A_A, A_A, A_A, /* ^P ^Q ^R ^S */ X0, A_T|A_Q, 0, 0, /* ^T ^U ^V ^W */ XA_A, 0, 0, 0, /* ^X ^Y ^Z ^[ */ X0, 0, A_S, 0, /* ^\ ^] ^^ ^_ */ X0, A_S, A_X, 0, /* ! " # */ X0, 0, 0, A_X, /* $ % & ' */ X0, 0, 0, 0, /* ( ) * + */ X0, 0, 0, 0, /* , - . / */ X0, 0, 0, 0, /* 0 1 2 3 */ X0, 0, 0, 0, /* 4 5 6 7 */ X0, 0, 0, 0, /* 8 9 : ; */ XA_X, 0, A_X, 0, /* < = > ? */ XA_S, 0, A_E|A_F, 0, /* @ A B C */ X0, A_S, A_S, A_Q, /* D E F G */ X0, A_T|A_E, 0, 0, /* H I J K */ X0, A_Q, A_T|A_F, A_T, /* L M N O */ X0, A_Q, A_E, A_T|A_F, /* P Q R S */ X0, A_Q, 0, A_E, /* T U V W */ XA_Q, 0, 0, A_Q, /* X Y Z [ */ X0, A_Q, A_S, A_T|A_E, /* \ ] ^ _ */ X0, 0, A_E|A_F, 0, /* ` a b c */ X0, A_S, A_S, A_Q, /* d e f g */ X0, A_T|A_E, 0, 0, /* h i j k */ X0, A_Q, A_T|A_F, A_T, /* l m n o */ X0, A_Q, A_E, A_T|A_F, /* p q r s */ X0, A_Q, 0, A_E, /* t u v w */ XA_Q, 0, 0, 0, /* x y z { */ XA_X, 0, 0, 0, /* | } ~ del */ X} ; X END_OF_te_data.c if test 7350 -ne `wc -c te_exec0.c <<'END_OF_te_exec0.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_exec0.c execute command string 11/31/86 */ X#include "te_defs.h" X#include X Xexec_cmdstr() X { X char c; X int digit_sw; X struct tm *timeptr; X char *timestring, *asctime(); X struct timeval timevalue, tzvalue; X X exitflag = 0; /* set flag to "executing" */ X cmdstr.p = cbuf.f; /* cmdstr to start of command string */ X cmdstr.z = cbuf.z; X cmdstr.flag = cmdstr.dot = cmdstr.c = 0; /* clear char ptr and iteration flag */ X msp = &cmdstr; /* initialize macro stack pointer */ X esp = &estack[0]; /* initialize expression stack pointer */ X qsp = &qstack[-1]; /* initialize q-reg stack pointer */ X atflag = colonflag = 0; /* clear flags */ X esp->flag2 = esp->flag1 = 0; /* initialize expression reader */ X esp->op = OP_START; X trace_sw = 0; /* start out with trace off */ X digit_sw = 0; /* and no digits read */ X X while (!exitflag) /* until end of command string */ X { X if (getcmdc0(trace_sw) == '^') /* interpret next char as corresp. control char */ X cmdc = getcmdc(trace_sw) & 0x1f; X X if (isdigit(cmdc)) /* process number */ X { /* this works lousy for hex but so does TECO-11 */ X if (cmdc - '0' >= ctrl_r) ERROR(E_ILN); /* invalid digit */ X if (!(digit_sw++)) esp->val1 = cmdc - '0'; /* first digit */ X else esp->val1 = esp->val1 * ctrl_r + cmdc - '0'; /* other digits */ X esp->flag1++; /* indicate a value read in */ X } X X /* not a digit: dispatch on character */ X X else X { X digit_sw = 0; X switch (mapch_l[cmdc]) X { X X/* characters ignored */ X X case CR: X case LF: X case VT: X case FF: X case ' ': X break; X /* ESC: one absorbs argument, two terminate current level */ X X case ESC: X if (peekcmdc(ESC)) /* if next char is an ESC */ X { X if (msp <= &mstack[0]) exitflag = 1; /* pop stack; if empty, terminate */ X else --msp; X } X else X { X esp->flag1 = 0; /* else consume argument */ X esp->op = OP_START; X } X break; X X/* skip comments */ X X case '!': X while (getcmdc(trace_sw) != '!'); X break; X X/* modifiers */ X X case '@': X atflag++; X break; X X case ':': X if (peekcmdc(':')) /* is it "::" ? */ X { X getcmdc(trace_sw); /* yes, skip 2nd : */ X colonflag = 2; /* and set flag to show 2 */ X } X else colonflag = 1; /* otherwise just 1 colon */ X break; X X/* trace control */ X X case '?': X trace_sw = !(trace_sw); X break; X X/* values */ X X case '.': X esp->val1 = dot; X esp->flag1 = 1; X break; X X case 'z': X esp->val1 = z; X esp->flag1 = 1; X break; X X case 'b': X esp->val1 = 0; X esp->flag1 = 1; X break; X case 'h': X esp->val1 = z; X esp->val2 = 0; X esp->flag2 = esp->flag1 = 1; X esp->op = OP_START; X break; X X case CTL (S): /* -length of last insert, etc. */ X esp->val1 = ctrl_s; X esp->flag1 = 1; X break; X X case CTL (Y): /* .-^S, . */ X esp->val1 = dot + ctrl_s; X esp->val2 = dot; X esp->flag1 = esp->flag2 = 1; X esp->op = OP_START; X break; X X case '(': X if (++esp > &estack[ESTACKSIZE-1]) ERROR(E_PDO); X esp->flag2 = esp->flag1 = 0; X esp->op = OP_START; X break; X X case CTL (E): /* form feed flag */ X esp->val1 = ctrl_e; X esp->flag1 = 1; X break; X X case CTL (N): /* eof flag */ X esp->val1 = infile->eofsw; X esp->flag1 = 1; X break; X X case CTL (^): /* value of next char */ X esp->val1 = getcmdc(trace_sw); X esp->flag1 = 1; X break; X X/* date, time */ X X case CTL (B): X case CTL (H): X gettimeofday(&timevalue, &tzvalue); X timeptr = localtime(&timevalue.tv_sec); X esp->val1 = (cmdc == CTL (B)) ? X timeptr->tm_year * 512 + timeptr->tm_mon * 32 + timeptr->tm_mday : X timeptr->tm_hour * 1800 + timeptr->tm_min * 30 + timeptr->tm_sec/2; X esp->flag1 = 1; X make_buffer(&timbuf); /* make a time buffer */ X timestring = asctime(timeptr); X for (timbuf.z = 0; timbuf.z < 24; timbuf.z++) /* copy character string */ X timbuf.f->ch[timbuf.z] = *(timestring + timbuf.z); X break; X /* number of characters to matching ( ) { } [ ] */ X X case CTL (P): X do_ctlp(); X break; X X/* none of the above: incorporate the last value into the expression */ X X default: X if (esp->flag1) /* if a value entered */ X { X switch (esp->op) X { X case OP_START: X break; X X case OP_ADD: X esp->val1 += esp->exp; X esp->op = OP_START; X break; X X case OP_SUB: X esp->val1 = esp->exp - esp->val1; X esp->op = OP_START; X break; X X case OP_MULT: X esp->val1 *= esp->exp; X esp->op = OP_START; X break; X X case OP_DIV: X esp->val1 = (esp->val1) ? esp->exp / esp->val1 : 0; X esp->op = OP_START; X break; X X case OP_AND: X esp->val1 &= esp->exp; X esp->op = OP_START; X break; X X case OP_OR: X esp->val1 |= esp->exp; X esp->op = OP_START; X break; X } X } /* end of "if a new value" */ X X exec_cmds1(); /* go do the command */ X } /* end of command dispatch */ X } /* end of "not a digit" */ X } /* end of "while" command loop */ X return; X } /* end of exec_cmdstr */ X X END_OF_te_exec0.c if test 5458 -ne `wc -c te_main.c <<'END_OF_te_main.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_main.c main program 1/14/87 */ X X/* X* This is TECO for Ultrix on a Vax. It is mostly compatible with DEC TECO X* as explained in the DEC TECO manual. It was written from a manual for X* TECO-11 version 34, and so adheres most closely to that version. X* X* This program consists of several source files, as follows: X* X* te_main.c (this file) Main program - initialize, read command line and X* startup file, handle errors, high-level read and X* execute command strings. X* X* te_defs.h Definitions file, to be #included with other files X* X* te_data.c Global variables X* X* te_rdcmd.c Read in a command string X* X* te_exec0.c First-level command execution - numbers, values, X* assemble expressions X* X* te_exec1.c Most commands X* X* te_exec2.c "E" and "F" commands, and file I/O X* X* te_srch.c routines associated with "search" commands X* X* te_subs.c higher-level subroutines X* X* te_utils.c lower-level subroutines X* X* te_chario.c keyboard (stdin), typeout (stdout), suspend X* X* te_window.c display window and display special functions X* X* These routines should be compiled and linked to form the TECO executable. X*/ X X#include "te_defs.h" X Xmain(argc, argv) X int argc; /* arg count */ X char *argv[]; /* array of string pointers */ X { X int i; X X save_args(argc, argv, &qreg[36]); /* copy command line to Qz */ X read_startup(); /* read startup file */ X setup_tty(TTY_ON); /* set tty to CBREAK, no echo, asynch mode */ X window(WIN_INIT); /* initialize screen-image buffer */ X get_term_par(); /* set terminal screen-size parameters */ X X/* set up error restart */ X if (err = setjmp(xxx)) X { X if (err == E_EFI) goto quit; /* EOF from standard input - clean up and exit */ X printf("\015\012? %s", errors[err-1]); X if (err == E_SRH) print_string(SERBUF); /* print unfulfilled search string */ X else if ((err == E_FNF) || (err == E_COF) || (err == E_AMB)) print_string(FILBUF); /* or file string */ X crlf(); X eisw = 0; /* stop indirect command execution */ X et_val &= ~(ET_CTRLC | ET_NOWAIT | ET_CTRLO | ET_NOECHO); /* reset ^C trap, read w/o wait, ^O (unused), no echo */ X if (et_val & ET_QUIT) /* if ET has "quit on error" set, exit (phone home) */ X { X cleanup(); /* reset screen, keyboard, output files */ X exit(1); /* and exit */ X } X } X X/* forever: read and execute command strings */ X X for (exitflag = 1; exitflag >= 0; ) /* "exit" sets exitflag to -1; ^C to -2; "hangup" to -3 */ X { X window(WIN_REFR); /* display the buffer */ X free_blist(insert_p); /* free any storage from failed insert */ X free_blist(dly_freebuff); /* return any delayed cells */ X insert_p = dly_freebuff = NULL; X et_val &= ~ET_QUIT; /* clear "abort on error" */ X if (read_cmdstr()) goto quit; X exitflag = 0; /* enable ^C detector */ X if (!WN_scroll) window(WIN_REDRAW); /* if not in scroll mode, force full redraw on first ^W or nW */ X exec_cmdstr(); X } X X if (exitflag == -2) ERROR(E_XAB); /* ^C detected during execution */ X else if (exitflag == -3) panic(); /* hangup during execution: save buffer and close files */ X X/* exit from program */ X X quit: X ev_val = es_val = 0; /* no last one-line window */ X window(WIN_REFR); /* last display */ X cleanup(); /* reset screen, terminal, output files */ X exit(0); /* and quit */ X } X /* reset screen state, keyboard state; remove open output files */ X Xcleanup() X { X window(WIN_OFF); /* restore screen */ X setup_tty(TTY_OFF); /* restore terminal */ X kill_output(&po_file); /* kill any open primary output file */ X kill_output(&so_file); /* and secondary file */ X } X X X/* print string for error message */ X/* argument is subscript of a qreg qh, prints text from that buffer */ X Xprint_string(arg) X int arg; X { X int i, c; X struct buffcell *p; X X type_char('"'); X for (p = qreg[arg].f, c = 0, i = 0; i < qreg[arg].z; i++) X { X if (!p->ch[c]) break; X type_char(p->ch[c]); X if (++c > CELLSIZE-1) X { X p = p->f; X c = 0; X } X } X type_char('"'); X } X /* copy invocation command line to a text buffer */ X Xsave_args(argc, argv, q) X int argc; X char *argv[]; X struct qh *q; X { X char c; X struct qp ptr; X X make_buffer(q); /* attach a text buffer */ X ptr.p = q->f; /* initialize pointer to output string */ X ptr.c = q->z = 0; /* and output char count */ X for (; argc > 0; argv++, argc--) /* for each arg */ X { X while ( ((c = *((*argv)++)) != '\0') && (q->z < CELLSIZE-1) ) X { X ptr.p->ch[ptr.c] = c; /* copy char to q-reg */ X fwdcx(&ptr); X ++q->z; /* count characters */ X } X if (argc > 1) /* if not last argument... */ X { X ptr.p->ch[ptr.c] = ' '; /* space to separate arguments */ X fwdcx(&ptr); X } X } X } X X X X/* routine to read startup file */ X Xchar startup_name[] = "/.tecorc"; /* name of startup file */ X Xread_startup() X { X char *hp, *getenv(); X char fn[CELLSIZE]; /* filename storage */ X int i; X X/* look for ".tecorc" in current directory first */ X X if (!(eisw = fopen(&startup_name[1], "r"))) X { X if (hp = getenv("HOME")) /* if not found, look in home directory */ X { X for (i = 0; i < CELLSIZE; i++) if (!(fn[i] = *(hp++))) break; /* copy until trailing null */ X for (hp = &startup_name[0]; i < CELLSIZE; i++) if (!(fn[i] = *(hp++))) break; X eisw = fopen(fn, "r"); /* set eisw if file found, or not if not */ X } X } X } X /* routine to get terminal height and width from termcap */ X Xget_term_par() X { X char buff[1024]; /* termcap buffer */ X char *pname; /* pointer to name of terminal */ X extern char *getenv(); X X if (pname = getenv("TERM")) /* read terminal name */ X { X tgetent(buff, pname); /* get entry */ X set_term_par(tgetnum("li"), tgetnum("co")); /* get #lines and #columns and set params */ X } X } X END_OF_te_main.c if test 5907 -ne `wc -c te_rdcmd.c <<'END_OF_te_rdcmd.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_rdcmd.c read in the command string 12/20/85 */ X#include "te_defs.h" X Xint ccount; /* count of chars read in */ X Xint read_cmdstr() X { X char c; /* temporary character */ X int i; /* temporary */ X X goto prompt; X X restart: /* prompt again: new line */ X if (!eisw && !inp_noterm) crlf(); /* if input not from a file */ X prompt: /* issue prompt */ X if (!eisw && !inp_noterm) X { X reset_ctlo(); /* reset any ^O */ X type_char('*'); X } X ccount = 0; X lastc = ' '; X Xreline: /* continue reading */ X for (;;) /* loop to read command string chars */ X { X if (!eisw && !inp_noterm) /* if terminal input */ X { X if ((c = gettty()) == DEL) /* process rubout */ X { X if (!ccount) goto restart; /* if at beginning, ignore */ X --ccount; /* decrement char count */ X backc(&cmdstr); /* back up the command-string pointer */ X X/* look at the character just deleted */ X if (((c = cmdstr.p->ch[cmdstr.c]) < ' ') && (c != ESC)) /* control chars: set c to char erased */ X { X if (c == LF) vt(VT_LINEUP); /* line up */ X X else if ((c == CR) || (c == TAB)) X { X i = find_lasteol(); /* back up to previous line */ X type_char(CR); /* erase current line */ X vt(VT_EEOL); X if (i == ccount) type_char('*'); /* if this was line with prompt, retype the prompt */ X for (; (t_qp.p != cmdstr.p) || (t_qp.c != cmdstr.c); fwdc(&t_qp)) X type_char(t_qp.p->ch[t_qp.c]); /* retype line: stop before deleted position */ X } X X else X { X vt(VT_BS2); /* erase ordinary ctrl chars */ X char_count -= 2; X } X } X X else X { X vt(VT_BS1); /* erase printing chars */ X char_count--; X } X lastc = ' '; /* disable dangerous last chars */ X continue; X } /* end 'rubout' processing */ X X else if (c == CTL(U)) /* process "erase current line" */ X { X type_char(CR); /* erase line */ X vt(VT_EEOL); X if ((ccount -= find_lasteol()) <= 0) goto prompt; /* back up to last eol: if beginning, restart */ X cmdstr.p = t_qp.p; /* put command pointer back to this point */ X cmdstr.c = t_qp.c; X lastc = ' '; X continue; /* and read it again */ X } X X else /* not a rubout or ^U */ X { X if (!ccount) /* if at beginning of line */ X { X if (c == '*') /* save old command string */ X { X type_char('*'); /* echo character */ X type_char(c = gettty()); /* read reg spec and echo */ X i = getqspec(0, c); X free_blist(qreg[i].f); /* return its previous contents */ X qreg[i].f = cbuf.f; /* put the old command string in its place */ X qreg[i].f->b = (struct buffcell *) &qreg[i]; X qreg[i].z = cbuf.z; X cbuf.f = NULL; /* no old command string */ X err = 0; /* no previous error */ X goto restart; X } X else if ((c == '?') && (err)) /* echo previous command string up to error */ X { X type_char('?'); /* echo ? */ X for (aa.p = cptr.p; aa.p->b->b != NULL; aa.p = aa.p->b); /* find beginning */ X for (aa.c = 0; (aa.p != cptr.p) || (aa.c < cptr.c); fwdc(&aa)) type_char(aa.p->ch[aa.c]); X type_char('?'); /* a final ? */ X err = 0; /* reset error switch */ X goto restart; X } X else if ((c == LF) || (c == CTL (H))) /* line feed, backspace */ X { X dot += lines( (c == LF) ? 1 : -1); /* pointer up or down one line */ X window(WIN_LINE); /* display one line */ X goto restart; X } X X else /* first real command on a line */ X { X make_buffer(&cbuf); /* start a command string if need be */ X cmdstr.p = cbuf.f; /* set cmdstr to point to start of command string */ X cmdstr.c = 0; X cbuf.z = 0; /* no chars in command string now */ X err = 0; /* clear last error flag */ X } X } /* end of "if first char on line" */ X X X/* check ^G-something */ X X if (lastc == CTL (G)) X { X if (c == CTL(G)) X { X cbuf.z = ccount; /* save count for possible "save in q-reg" */ X goto restart; X } X if ((c == '*') || (c == ' ')) X { X backc(&cmdstr); /* remove the previous ^G from buffer */ X --ccount; X crlf(); X retype_cmdstr(c); /* retype appropriate part of command string */ X lastc = ' '; X continue; X } /* end of ^G* and ^G processing */ X } /* end of "last char was ^G" */ X } /* end of "not rubout or ^U */ X } /* end of "if !eisw" */ X X else /* if input from indirect file or redirected std input */ X { X if (!ccount) /* first command? */ X { X if (!cbuf.f) /* start a command string if need be */ X { X cbuf.f = get_bcell(); X cbuf.f->b = (struct buffcell *) &cbuf; X } X cmdstr.p = cbuf.f; /* point cmdstr to start of command string */ X cbuf.z = cmdstr.c = 0; X } X X c = (eisw) ? getc(eisw) : gettty() ; /* get char */ X if (eisw && (c == EOF)) /* if this is end of the indirect command file */ X { X fclose(eisw); /* close the input file */ X eisw = 0; /* reset the switch */ X lastc = ' '; X continue; /* and go read more chars */ X } X else X { X if ((c == LF) && (lastc != CR) && !(ez_val & EZ_CRLF)) /* LF: store implied CR first */ X { X cmdstr.p->ch[cmdstr.c] = CR; X ++ccount; X fwdcx(&cmdstr); X } X } X } /* end of "if redirected std in or eisw" */ X X X/* store character in command string */ X X cmdstr.p->ch[cmdstr.c] = c; /* store the character */ X ++ccount; /* keep count of chars */ X if (!eisw && !inp_noterm) type_char(c); /* echo the character */ X fwdcx(&cmdstr); /* next char pos'n; extend command string if nec */ X X if ((c == ESC) && (lastc == ESC)) break; /* stop on 2nd ESC */ X if ((c == CTL (C)) && (lastc == CTL (C))) return(-1); /* immediate exit */ X lastc = c; /* keep track of last char */ X } /* end of read-char loop */ X X cbuf.z = ccount; /* indicate number of chars in command string */ X if (!eisw && !inp_noterm) crlf(); /* final new-line */ X return(0); X } /* end of read_cmdstr() */ X X/* back up to find most recent CR or LF in entered command string */ X/* return number of chars backed up */ X Xint find_lasteol() X { X int i; X X for (i = 0, t_qp.p = cmdstr.p, t_qp.c = cmdstr.c; (backc(&t_qp)) ; i++) /* look for beg. of line */ X { X if ((t_qp.p->ch[t_qp.c] == CR) || (t_qp.p->ch[t_qp.c] == LF)) X { X fwdc(&t_qp); /* stop short of previous eol */ X break; X } X } X char_count = 0; /* reset tab count */ X return(i); X } X X X X/* retype command string: entirely (arg = '*') or most recent line (arg = ' ') */ X Xretype_cmdstr(c) X char c; X { X int i; X X if (!inp_noterm) /* if input is really from terminal */ X { X if (c == ' ') /* look for beginning of this line */ X i = ccount - find_lasteol(); /* to last eol, and count char's backed up */ X else X { X t_qp.p = cbuf.f; /* retype whole command string */ X i = t_qp.c = 0; X } X if (!i) type_char('*'); /* if from beginning, retype prompt */ X for (; i < ccount; i++) /* type command string from starting point */ X { X type_char(t_qp.p->ch[t_qp.c]); X fwdc(&t_qp); X } X } X } X END_OF_te_rdcmd.c if test 7303 -ne `wc -c te_utils.c <<'END_OF_te_utils.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_utils.c utility subroutines 10/28/85 */ X X#include "te_defs.h" X X/* routines to handle storage */ X/* get a buffcell */ X/* if there are no buffcells available, call malloc for more storage */ X Xstruct buffcell *get_bcell() X { X char *malloc(); X struct buffcell *p; X int i; X X if (freebuff == NULL) X { X p = (struct buffcell *) malloc(BLOCKSIZE); X if (!p) ERROR(E_MEM); X else X { X freebuff = p; /* take the given address as storage */ X for (i = 0; i < (BLOCKSIZE/sizeof(struct buffcell)) - 1; i++) /* for all cells in the new block */ X (p+i)->f = p+i+1; /* chain the forward pointers together */ X (p+i)->f = NULL; /* make the last one's forward pointer NULL */ X } X } X X p = freebuff; /* cut out head of "free" list */ X freebuff = freebuff->f; X p->f = p->b = NULL; X return(p); X } X X X/* free a list of buffcells */ Xfree_blist(p) X struct buffcell *p; X { X struct buffcell *t; X X if (p != NULL) X { X for (t = p; t -> f != NULL; t = t -> f); /* find end of ret'd list */ X t->f = freebuff; /* put ret'd list at head of "free" list */ X freebuff = p; X } X } X /* free a list of buffcells to the "delayed free" list */ Xdly_free_blist(p) X struct buffcell *p; X { X struct buffcell *t; X X if (p != NULL) X { X for (t = p; t -> f != NULL; t = t -> f); /* find end of ret'd list */ X t->f = dly_freebuff; /* put ret'd list at head of "free" list */ X dly_freebuff = p; X } X } X X X X/* get a cell */ X/* if there are no cells available, get a buffcell and make more */ X Xstruct qp *get_dcell() X { X struct qp *t; X int i; X X if (freedcell == NULL) X { X t = freedcell = (struct qp *) get_bcell(); /* get a buffcell */ X for (i = 0; i < (sizeof(struct buffcell)/sizeof(struct qp)) - 1; i++) X { X (t+i)->f = t+i+1; /* chain the fwd pointers together */ X (t+i)->f = NULL; /* make the last one's forward pointer NULL */ X } X } X X t = freedcell; /* cut out head of "free" list */ X freedcell = (struct qp *) freedcell->f; X t->f = NULL; X return(t); X } X X X/* free a list of cells */ Xfree_dlist(p) X struct qp *p; X { X struct qp *t; X X if (p != NULL) X { X for (t = p; t->f != NULL; t = t->f); /* find end of ret'd list */ X t->f = freedcell; /* put ret'd list at head of "free" list */ X freedcell = p; X } X } X /* build a buffer: called with address of a qh */ X/* if no buffer there, get a cell and link it in */ Xmake_buffer(p) X struct qh *p; X { X if (!(p->f)) X { X p->f = get_bcell(); X p->f->b = (struct buffcell *) p; X } X } X X X/* routines to advance one character forward or backward */ X/* argument is the address of a qp */ X/* fwdc, backc return 1 if success, 0 if beyond extremes of buffer) */ X/* fwdcx extends buffer if failure */ X Xint fwdc(arg) X struct qp *arg; X { X if ((*arg).c >= CELLSIZE-1) /* test char count for max */ X { X if ((*arg).p->f == NULL) return(0); /* last cell: fail */ X else X { X (*arg).p = (*arg).p->f; /* chain through list */ X (*arg).c = 0; /* and reset char count */ X } X } X else ++(*arg).c; /* otherwise just incr char count */ X ++(*arg).dot; X return(1); X } X Xfwdcx(arg) X struct qp *arg; X { X if ((*arg).c >= CELLSIZE-1) /* test char count for max */ X { X if ((*arg).p->f == NULL) /* last cell: extend */ X { X (*arg).p->f = get_bcell(); X (*arg).p->f->b = (*arg).p; X } X (*arg).p = (*arg).p->f; /* chain through list */ X (*arg).c = 0; /* and reset char count */ X } X else ++(*arg).c; /* otherwise just incr char count */ X ++(*arg).dot; X return(1); X } X int backc(arg) X struct qp *arg; X { X if ((*arg).c <= 0) /* test char count for min */ X { X if ((*arg).p->b->b == NULL) return(0); /* first cell: fail */ X else X { X (*arg).p = (*arg).p->b; /* chain through list */ X (*arg).c = CELLSIZE-1; /* reset char count */ X } X } X else --(*arg).c; /* otherwise just decr char count */ X --(*arg).dot; X return(1); X } X X X/* set up a pointer to a particular text buffer position */ X Xset_pointer(pos, ptr) /* first arg is position, 2nd is addr of pointer */ X int pos; X struct qp *ptr; X { X struct buffcell *t; X int i; X X if (!buff.f) X { X buff.f = get_bcell(); /* if no text buffer, make one */ X buff.f->b = (struct buffcell *) &buff; X } X for (i = pos/CELLSIZE, t = buff.f; (i > 0) && (t->f != NULL); i--) t = t->f; X ptr->p = t; X ptr->c = pos % CELLSIZE; X ptr->dot = pos; X ptr->z = z; X } X /* routines to get next character from command buffer */ X/* getcmdc0, when reading beyond command string, pops */ X/* macro stack and continues. */ X/* getcmdc, in similar circumstances, reports an error */ X/* if pushcmdc() has returned any chars, read them first */ X/* routines type characters as read, if argument != 0 */ X Xchar getcmdc0(trace) X { X while (cptr.dot >= cptr.z) /* if at end of this level, pop macro stack */ X { X if (--msp < &mstack[0]) /* pop stack; if top level */ X { X msp = &mstack[0]; /* restore stack pointer */ X cmdc = ESC; /* return an ESC (ignored) */ X exitflag = 1; /* set to terminate execution */ X return(cmdc); /* exit "while" and return */ X } X } X cmdc = cptr.p->ch[cptr.c++]; /* get char */ X ++cptr.dot; /* increment character count */ X if (trace) type_char(cmdc); /* trace */ X if (cptr.c > CELLSIZE-1) /* and chain if need be */ X { X cptr.p = cptr.p->f; X cptr.c = 0; X } X return(cmdc); X } X X Xchar getcmdc(trace) X { X if (cptr.dot++ >= cptr.z) ERROR((msp <= &mstack[0]) ? E_UTC : E_UTM); X else X { X cmdc = cptr.p->ch[cptr.c++]; /* get char */ X if (trace) type_char(cmdc); /* trace */ X if (cptr.c > CELLSIZE-1) /* and chain if need be */ X { X cptr.p = cptr.p->f; X cptr.c = 0; X } X } X return(cmdc); X } X X X/* peek at next char in command string, return 1 if it is equal (case independent) to argument */ Xint peekcmdc(arg) X char arg; X { X return(((cptr.dot < cptr.z) && (mapch_l[cptr.p->ch[cptr.c]] == mapch_l[arg])) ? 1 : 0); X } X END_OF_te_utils.c if test 5967 -ne `wc -c teco.doc <<'END_OF_teco.doc' XTECO for Ultrix Matt Fichtenbaum January 12, 1987 X X This TECO is almost compatible with "Standard TECO" as described in XDEC literature. It differs in its display support and interaction with the Xoperating system, and in the omission of a few less-frequently-used features. XA few of its features are extensions not found in Standard TECO. X X The following is a brief comparison between this TECO and Standard XTECO version 34, the last for which I have notes. X X XMissing features: X X ^F (console switch register) X EG (exit and execute system command) X EH (help flag) X EO (version) X ET & 16 (cancel ^O on output) X ET & 64 (detach) X EU (upper/lower case flag) X backward paging in file (negative arguments to P, N, etc.) X X XNew features: X X EQtext$ pass "text" as a command to the shell. The command is executed X or in a Unix subshell, with standard input, output, and error X @EQ/text/ connected to the terminal (i.e., unchanged from TECO's setup). X X nEQtext$ pass "text" as a command to the shell. The command is executed X or in a Unix subshell. The command's standard input is the X m,nEQtext$ specified text ("n" lines or "m,n" characters) from TECO's X (and "@" buffer, and its standard output replaces the text in q-register X forms) q#. This form of the EQ command thus passes text from TECO's X buffer through a Unix command and makes the result available to X TECO. (The pointer is left after the specified text and ^S is X set appropriately, so ^SDG# after m,nEQcommand$ replaces the X original text with the command's output) X X EQ commands execute same command as previous EQ. The presence of arguments X with empty specifies the command's standard input and output, as above. X string (EQ$) X X ^P if the pointer is positioned on one of the characters X ( ) { } [ ] < > " ' then ^P has the value of the number X of characters to the matching character. Otherwise ^P has X value 0. Thus ^PC performs the same function as "%" in vi. X (Note: " is considered to match ' ) X X Q% buffer containing last Unix command string from EQ command X X Q# buffer containing ASCII text from Unix. After the commands X "nEQ" and "m,nEQ" this is the standard output from the X invoked command; after ^B or ^H this is the date and time X expressed as a character string. X X Text buffer of arbitrary size, using Ultrix virtual memory. X EZ word with some unrelated one-bit flags, as follows: X X 1 0: read Unix files: '\n' in file becomes in X TECO buffer, converted back to '\n' on write. X 1: read files literally without this conversion. X X 2 0: read from file terminates on form feed or EOF X 1: read terminates on EOF only; FFs are put into the buffer X X 4 0: tabs are displayed every 8 columns X 1: tabs are displayed every 4 columns X X 8 0: EWfile$ and EBfile$ open "file.tmp" as output file; X this is renamed to "file" when output file is closed. X 1: EWfile$ opens "file" as named - any preexisting file X of this name is immediately deleted. EBfile$ always X opens "file.tmp." X (This form must be used when the output is to a non- X directory device, e.g. /dev/tty) X X 16 0: text$ inserts a tab, then text X 1: is ignored as a command X X 32 0: , and count as line separators for commands X that count lines (nL, nXq, etc.) X 1: only counts as a line separator X X 64 0: ER or EB commands that specify multiple files cause an error X 1: These commands open the first specified files; other files X may be accessed with the EN$ command (see File Expansion, below) X X XThings that work differently X X startup this TECO looks for a file called ".tecorc" in the user's X home directory and then in the current directory. TECO X does no processing of command line arguments, but the X entire command line is passed to .tecorc in Q-register Z. X .tecorc may treat the command line as it wishes. X X EB EBfile$ opens "file.tmp" for output. When the file is X successfully closed (EC, EF, EX), the original file is X renamed with a ".bak" suffix and the ".tmp" file is X renamed with the original name (see bit "8" of variable EZ). X X ED & 4 When this bit is set, a command that reads the input file X (P, Y, A) stops reading when the number of characters in X the buffer exceeds 16384. When this bit is clear, the X buffer is expanded without limit. X X EN ENtext$ or @EN/text/ specifies a file specification that is X passed to the shell for expansion into one or more file names. X EN$ or @EN//, with no string specified, fetches the next file X name into the filespec buffer. If there are no more files X :EN returns 0, EN causes an error. X Filename When specified in ER or EB commands, filenames that begin with X expansion the character ~, or that contain any of the characters * ? { [, X are passed to the shell to be expanded. If exactly one file X matches the specification this file is opened. If more than one X file matches, the action depends on the setting of bit 64 in X variable EZ: if this bit is 0, TECO reponds with an "ambiguous X file specification" error; if this bit is 1, TECO substitutes X the list of files for the list resulting from the last EN command, X if any, and opens the first file on the list. Subsequent entries on X this list are fetched to the filespec buffer by the EN$ command, X as above. EW (output) and EI (indirect command execution) commands X are not expanded in this manner. X X window this TECO's window support is hard-coded for a VT100-type X terminal. It includes many but not all of the window X functions of Standard TECO. The following is a summary of X the display features: X X 0:w (terminal type) always 4 (VT100 in ANSI mode) X 1:w (terminal width) obtained from Ultrix at startup X 2:w (terminal height) obtained from Ultrix at startup X 3:w (seeall mode) unimplemented X 4:w (mark) unimplemented X 5:w (hold mode) unimplemented X 6:w (screen origin) read-only parameter X 7:w (split-screen mode) implemented X X ^W immediate absolute window redraw X X nW (n not 0 or -1000) immediate window redraw, incremental X unless other output commands have been executed and scroll X mode was disabled X X -1000W "forget" that output has occurred X X Note: window functions using other "special" values as arguments X to nW are not implemented. X X EV, ES argument to these is the number of lines to display X (nEV displays 2N-1 lines about the line containing X the pointer). The character at the pointer is X shown in reverse video. X X XInteractions with Ultrix X X Terminal I/O uses stdin and stdout; numerous ioctl's are used to Xadjust the terminal driver's modes. File I/O uses the buffered I/O Xfunctions of . X X The time (^H) and date (^B) make use of the system call X"gettimeofday" (2). X X The EQ and EN commands and the expansion of filenames for the ER Xand EB commands spawn child processes that use pipes for interprocess Xcommunication. X Teco explicitly handles the following signals: X X SIGINT (interrupt) The "interrupt" character (^C or equivalent) X interrupts an executing Teco command string or macro, and X gives the error "Execution interrupted." When no command X string is executing this character is not treated specially. X X SIGTSTP (suspend) Teco responds to this signal by restoring X the terminal characteristics to their original values and X then suspending itself. X X SIGHUP (hangup) Teco responds to this signal by attempting X to save the text buffer and any open output files before X exiting. If there is any text in the buffer, it is written X to the currently- selected output file; if there is no file, X the buffer is written to the file TECO_SAVED.tmp. Any open X output files are closed, retaining their ".tmp" suffixes. X Unread input is not copied to the output files. X END_OF_teco.doc if test 7799 -ne `wc -c teco_data.doc <<'END_OF_teco_data.doc' XA few quick comments on the ways teco arranges its internal Xdata storage X X Teco has two classes of buffer: X 1. Text buffer (the text buffer, q-registers, the command string) X 2. Other (buffer header, buffer pointer, iteration-stack entry) X X XText buffers X X Each text buffer is a linked list of cells; this is a convenient Xway to divide a single address space among an arbitrary number of Xbuffers of arbitrary size. Each cell ("struct buffcell") contains a Xforward pointer, a backward pointer, and an array of characters. XThe teco buffer, each q-register, each entry on the q-register Xpushdown list, and the command string are lists of these cells. X X Teco maintains a list of free buffer cells, and its storage Xallocation routines furnish cells when needed and accept returned Xones. If the list of free cells is empty, teco calls malloc() to Xget a block of storage which it then divides into cells. Teco never Xreturns the storage it gets from malloc(). X X The text buffer is kept in packed form: each cell but the last Xcontains the full number of characters. This makes it easy for teco Xto find its way through the buffer but increases the time taken by Xinsert and delete. It might be interesting to add a "count" field Xto the buffer cell and allow cells within the buffer to have empty Xspaces at the end; this is a possible future modification. X X XOther X X Teco uses three types of small data cell - the q-register header X("struct qh"), the q-register pointer ("struct qp"), and the macro Xiteration list entry ("struct is"). Although defined as separate Xstructures, these are really the same basic cell: they are kept as Xa list of free cells and cast to the proper definition when needed. X X As in the case of text-buffer cells, data cells are supplied by and Xreturned to teco's storage allocation routines. When a data cell is Xneeded and no free cells are available, the storage allocator obtains Xa text buffer cell and carves it up into smaller cells, thus, the text- Xbuffer cell system is the only interface to malloc(). X X A text-buffer header ("struct qh") consists of a forward pointer that Xpoints to the first cell of the buffer, a backward pointer that is NULL, Xa count of characters, and an integer value - the "value" of a numeric Xq-register. The teco buffer has a named header named buff; the q-registers Xhave an array, named qreg[], of headers. X X NOTE that the "struct qh" and the "struct buffcell" begin X identically: the forward and backward pointers are in X the same locations in both structures. Teco uses this in X handling lists: because the buffer header appears to be a X buffer cell, inserting a cell at the beginning of a list is X the same as inserting one farther down. This is probably a X bad implementation and may not work on all machines. X X A q-register pointer ("struct qp") points to a place within a text Xbuffer or q-register or the command string. It has a pointer to the Xcurrent buffer cell, a value of the current character within the cell, Xvalues for the number of characters in the buffer and the number of the Xcurrent character, and, if it is a macro-stack entry, a pointer to the Xiteration stack for that macro. X X A number of q-register pointers are used as general temporaries, for Xmoving text within the buffer, outputting text, etc. A stack of these Xpointers, the macro stack mstack[], controls the execution of command Xstrings and macros. The stack's first entry always points to the command Xstring, and a new entry is pushed each time a macro is invoked and is Xpopped when the macro terminates. X X An iteration-stack entry ("struct is") maintains the data needed for Xcommand-string or macro iteration. Each command or macro level can Xhave an iteration stack associated with it: this is kept as a linked Xlist of iteration-stack entries, and the macro stack entry contains a Xpointer to the head of the list. Each iteration-stack entry contains Xforward and backward pointers for the iteration stack, a pointer X(cell address, character offset, absolute character count) to the Xcommand that starts the iteration, a count of iterations remaining, Xand a flag for definite/indefinite iteration. X X An iteration stack is created for a particular command or macro level Xthe first time that a loop is encountered at that level. The cells of Xthe stack are not reclaimed but are reused the next time an iteration Xoccurs at that command level. X X The data definitions specify two more data-structure types, a macro Xstack entry ("struct ms") and a buffer header list entry ("struct bh"). XThese are not used at present. X END_OF_teco_data.doc if test 4567 -ne `wc -c