allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (08/20/89)
Posting-number: Volume 8, Issue 5 Submitted-by: tony@cs.utexas.edu@wldrdg.UUCP (Tony Andrews) Archive-name: stevie3.68/part03 #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # normal.c # ops.c # ops.h # param.c # param.h # ptrfunc.c # This archive created: Sun Aug 13 11:46:00 1989 export PATH; PATH=/bin:$PATH echo shar: extracting "'normal.c'" '(16909 characters)' if test -f 'normal.c' then echo shar: will not over-write existing file "'normal.c'" else sed 's/^X//' << \HE_HATES_THESE_CANS > 'normal.c' X/* $Header: /nw/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $ X * X * Contains the main routine for processing characters in command mode. X * Communicates closely with the code in ops.c to handle the operators. X */ X X#include "stevie.h" X#include "ops.h" X X/* X * Generally speaking, every command in normal() should either clear any X * pending operator (with CLEAROP), or set the motion type variable. X */ X X#define CLEAROP (operator=NOP) /* clear any pending operator */ X Xint operator = NOP; /* current pending operator */ Xint mtype; /* type of the current cursor motion */ Xbool_t mincl; /* true if char motion is inclusive */ XLPTR startop; /* cursor pos. at start of operator */ X X/* X * Operators can have counts either before the operator, or between the X * operator and the following cursor motion as in: X * X * d3w or 3dw X * X * If a count is given before the operator, it is saved in opnum. If X * normal() is called with a pending operator, the count in opnum (if X * present) overrides any count that came later. X */ Xstatic int opnum = 0; X X#define DEFAULT1(x) (((x) == 0) ? 1 : (x)) X X/* X * normal(c) X * X * Execute a command in command mode. X * X * This is basically a big switch with the cases arranged in rough categories X * in the following order: X * X * 1. File positioning commands X * 2. Control commands (e.g. ^G, Z, screen redraw, etc) X * 3. Character motions X * 4. Search commands (of various kinds) X * 5. Edit commands (e.g. J, x, X) X * 6. Insert commands (e.g. i, o, O, A) X * 7. Operators X * 8. Abbreviations (e.g. D, C) X * 9. Marks X */ Xvoid Xnormal(c) Xregister int c; X{ X register int n; X register char *s; /* temporary variable for misc. strings */ X bool_t flag = FALSE; X int type = 0; /* used in some operations to modify type */ X int dir = FORWARD; /* search direction */ X int nchar = NUL; X bool_t finish_op; X X /* X * If there is an operator pending, then the command we take X * this time will terminate it. Finish_op tells us to finish X * the operation before returning this time (unless the operation X * was cancelled. X */ X finish_op = (operator != NOP); X X /* X * If we're in the middle of an operator AND we had a count before X * the operator, then that count overrides the current value of X * Prenum. What this means effectively, is that commands like X * "3dw" get turned into "d3w" which makes things fall into place X * pretty neatly. X */ X if (finish_op) { X if (opnum != 0) X Prenum = opnum; X } else X opnum = 0; X X u_lcheck(); /* clear the "line undo" buffer if we've moved */ X X switch (c & 0xff) { X X /* X * Screen positioning commands X */ X case CTRL('D'): X CLEAROP; X if (Prenum) X P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum; X scrollup(P(P_SS)); X onedown(P(P_SS)); X updatescreen(); X break; X X case CTRL('U'): X CLEAROP; X if (Prenum) X P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum; X scrolldown(P(P_SS)); X oneup(P(P_SS)); X updatescreen(); X break; X X /* X * This is kind of a hack. If we're moving by one page, the calls X * to stuffin() do exactly the right thing in terms of leaving X * some context, and so on. If a count was given, we don't have X * to worry about these issues. X */ X case CTRL('F'): X CLEAROP; X n = DEFAULT1(Prenum); X if (n > 1) { X if ( ! onedown(Rows * n) ) X beep(); X cursupdate(); X } else { X screenclear(); X stuffin("Lz\nM"); X } X break; X X case CTRL('B'): X CLEAROP; X n = DEFAULT1(Prenum); X if (n > 1) { X if ( ! oneup(Rows * n) ) X beep(); X cursupdate(); X } else { X screenclear(); X stuffin("Hz-M"); X } X break; X X case CTRL('E'): X CLEAROP; X scrollup(DEFAULT1(Prenum)); X updatescreen(); X break; X X case CTRL('Y'): X CLEAROP; X scrolldown(DEFAULT1(Prenum)); X updatescreen(); X break; X X case 'z': X CLEAROP; X switch (vgetc()) { X case NL: /* put Curschar at top of screen */ X case CR: X *Topchar = *Curschar; X Topchar->index = 0; X updatescreen(); X break; X X case '.': /* put Curschar in middle of screen */ X n = Rows/2; X goto dozcmd; X X case '-': /* put Curschar at bottom of screen */ X n = Rows-1; X /* fall through */ X X dozcmd: X { X register LPTR *lp = Curschar; X register int l = 0; X X while ((l < n) && (lp != NULL)) { X l += plines(lp); X *Topchar = *lp; X lp = prevline(lp); X } X } X Topchar->index = 0; X updatescreen(); X break; X X default: X beep(); X } X break; X X /* X * Control commands X */ X case ':': X CLEAROP; X if ((s = getcmdln(c)) != NULL) X docmdln(s); X break; X X case K_HELP: X CLEAROP; X if (help()) { X screenclear(); X updatescreen(); X } X break; X X case CTRL('L'): X CLEAROP; X screenclear(); X updatescreen(); X break; X X X case CTRL('O'): /* ignored */ X /* X * A command that's ignored can be useful. We use it at X * times when we want to postpone redraws. By stuffing X * in a control-o, redraws get suspended until the editor X * gets back around to processing input. X */ X break; X X case CTRL('G'): X CLEAROP; X fileinfo(); X break; X X case K_CGRAVE: /* shorthand command */ X CLEAROP; X stuffin(":e #\n"); X break; X X case 'Z': /* write, if changed, and exit */ X if (vgetc() != 'Z') { X beep(); X break; X } X doxit(); X break; X X /* X * Macro evaluates true if char 'c' is a valid identifier character X */ X# define IDCHAR(c) (isalpha(c) || isdigit(c) || (c) == '_') X X case CTRL(']'): /* :ta to current identifier */ X CLEAROP; X { X char ch; X LPTR save; X X save = *Curschar; X /* X * First back up to start of identifier. This X * doesn't match the real vi but I like it a X * little better and it shouldn't bother anyone. X */ X ch = gchar(Curschar); X while (IDCHAR(ch)) { X if (!oneleft()) X break; X ch = gchar(Curschar); X } X if (!IDCHAR(ch)) X oneright(); X X stuffin(":ta "); X /* X * Now grab the chars in the identifier X */ X ch = gchar(Curschar); X while (IDCHAR(ch)) { X stuffin(mkstr(ch)); X if (!oneright()) X break; X ch = gchar(Curschar); X } X stuffin("\n"); X X *Curschar = save; /* restore, in case of error */ X } X break; X X /* X * Character motion commands X */ X case 'G': X mtype = MLINE; X *Curschar = *gotoline(Prenum); X beginline(TRUE); X break; X X case 'H': X mtype = MLINE; X *Curschar = *Topchar; X for (n = Prenum; n && onedown(1) ;n--) X ; X beginline(TRUE); X break; X X case 'M': X mtype = MLINE; X *Curschar = *Topchar; X for (n = 0; n < Rows/2 && onedown(1) ;n++) X ; X beginline(TRUE); X break; X X case 'L': X mtype = MLINE; X *Curschar = *prevline(Botchar); X for (n = Prenum; n && oneup(1) ;n--) X ; X beginline(TRUE); X break; X X case 'l': X case K_RARROW: X case ' ': X mtype = MCHAR; X mincl = FALSE; X n = DEFAULT1(Prenum); X while (n--) { X if ( ! oneright() ) X beep(); X } X set_want_col = TRUE; X break; X X case 'h': X case K_LARROW: X case CTRL('H'): X mtype = MCHAR; X mincl = FALSE; X n = DEFAULT1(Prenum); X while (n--) { X if ( ! oneleft() ) X beep(); X } X set_want_col = TRUE; X break; X X case '-': X flag = TRUE; X /* fall through */ X X case 'k': X case K_UARROW: X case CTRL('P'): X mtype = MLINE; X if ( ! oneup(DEFAULT1(Prenum)) ) X beep(); X if (flag) X beginline(TRUE); X break; X X case '+': X case CR: X case NL: X flag = TRUE; X /* fall through */ X X case 'j': X case K_DARROW: X case CTRL('N'): X mtype = MLINE; X if ( ! onedown(DEFAULT1(Prenum)) ) X beep(); X if (flag) X beginline(TRUE); X break; X X /* X * This is a strange motion command that helps make operators X * more logical. It is actually implemented, but not documented X * in the real 'vi'. This motion command actually refers to "the X * current line". Commands like "dd" and "yy" are really an alternate X * form of "d_" and "y_". It does accept a count, so "d3_" works to X * delete 3 lines. X */ X case '_': X lineop: X mtype = MLINE; X onedown(DEFAULT1(Prenum)-1); X break; X X case '|': X mtype = MCHAR; X mincl = TRUE; X beginline(FALSE); X if (Prenum > 0) X *Curschar = *coladvance(Curschar, Prenum-1); X Curswant = Prenum - 1; X break; X X /* X * Word Motions X */ X X case 'B': X type = 1; X /* fall through */ X X case 'b': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X if ((pos = bck_word(Curschar, type)) == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case 'W': X type = 1; X /* fall through */ X X case 'w': X /* X * This is a little strange. To match what the real vi X * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'. X * This seems impolite at first, but it's really more X * what we mean when we say 'cw'. X */ X if (operator == CHANGE) X goto doecmd; X X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X if ((pos = fwd_word(Curschar, type)) == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case 'E': X type = 1; X /* fall through */ X X case 'e': X doecmd: X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X /* X * The first motion gets special treatment if we're X * do a 'CHANGE'. X */ X if (n == DEFAULT1(Prenum)) X pos = end_word(Curschar,type,operator==CHANGE); X else X pos = end_word(Curschar, type, FALSE); X X if (pos == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case '$': X mtype = MCHAR; X mincl = TRUE; X while ( oneright() ) X ; X Curswant = 999; /* so we stay at the end */ X break; X X case '^': X mtype = MCHAR; X mincl = FALSE; X beginline(TRUE); X break; X X case '0': X mtype = MCHAR; X mincl = TRUE; X beginline(FALSE); X break; X X /* X * Searches of various kinds X */ X case '?': X case '/': X s = getcmdln(c); /* get the search string */ X X /* X * If they backspaced out of the search command, X * just bag everything. X */ X if (s == NULL) { X CLEAROP; X break; X } X X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X X /* X * If no string given, pass NULL to repeat the prior search. X * If the search fails, abort any pending operator. X */ X if (!dosearch( X (c == '/') ? FORWARD : BACKWARD, X (*s == NUL) ? NULL : s X )) X CLEAROP; X break; X X case 'n': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (!repsearch(0)) X CLEAROP; X break; X X case 'N': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (!repsearch(1)) X CLEAROP; X break; X X /* X * Character searches X */ X case 'T': X dir = BACKWARD; X /* fall through */ X X case 't': X type = 1; X goto docsearch; X X case 'F': X dir = BACKWARD; X /* fall through */ X X case 'f': X docsearch: X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X if ((nchar = vgetc()) == ESC) /* search char */ X break; X X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X if (!searchc(nchar, dir, type)) { X CLEAROP; X beep(); X } X } X break; X X case ',': X flag = 1; X /* fall through */ X X case ';': X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X if (!crepsearch(flag)) { X CLEAROP; X beep(); X } X } X break; X X case '[': /* function searches */ X dir = BACKWARD; X /* fall through */ X X case ']': X mtype = MLINE; X set_want_col = TRUE; X if (vgetc() != c) { X beep(); X CLEAROP; X break; X } X X if (!findfunc(dir)) { X beep(); X CLEAROP; X } X break; X X case '%': X mtype = MCHAR; X mincl = TRUE; X { X LPTR *pos; X X if ((pos = showmatch()) == NULL) { X beep(); X CLEAROP; X } else { X setpcmark(); X *Curschar = *pos; X set_want_col = TRUE; X } X } X break; X X /* X * Edits X */ X case '.': /* repeat last change (usually) */ X /* X * If a delete is in effect, we let '.' help out the same X * way that '_' helps for some line operations. It's like X * an 'l', but subtracts one from the count and is inclusive. X */ X if (operator == DELETE || operator == CHANGE) { X if (Prenum != 0) { X n = DEFAULT1(Prenum) - 1; X while (n--) X if (! oneright()) X break; X } X mtype = MCHAR; X mincl = TRUE; X } else { /* a normal 'redo' */ X CLEAROP; X stuffin(Redobuff); X } X break; X X case 'u': X case K_UNDO: X CLEAROP; X u_undo(); X break; X X case 'U': X CLEAROP; X u_lundo(); X break; X X case 'x': X CLEAROP; X if (lineempty()) /* can't do it on a blank line */ X beep(); X if (Prenum) X stuffnum(Prenum); X stuffin("d."); X break; X X case 'X': X CLEAROP; X if (!oneleft()) X beep(); X else { X strcpy(Redobuff, "X"); X u_saveline(); X delchar(TRUE); X updateline(); X } X break; X X case 'r': X CLEAROP; X if (lineempty()) { /* Nothing to replace */ X beep(); X break; X } X if ((nchar = vgetc()) == ESC) X break; X X if ((nchar & 0x80) || nchar == CR || nchar == NL) { X beep(); X break; X } X u_saveline(); X X /* Change current character. */ X pchar(Curschar, nchar); X X /* Save stuff necessary to redo it */ X sprintf(Redobuff, "r%c", nchar); X X CHANGED; X updateline(); X break; X X case '~': /* swap case */ X if (!P(P_TO)) { X CLEAROP; X if (lineempty()) { X beep(); X break; X } X c = gchar(Curschar); X X if (isalpha(c)) { X if (islower(c)) X c = toupper(c); X else X c = tolower(c); X } X u_saveline(); X X pchar(Curschar, c); /* Change current character. */ X oneright(); X X strcpy(Redobuff, "~"); X X CHANGED; X updateline(); X } X#ifdef TILDEOP X else { X if (operator == TILDE) /* handle '~~' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = TILDE; X } X#endif X X break; X X case 'J': X CLEAROP; X X u_save(Curschar->linep->prev, Curschar->linep->next->next); X X if (!dojoin(TRUE)) X beep(); X X strcpy(Redobuff, "J"); X updatescreen(); X break; X X /* X * Inserts X */ X case 'A': X set_want_col = TRUE; X while (oneright()) X ; X /* fall through */ X X case 'a': X CLEAROP; X /* Works just like an 'i'nsert on the next character. */ X if (!lineempty()) X inc(Curschar); X u_saveline(); X startinsert(mkstr(c), FALSE); X break; X X case 'I': X beginline(TRUE); X /* fall through */ X X case 'i': X case K_INSERT: X CLEAROP; X u_saveline(); X startinsert(mkstr(c), FALSE); X break; X X case 'o': X CLEAROP; X u_save(Curschar->linep, Curschar->linep->next); X opencmd(FORWARD, TRUE); X startinsert("o", TRUE); X break; X X case 'O': X CLEAROP; X u_save(Curschar->linep->prev, Curschar->linep); X opencmd(BACKWARD, TRUE); X startinsert("O", TRUE); X break; X X case 'R': X CLEAROP; X u_saveline(); X startinsert("R", FALSE); X break; X X /* X * Operators X */ X case 'd': X if (operator == DELETE) /* handle 'dd' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = DELETE; X break; X X case 'c': X if (operator == CHANGE) /* handle 'cc' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = CHANGE; X break; X X case 'y': X if (operator == YANK) /* handle 'yy' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = YANK; X break; X X case '>': X if (operator == RSHIFT) /* handle >> */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = RSHIFT; X break; X X case '<': X if (operator == LSHIFT) /* handle << */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; /* save current position */ X operator = LSHIFT; X break; X X case '!': X if (operator == FILTER) /* handle '!!' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = FILTER; X break; X X case 'p': X doput(FORWARD); X break; X X case 'P': X doput(BACKWARD); X break; X X /* X * Abbreviations X */ X case 'D': X stuffin("d$"); X break; X X case 'Y': X if (Prenum) X stuffnum(Prenum); X stuffin("yy"); X break; X X case 'C': X stuffin("c$"); X break; X X case 's': /* substitute characters */ X if (Prenum) X stuffnum(Prenum); X stuffin("c."); X break; X X /* X * Marks X */ X case 'm': X CLEAROP; X if (!setmark(vgetc())) X beep(); X break; X X case '\'': X flag = TRUE; X /* fall through */ X X case '`': X { X LPTR mtmp, *mark = getmark(vgetc()); X X if (mark == NULL) { X beep(); X CLEAROP; X } else { X mtmp = *mark; X setpcmark(); X *Curschar = mtmp; X if (flag) X beginline(TRUE); X } X mtype = flag ? MLINE : MCHAR; X mincl = TRUE; /* ignored if not MCHAR */ X set_want_col = TRUE; X } X break; X X default: X CLEAROP; X beep(); X break; X } X X /* X * If an operation is pending, handle it... X */ X if (finish_op) { /* we just finished an operator */ X if (operator == NOP) /* ... but it was cancelled */ X return; X X switch (operator) { X X case LSHIFT: X case RSHIFT: X doshift(operator, c, nchar, Prenum); X break; X X case DELETE: X dodelete(c, nchar, Prenum); X break; X X case YANK: X (void) doyank(); /* no redo on yank... */ X break; X X case CHANGE: X dochange(c, nchar, Prenum); X break; X X case FILTER: X dofilter(c, nchar, Prenum); X break; X X#ifdef TILDEOP X case TILDE: X dotilde(c, nchar, Prenum); X break; X#endif X X default: X beep(); X } X operator = NOP; X } X} HE_HATES_THESE_CANS if test 16909 -ne "`wc -c < 'normal.c'`" then echo shar: error transmitting "'normal.c'" '(should have been 16909 characters)' fi fi # end of overwriting check echo shar: extracting "'ops.c'" '(12446 characters)' if test -f 'ops.c' then echo shar: will not over-write existing file "'ops.c'" else sed 's/^X//' << \HE_HATES_THESE_CANS > 'ops.c' X/* $Header: /nw/tony/src/stevie/src/RCS/ops.c,v 1.5 89/08/06 09:50:42 tony Exp $ X * X * Contains routines that implement the operators in vi. Everything in this X * file is called only from code in normal.c X */ X X#include "stevie.h" X#include "ops.h" X X/* X * doshift - handle a shift operation X */ Xvoid Xdoshift(op, c1, c2, num) Xint op; Xchar c1, c2; Xint num; X{ X void tabinout(); X LPTR top, bot; X int nlines; X char opchar; X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X u_save(top.linep->prev, bot.linep->next); X X nlines = cntllines(&top, &bot); X *Curschar = top; X tabinout((op == LSHIFT), nlines); X X /* construct Redo buff */ X opchar = (op == LSHIFT) ? '<' : '>'; X if (num != 0) X sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2); X else X sprintf(Redobuff, "%c%c%c", opchar, c1, c2); X X /* X * The cursor position afterward is the prior of the two positions. X */ X *Curschar = top; X X /* X * If we were on the last char of a line that got shifted left, X * then move left one so we aren't beyond the end of the line X */ X if (gchar(Curschar) == NUL && Curschar->index > 0) X Curschar->index--; X X updatescreen(); X X if (nlines > P(P_RP)) X smsg("%d lines %ced", nlines, opchar); X} X X/* X * dodelete - handle a delete operation X */ Xvoid Xdodelete(c1, c2, num) Xchar c1, c2; Xint num; X{ X LPTR top, bot; X int nlines; X register int n; X X /* X * Do a yank of whatever we're about to delete. If there's too much X * stuff to fit in the yank buffer, then get a confirmation before X * doing the delete. This is crude, but simple. And it avoids doing X * a delete of something we can't put back if we want. X */ X if (!doyank()) { X msg("yank buffer exceeded: press <y> to confirm"); X if (vgetc() != 'y') { X msg("delete aborted"); X *Curschar = startop; X return; X } X } X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X u_save(top.linep->prev, bot.linep->next); X X nlines = cntllines(&top, &bot); X *Curschar = top; X cursupdate(); X X if (mtype == MLINE) { X delline(nlines, TRUE); X } else { X if (!mincl && bot.index != 0) X dec(&bot); X X if (top.linep == bot.linep) { /* del. within line */ X n = bot.index - top.index + 1; X while (n--) X if (!delchar(TRUE)) X break; X } else { /* del. between lines */ X n = Curschar->index; X while (Curschar->index >= n) X if (!delchar(TRUE)) X break; X X top = *Curschar; X *Curschar = *nextline(Curschar); X delline(nlines-2, TRUE); X Curschar->index = 0; X n = bot.index + 1; X while (n--) X if (!delchar(TRUE)) X break; X *Curschar = top; X (void) dojoin(FALSE); X oneright(); /* we got bumped left up above */ X } X } X X /* construct Redo buff */ X if (num != 0) X sprintf(Redobuff, "d%d%c%c", num, c1, c2); X else X sprintf(Redobuff, "d%c%c", c1, c2); X X if (mtype == MCHAR && nlines == 1) X updateline(); X else X updatescreen(); X X if (nlines > P(P_RP)) X smsg("%d fewer lines", nlines); X} X X/* X * dofilter - handle a filter operation X */ X X#define ITMP "viXXXXXX" X#define OTMP "voXXXXXX" X Xstatic char itmp[32]; Xstatic char otmp[32]; X X X/* X * dofilter - filter lines through a command given by the user X * X * We use temp files and the system() routine here. This would normally X * be done using pipes on a UNIX machine, but this is more portable to X * the machines we usually run on. The system() routine needs to be able X * to deal with redirection somehow, and should handle things like looking X * at the PATH env. variable, and adding reasonable extensions to the X * command name given by the user. All reasonable versions of system() X * do this. X */ Xvoid Xdofilter(c1, c2, num) Xchar c1, c2; Xint num; X{ X char *mktemp(); X static char *lastcmd = NULL;/* the last thing we did */ X char *buff; /* cmd buffer from getcmdln() */ X char cmdln[200]; /* filtering command line */ X LPTR top, bot; X int nlines; X X top = startop; X bot = *Curschar; X X buff = getcmdln('!'); X X if (buff == NULL) /* user backed out of the command prompt */ X return; X X if (*buff == '!') { /* use the 'last' command */ X if (lastcmd == NULL) { X emsg("No previous command"); X return; X } X buff = lastcmd; X } X X /* X * Remember the current command X */ X if (lastcmd != NULL) X free(lastcmd); X lastcmd = strsave(buff); X X if (lt(&bot, &top)) X pswap(&top, &bot); X X u_save(top.linep->prev, bot.linep->next); X X nlines = cntllines(&top, &bot); X *Curschar = top; X cursupdate(); X X /* X * 1. Form temp file names X * 2. Write the lines to a temp file X * 3. Run the filter command on the temp file X * 4. Read the output of the command into the buffer X * 5. Delete the original lines to be filtered X * 6. Remove the temp files X */ X X#ifdef TMPDIR X strcpy(itmp, TMPDIR); X strcpy(otmp, TMPDIR); X#else X itmp[0] = otmp[0] = NUL; X#endif X strcat(itmp, ITMP); X strcat(otmp, OTMP); X X if (mktemp(itmp) == NULL || mktemp(otmp) == NULL) { X emsg("Can't get temp file names"); X return; X } X X if (!writeit(itmp, &top, &bot)) { X emsg("Can't create input temp file"); X return; X } X X sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp); X X if (system(cmdln) != 0) { X emsg("Filter command failed"); X remove(ITMP); X return; X } X X if (readfile(otmp, &bot, TRUE)) { X emsg("Can't read filter output"); X return; X } X X delline(nlines, TRUE); X X remove(itmp); X remove(otmp); X X /* construct Redo buff */ X if (num != 0) X sprintf(Redobuff, "d%d%c%c", num, c1, c2); X else X sprintf(Redobuff, "d%c%c", c1, c2); X X updatescreen(); X X if (nlines > P(P_RP)) X smsg("%d lines filtered", nlines); X} X X#ifdef TILDEOP Xvoid Xdotilde(c1, c2, num) Xchar c1, c2; Xint num; X{ X LPTR top, bot; X register char c; X X /* construct Redo buff */ X if (num != 0) X sprintf(Redobuff, "~%d%c%c", num, c1, c2); X else X sprintf(Redobuff, "~%c%c", c1, c2); X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X u_save(top.linep->prev, bot.linep->next); X X if (mtype == MLINE) { X top.index = 0; X bot.index = strlen(bot.linep->s); X } else { X if (!mincl) { X if (bot.index) X bot.index--; X } X } X X for (; ltoreq(&top, &bot) ;inc(&top)) { X /* X * Swap case through the range X */ X c = gchar(&top); X if (isalpha(c)) { X if (islower(c)) X c = toupper(c); X else X c = tolower(c); X X pchar(&top, c); /* Change current character. */ X CHANGED; X } X } X *Curschar = startop; X updatescreen(); X} X#endif X X/* X * dochange - handle a change operation X */ Xvoid Xdochange(c1, c2, num) Xchar c1, c2; Xint num; X{ X char sbuf[16]; X bool_t doappend; /* true if we should do append, not insert */ X bool_t at_eof; /* changing through the end of file */ X LPTR top, bot; X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X doappend = endofline(&bot); X at_eof = (bot.linep->next == Fileend->linep); X X dodelete(c1, c2, num); X X if (mtype == MLINE) { X /* X * If we made a change through the last line of the file, X * then the cursor got backed up, and we need to open a X * new line forward, otherwise we go backward. X */ X if (at_eof) X opencmd(FORWARD, FALSE); X else X opencmd(BACKWARD, FALSE); X } else { X if (doappend && !lineempty()) X inc(Curschar); X } X X if (num) X sprintf(sbuf, "c%d%c%c", num, c1, c2); X else X sprintf(sbuf, "c%c%c", c1, c2); X X startinsert(sbuf, mtype == MLINE); X} X X#ifndef YBSIZE X#define YBSIZE 4096 X#endif X Xstatic char ybuf[YBSIZE]; Xstatic int ybtype = MBAD; X Xbool_t Xdoyank() X{ X LPTR top, bot; X char *yptr = ybuf; X char *ybend = &ybuf[YBSIZE-1]; X int nlines; X X top = startop; X bot = *Curschar; X X if (lt(&bot, &top)) X pswap(&top, &bot); X X nlines = cntllines(&top, &bot); X X ybtype = mtype; /* set the yank buffer type */ X X if (mtype == MLINE) { X top.index = 0; X bot.index = strlen(bot.linep->s); X /* X * The following statement checks for the special case of X * yanking a blank line at the beginning of the file. If X * not handled right, we yank an extra char (a newline). X */ X if (dec(&bot) == -1) { X ybuf[0] = NUL; X if (operator == YANK) X *Curschar = startop; X return TRUE; X } X } else { X if (!mincl) { X if (bot.index) X bot.index--; X } X } X X for (; ltoreq(&top, &bot) ;inc(&top)) { X *yptr = (gchar(&top) != NUL) ? gchar(&top) : NL; X if (++yptr >= ybend) { X msg("yank too big for buffer"); X ybtype = MBAD; X return FALSE; X } X } X X *yptr = NUL; X X if (operator == YANK) { /* restore Curschar if really doing yank */ X *Curschar = startop; X X if (nlines > P(P_RP)) X smsg("%d lines yanked", nlines); X } X X return TRUE; X} X X/* X * doput(dir) X * X * Put the yank buffer at the current location, using the direction given X * by 'dir'. X */ Xvoid Xdoput(dir) Xint dir; X{ X void inslines(); X X if (ybtype == MBAD) { X beep(); X return; X } X X u_saveline(); X X if (ybtype == MLINE) X inslines(Curschar->linep, dir, ybuf); X else { X /* X * If we did a character-oriented yank, and the buffer X * contains multiple lines, the situation is more complex. X * For the moment, we punt, and pretend the user did a X * line-oriented yank. This doesn't actually happen that X * often. X */ X if (strchr(ybuf, NL) != NULL) X inslines(Curschar->linep, dir, ybuf); X else { X char *s; X int len; X X len = strlen(Curschar->linep->s) + strlen(ybuf) + 1; X s = alloc((unsigned) len); X strcpy(s, Curschar->linep->s); X if (dir == FORWARD) X Curschar->index++; X strcpy(s + Curschar->index, ybuf); X strcat(s, &Curschar->linep->s[Curschar->index]); X free(Curschar->linep->s); X Curschar->linep->s = s; X Curschar->linep->size = len; X updateline(); X } X } X X CHANGED; X} X Xbool_t Xdojoin(join_cmd) Xbool_t join_cmd; /* handling a real "join" command? */ X{ X int scol; /* save cursor column */ X int size; /* size of the joined line */ X X if (nextline(Curschar) == NULL) /* on last line */ X return FALSE; X X if (!canincrease(size = strlen(Curschar->linep->next->s))) X return FALSE; X X while (oneright()) /* to end of line */ X ; X X strcat(Curschar->linep->s, Curschar->linep->next->s); X X /* X * Delete the following line. To do this we move the cursor X * there briefly, and then move it back. Don't back up if the X * delete made us the last line. X */ X Curschar->linep = Curschar->linep->next; X scol = Curschar->index; X X if (nextline(Curschar) != NULL) { X delline(1, TRUE); X Curschar->linep = Curschar->linep->prev; X } else X delline(1, TRUE); X X Curschar->index = scol; X X if (join_cmd) X oneright(); /* go to first char. of joined line */ X X if (join_cmd && size != 0) { X /* X * Delete leading white space on the joined line X * and insert a single space. X */ X while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) X delchar(TRUE); X inschar(' '); X } X X return TRUE; X} X Xvoid Xstartinsert(initstr, startln) Xchar *initstr; Xint startln; /* if set, insert point really at start of line */ X{ X register char *p, c; X X *Insstart = *Curschar; X if (startln) X Insstart->index = 0; X Ninsert = 0; X Insptr = Insbuff; X for (p=initstr; (c=(*p++))!='\0'; ) X *Insptr++ = c; X X if (*initstr == 'R') X State = REPLACE; X else X State = INSERT; X X if (P(P_MO)) X msg((State == INSERT) ? "Insert Mode" : "Replace Mode"); X} X/* X * tabinout(inout,num) X * X * If inout==0, add a tab to the begining of the next num lines. X * If inout==1, delete a tab from the beginning of the next num lines. X */ Xstatic void Xtabinout(inout, num) Xint inout; Xint num; X{ X int ntodo = num; X LPTR *p; X X beginline(FALSE); X while (ntodo-- > 0) { X beginline(FALSE); X if (inout == 0) X inschar(TAB); X else { X if (gchar(Curschar) == TAB) X delchar(TRUE); X } X if ( ntodo > 0 ) { X if ((p = nextline(Curschar)) != NULL) X *Curschar = *p; X else X break; X } X } X} X X/* X * inslines(lp, dir, buf) X * X * Inserts lines in the file from the given buffer. Lines are inserted X * before or after "lp" according to the given direction flag. Newlines X * in the buffer result in multiple lines being inserted. The cursor X * is left on the first of the inserted lines. X */ Xstatic void Xinslines(lp, dir, buf) XLINE *lp; Xint dir; Xchar *buf; X{ X register char *cp = buf; X register int len; X char *ep; X LINE *l, *nc = NULL; X X if (dir == BACKWARD) X lp = lp->prev; X X do { X if ((ep = strchr(cp, NL)) == NULL) X len = strlen(cp); X else X len = ep - cp; X X l = newline(len); X if (len != 0) X strncpy(l->s, cp, len); X l->s[len] = NUL; X X l->next = lp->next; X l->prev = lp; X lp->next->prev = l; X lp->next = l; X X if (nc == NULL) X nc = l; X X lp = lp->next; X X cp = ep + 1; X } while (ep != NULL); X X if (dir == BACKWARD) /* fix the top line in case we were there */ X Filemem->linep = Filetop->linep->next; X X renum(); X X updatescreen(); X Curschar->linep = nc; X Curschar->index = 0; X} HE_HATES_THESE_CANS if test 12446 -ne "`wc -c < 'ops.c'`" then echo shar: error transmitting "'ops.c'" '(should have been 12446 characters)' fi fi # end of overwriting check echo shar: extracting "'ops.h'" '(1217 characters)' if test -f 'ops.h' then echo shar: will not over-write existing file "'ops.h'" else sed 's/^X//' << \HE_HATES_THESE_CANS > 'ops.h' X/* $Header: /nw/tony/src/stevie/src/RCS/ops.h,v 1.2 89/07/19 08:08:21 tony Exp $ X * X * Macros and declarations for the operator code in ops.c X */ X X/* X * Operators X */ X#define NOP 0 /* no pending operation */ X#define DELETE 1 X#define YANK 2 X#define CHANGE 3 X#define LSHIFT 4 X#define RSHIFT 5 X#define FILTER 6 X#define TILDE 7 X Xextern int operator; /* current pending operator */ X X/* X * When a cursor motion command is made, it is marked as being a character X * or line oriented motion. Then, if an operator is in effect, the operation X * becomes character or line oriented accordingly. X * X * Character motions are marked as being inclusive or not. Most char. X * motions are inclusive, but some (e.g. 'w') are not. X */ X X/* X * Cursor motion types X */ X#define MBAD (-1) /* 'bad' motion type marks unusable yank buf */ X#define MCHAR 0 X#define MLINE 1 X Xextern int mtype; /* type of the current cursor motion */ Xextern bool_t mincl; /* true if char motion is inclusive */ X Xextern LPTR startop; /* cursor pos. at start of operator */ X X/* X * Functions defined in ops.c X */ Xvoid doshift(), dodelete(), doput(), dochange(), dofilter(); X#ifdef TILDEOP Xvoid dotilde(); X#endif Xbool_t dojoin(), doyank(); Xvoid startinsert(); HE_HATES_THESE_CANS if test 1217 -ne "`wc -c < 'ops.h'`" then echo shar: error transmitting "'ops.h'" '(should have been 1217 characters)' fi fi # end of overwriting check echo shar: extracting "'param.c'" '(3887 characters)' if test -f 'param.c' then echo shar: will not over-write existing file "'param.c'" else sed 's/^X//' << \HE_HATES_THESE_CANS > 'param.c' X/* $Header: /nw/tony/src/stevie/src/RCS/param.c,v 1.10 89/08/02 10:59:10 tony Exp $ X * X * Code to handle user-settable parameters. This is all pretty much table- X * driven. To add a new parameter, put it in the params array, and add a X * macro for it in param.h. If it's a numeric parameter, add any necessary X * bounds checks to doset(). String parameters aren't currently supported. X */ X X#include "stevie.h" X Xstruct param params[] = { X X { "tabstop", "ts", 8, P_NUM }, X { "scroll", "scroll", 12, P_NUM }, X { "report", "report", 5, P_NUM }, X { "lines", "lines", 25, P_NUM }, X X { "vbell", "vb", TRUE, P_BOOL }, X { "showmatch", "sm", FALSE, P_BOOL }, X { "wrapscan", "ws", TRUE, P_BOOL }, X { "errorbells", "eb", FALSE, P_BOOL }, X { "showmode", "mo", FALSE, P_BOOL }, X { "backup", "bk", FALSE, P_BOOL }, X { "return", "cr", TRUE, P_BOOL }, X { "list", "list", FALSE, P_BOOL }, X { "ignorecase", "ic", FALSE, P_BOOL }, X { "autoindent", "ai", FALSE, P_BOOL }, X { "number", "nu", FALSE, P_BOOL }, X { "modelines", "ml", FALSE, P_BOOL }, X { "tildeop", "to", FALSE, P_BOOL }, X { "terse", "terse", FALSE, P_BOOL }, X { "", "", 0, 0, } /* end marker */ X X}; X Xstatic void showparms(); X Xvoid Xdoset(arg) Xchar *arg; /* parameter string */ X{ X register int i; X register char *s; X bool_t did_lines = FALSE; X bool_t state = TRUE; /* new state of boolean parms. */ X X if (arg == NULL) { X showparms(FALSE); X return; X } X if (strncmp(arg, "all", 3) == 0) { X showparms(TRUE); X return; X } X if (strncmp(arg, "no", 2) == 0) { X state = FALSE; X arg += 2; X } X X for (i=0; params[i].fullname[0] != NUL ;i++) { X s = params[i].fullname; X if (strncmp(arg, s, strlen(s)) == 0) /* matched full name */ X break; X s = params[i].shortname; X if (strncmp(arg, s, strlen(s)) == 0) /* matched short name */ X break; X } X X if (params[i].fullname[0] != NUL) { /* found a match */ X if (params[i].flags & P_NUM) { X did_lines = (i == P_LI); X if (arg[strlen(s)] != '=' || state == FALSE) X emsg("Invalid set of numeric parameter"); X else { X params[i].value = atoi(arg+strlen(s)+1); X params[i].flags |= P_CHANGED; X } X } else /* boolean */ { X if (arg[strlen(s)] == '=') X emsg("Invalid set of boolean parameter"); X else { X params[i].value = state; X params[i].flags |= P_CHANGED; X } X } X } else X emsg("Unrecognized 'set' option"); X X /* X * Update the screen in case we changed something like "tabstop" X * or "list" that will change its appearance. X */ X updatescreen(); X X if (did_lines) { X Rows = P(P_LI); X screenalloc(); /* allocate new screen buffers */ X screenclear(); X updatescreen(); X } X /* X * Check the bounds for numeric parameters here X */ X if (P(P_TS) <= 0 || P(P_TS) > 32) { X emsg("Invalid tab size specified"); X P(P_TS) = 8; X return; X } X X if (P(P_SS) <= 0 || P(P_SS) > Rows) { X emsg("Invalid scroll size specified"); X P(P_SS) = 12; X return; X } X X#ifndef TILDEOP X if (P(P_TO)) { X emsg("Tilde-operator not enabled"); X P(P_TO) = FALSE; X return; X } X#endif X /* X * Check for another argument, and call doset() recursively, if X * found. If any argument results in an error, no further X * parameters are processed. X */ X while (*arg != ' ' && *arg != '\t') { /* skip to next white space */ X if (*arg == NUL) X return; /* end of parameter list */ X arg++; X } X while (*arg == ' ' || *arg == '\t') /* skip to next non-white */ X arg++; X X if (*arg) X doset(arg); /* recurse on next parameter */ X} X Xstatic void Xshowparms(all) Xbool_t all; /* show ALL parameters */ X{ X register struct param *p; X char buf[64]; X X gotocmd(TRUE, 0); X outstr("Parameters:\r\n"); X X for (p = ¶ms[0]; p->fullname[0] != NUL ;p++) { X if (!all && ((p->flags & P_CHANGED) == 0)) X continue; X if (p->flags & P_BOOL) X sprintf(buf, "\t%s%s\r\n", X (p->value ? "" : "no"), p->fullname); X else X sprintf(buf, "\t%s=%d\r\n", p->fullname, p->value); X X outstr(buf); X } X wait_return(); X} HE_HATES_THESE_CANS if test 3887 -ne "`wc -c < 'param.c'`" then echo shar: error transmitting "'param.c'" '(should have been 3887 characters)' fi fi # end of overwriting check echo shar: extracting "'param.h'" '(1459 characters)' if test -f 'param.h' then echo shar: will not over-write existing file "'param.h'" else sed 's/^X//' << \HE_HATES_THESE_CANS > 'param.h' X/* $Header: /nw/tony/src/stevie/src/RCS/param.h,v 1.8 89/08/02 10:59:35 tony Exp $ X * X * Settable parameters X */ X Xstruct param { X char *fullname; /* full parameter name */ X char *shortname; /* permissible abbreviation */ X int value; /* parameter value */ X int flags; X}; X Xextern struct param params[]; X X/* X * Flags X */ X#define P_BOOL 0x01 /* the parameter is boolean */ X#define P_NUM 0x02 /* the parameter is numeric */ X#define P_CHANGED 0x04 /* the parameter has been changed */ X X/* X * The following are the indices in the params array for each parameter X */ X X/* X * Numeric parameters X */ X#define P_TS 0 /* tab size */ X#define P_SS 1 /* scroll size */ X#define P_RP 2 /* report */ X#define P_LI 3 /* lines */ X X/* X * Boolean parameters X */ X#define P_VB 4 /* visual bell */ X#define P_SM 5 /* showmatch */ X#define P_WS 6 /* wrap scan */ X#define P_EB 7 /* error bells */ X#define P_MO 8 /* show mode */ X#define P_BK 9 /* make backups when writing out files */ X#define P_CR 10 /* use cr-lf to terminate lines on writes */ X#define P_LS 11 /* show tabs and newlines graphically */ X#define P_IC 12 /* ignore case in searches */ X#define P_AI 13 /* auto-indent */ X#define P_NU 14 /* number lines on the screen */ X#define P_ML 15 /* enables mode-lines processing */ X#define P_TO 16 /* if true, tilde is an operator */ X#define P_TE 17 /* ignored; here for compatibility */ X X/* X * Macro to get the value of a parameter X */ X#define P(n) (params[n].value) HE_HATES_THESE_CANS if test 1459 -ne "`wc -c < 'param.h'`" then echo shar: error transmitting "'param.h'" '(should have been 1459 characters)' fi fi # end of overwriting check echo shar: extracting "'ptrfunc.c'" '(2663 characters)' if test -f 'ptrfunc.c' then echo shar: will not over-write existing file "'ptrfunc.c'" else sed 's/^X//' << \HE_HATES_THESE_CANS > 'ptrfunc.c' X/* $Header: /nw/tony/src/stevie/src/RCS/ptrfunc.c,v 1.5 89/03/11 22:43:12 tony Exp $ X * X * The routines in this file attempt to imitate many of the operations X * that used to be performed on simple character pointers and are now X * performed on LPTR's. This makes it easier to modify other sections X * of the code. Think of an LPTR as representing a position in the file. X * Positions can be incremented, decremented, compared, etc. through X * the functions implemented here. X */ X X#include "stevie.h" X X/* X * inc(p) X * X * Increment the line pointer 'p' crossing line boundaries as necessary. X * Return 1 when crossing a line, -1 when at end of file, 0 otherwise. X */ Xint Xinc(lp) Xregister LPTR *lp; X{ X register char *p; X X if (lp && lp->linep) X p = &(lp->linep->s[lp->index]); X else X return -1; X X if (*p != NUL) { /* still within line */ X lp->index++; X return ((p[1] != NUL) ? 0 : 1); X } X X if (lp->linep->next != Fileend->linep) { /* there is a next line */ X lp->index = 0; X lp->linep = lp->linep->next; X return 1; X } X X return -1; X} X X/* X * dec(p) X * X * Decrement the line pointer 'p' crossing line boundaries as necessary. X * Return 1 when crossing a line, -1 when at start of file, 0 otherwise. X */ Xint Xdec(lp) Xregister LPTR *lp; X{ X if (lp->index > 0) { /* still within line */ X lp->index--; X return 0; X } X X if (lp->linep->prev != Filetop->linep) { /* there is a prior line */ X lp->linep = lp->linep->prev; X lp->index = strlen(lp->linep->s); X return 1; X } X X lp->index = 0; /* stick at first char */ X return -1; /* at start of file */ X} X X/* X * gchar(lp) - get the character at position "lp" X */ Xint Xgchar(lp) Xregister LPTR *lp; X{ X if (lp && lp->linep) X return (lp->linep->s[lp->index]); X else X return 0; X} X X/* X * pchar(lp, c) - put character 'c' at position 'lp' X */ Xvoid Xpchar(lp, c) Xregister LPTR *lp; Xchar c; X{ X lp->linep->s[lp->index] = c; X} X X/* X * pswap(a, b) - swap two position pointers X */ Xvoid Xpswap(a, b) Xregister LPTR *a, *b; X{ X LPTR tmp; X X tmp = *a; X *a = *b; X *b = tmp; X} X X/* X * Position comparisons X */ X Xbool_t Xlt(a, b) Xregister LPTR *a, *b; X{ X register int an, bn; X X an = LINEOF(a); X bn = LINEOF(b); X X if (an != bn) X return (an < bn); X else X return (a->index < b->index); X} X X#if 0 Xbool_t Xgt(a, b) XLPTR *a, *b; X{ X register int an, bn; X X an = LINEOF(a); X bn = LINEOF(b); X X if (an != bn) X return (an > bn); X else X return (a->index > b->index); X} X#endif X Xbool_t Xequal(a, b) Xregister LPTR *a, *b; X{ X return (a->linep == b->linep && a->index == b->index); X} X Xbool_t Xltoreq(a, b) Xregister LPTR *a, *b; X{ X return (lt(a, b) || equal(a, b)); X} X X#if 0 Xbool_t Xgtoreq(a, b) XLPTR *a, *b; X{ X return (gt(a, b) || equal(a, b)); X} X#endif HE_HATES_THESE_CANS if test 2663 -ne "`wc -c < 'ptrfunc.c'`" then echo shar: error transmitting "'ptrfunc.c'" '(should have been 2663 characters)' fi fi # end of overwriting check # End of shell archive exit 0 --