home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-03-14 | 53.8 KB | 2,749 lines |
-
- : This is a shar archive. Extract with sh, not csh.
- : The rest of this file will extract:
- : linefunc.c main.c mark.c misccmds.c normal.c ops.c
- echo extracting - linefunc.c
- sed 's/^X//' > linefunc.c << '!EOR!'
- X/* $Header: /nw/tony/src/stevie/src/RCS/linefunc.c,v 1.2 89/03/11 22:42:32 tony Exp $
- X *
- X * Basic line-oriented motions.
- X */
- X
- X#include "stevie.h"
- X#include "ops.h"
- X
- X/*
- X * nextline(curr)
- X *
- X * Return a pointer to the beginning of the next line after the one
- X * referenced by 'curr'. Return NULL if there is no next line (at EOF).
- X */
- X
- XLPTR *
- Xnextline(curr)
- XLPTR *curr;
- X{
- X static LPTR next;
- X
- X if (curr->linep->next != Fileend->linep) {
- X next.index = 0;
- X next.linep = curr->linep->next;
- X return &next;
- X }
- X return (LPTR *) NULL;
- X}
- X
- X/*
- X * prevline(curr)
- X *
- X * Return a pointer to the beginning of the line before the one
- X * referenced by 'curr'. Return NULL if there is no prior line.
- X */
- X
- XLPTR *
- Xprevline(curr)
- XLPTR *curr;
- X{
- X static LPTR prev;
- X
- X if (curr->linep->prev != Filetop->linep) {
- X prev.index = 0;
- X prev.linep = curr->linep->prev;
- X return &prev;
- X }
- X return (LPTR *) NULL;
- X}
- X
- X/*
- X * coladvance(p,col)
- X *
- X * Try to advance to the specified column, starting at p.
- X */
- X
- XLPTR *
- Xcoladvance(p, col)
- XLPTR *p;
- Xregister int col;
- X{
- X static LPTR lp;
- X register int c, in;
- X
- X lp.linep = p->linep;
- X lp.index = p->index;
- X
- X /* If we're on a blank ('\n' only) line, we can't do anything */
- X if (lp.linep->s[lp.index] == '\0')
- X return &lp;
- X /* try to advance to the specified column */
- X for ( c=0; col-- > 0; c++ ) {
- X /* Count a tab for what it's worth (if list mode not on) */
- X if ( gchar(&lp) == TAB && !P(P_LS) ) {
- X in = ((P(P_TS)-1) - c%P(P_TS));
- X col -= in;
- X c += in;
- X }
- X /* Don't go past the end of */
- X /* the file or the line. */
- X if (inc(&lp)) {
- X dec(&lp);
- X break;
- X }
- X }
- X return &lp;
- X}
- X
- X
- X/*
- X * nextchar(curr)
- X *
- X * Return a line pointer to the next character after the
- X * one referenced by 'curr'. Return NULL if there is no next one (at EOF).
- X * NOTE: this COULD point to a \n or \0 character.
- X */
- X
- XLPTR *
- Xnextchar(curr)
- XLPTR *curr;
- X{
- X static LPTR *next;
- X char c;
- X
- X next = curr;
- X c = CHAR( next );
- X if (c=='\n' || c=='\0') /* end of line */
- X next = nextline (next);
- X else
- X next->index++;
- X
- X return (next);
- X}
- X
- X
- X/*
- X * prevchar(curr)
- X *
- X * Return a line pointer to the previous character before the
- X * one referenced by 'curr'. Return NULL if there is no previous one.
- X * Note: this COULD point to a \n or \0 character.
- X */
- X
- XLPTR *
- Xprevchar(curr)
- XLPTR *curr;
- X{
- X static LPTR *prev;
- X char c;
- X
- X prev = curr;
- X if (prev->index == 0) { /* beginning of line */
- X prev = prevline (prev); /* jump back */
- X c = CHAR( prev );
- X while (c!='\n' && c!= '\0') { /* go to end of line */
- X prev->index++;
- X c = CHAR( prev );
- X }
- X }
- X else
- X prev->index--;
- X
- X return (prev);
- X}
- X
- X
- !EOR!
- echo extracting - main.c
- sed 's/^X//' > main.c << '!EOR!'
- X/* $Header: /nw/tony/src/stevie/src/RCS/main.c,v 1.12 89/08/02 19:53:27 tony Exp $
- X *
- X * The main routine and routines to deal with the input buffer.
- X */
- X
- X#include "stevie.h"
- X
- Xint Rows; /* Number of Rows and Columns */
- Xint Columns; /* in the current window. */
- X
- Xchar *Realscreen = NULL; /* What's currently on the screen, a single */
- X /* array of size Rows*Columns. */
- Xchar *Nextscreen = NULL; /* What's to be put on the screen. */
- X
- Xchar *Filename = NULL; /* Current file name */
- X
- XLPTR *Filemem; /* Pointer to the first line of the file */
- X
- XLPTR *Filetop; /* Line 'above' the start of the file */
- X
- XLPTR *Fileend; /* Pointer to the end of the file in Filemem. */
- X /* (It points to the byte AFTER the last byte.) */
- X
- XLPTR *Topchar; /* Pointer to the byte in Filemem which is */
- X /* in the upper left corner of the screen. */
- X
- XLPTR *Botchar; /* Pointer to the byte in Filemem which is */
- X /* just off the bottom of the screen. */
- X
- XLPTR *Curschar; /* Pointer to byte in Filemem at which the */
- X /* cursor is currently placed. */
- X
- Xint Cursrow, Curscol; /* Current position of cursor */
- X
- Xint Cursvcol; /* Current virtual column, the column number of */
- X /* the file's actual line, as opposed to the */
- X /* column number we're at on the screen. This */
- X /* makes a difference on lines that span more */
- X /* than one screen line. */
- X
- Xint Curswant = 0; /* The column we'd like to be at. This is used */
- X /* try to stay in the same column through up/down */
- X /* cursor motions. */
- X
- Xbool_t set_want_col; /* If set, then update Curswant the next time */
- X /* through cursupdate() to the current virtual */
- X /* column. */
- X
- Xint State = NORMAL; /* This is the current state of the command */
- X /* interpreter. */
- X
- Xint Prenum = 0; /* The (optional) number before a command. */
- X
- XLPTR *Insstart; /* This is where the latest insert/append */
- X /* mode started. */
- X
- Xbool_t Changed = 0; /* Set to 1 if something in the file has been */
- X /* changed and not written out. */
- X
- Xchar Redobuff[1024]; /* Each command should stuff characters into this */
- X /* buffer that will re-execute itself. */
- X
- Xchar Insbuff[1024]; /* Each insertion gets stuffed into this buffer. */
- X
- Xint Ninsert = 0; /* Number of characters in the current insertion. */
- Xchar *Insptr = NULL;
- X
- Xbool_t got_int=FALSE; /* set to TRUE when an interrupt occurs (if possible) */
- X
- Xbool_t interactive = FALSE; /* set TRUE when main() is ready to roll */
- X
- Xchar **files; /* list of input files */
- Xint numfiles; /* number of input files */
- Xint curfile; /* number of the current file */
- X
- Xstatic void
- Xusage()
- X{
- X fprintf(stderr, "usage: stevie [file ...]\n");
- X fprintf(stderr, " stevie -t tag\n");
- X fprintf(stderr, " stevie +[num] file\n");
- X fprintf(stderr, " stevie +/pat file\n");
- X exit(1);
- X}
- X
- Xmain(argc,argv)
- Xint argc;
- Xchar *argv[];
- X{
- X char *initstr, *getenv(); /* init string from the environment */
- X char *tag = NULL; /* tag from command line */
- X char *pat = NULL; /* pattern from command line */
- X int line = -1; /* line number from command line */
- X
- X /*
- X * Process the command line arguments.
- X */
- X if (argc > 1) {
- X switch (argv[1][0]) {
- X
- X case '-': /* -t tag */
- X if (argv[1][1] != 't')
- X usage();
- X
- X if (argv[2] == NULL)
- X usage();
- X
- X Filename = NULL;
- X tag = argv[2];
- X numfiles = 1;
- X break;
- X
- X case '+': /* +n or +/pat */
- X if (argv[1][1] == '/') {
- X if (argv[2] == NULL)
- X usage();
- X Filename = strsave(argv[2]);
- X pat = &(argv[1][1]);
- X numfiles = 1;
- X
- X } else if (isdigit(argv[1][1]) || argv[1][1] == NUL) {
- X if (argv[2] == NULL)
- X usage();
- X Filename = strsave(argv[2]);
- X numfiles = 1;
- X
- X line = (isdigit(argv[1][1])) ?
- X atoi(&(argv[1][1])) : 0;
- X } else
- X usage();
- X
- X break;
- X
- X default: /* must be a file name */
- X Filename = strsave(argv[1]);
- X files = &(argv[1]);
- X numfiles = argc - 1;
- X break;
- X }
- X } else {
- X Filename = NULL;
- X numfiles = 1;
- X }
- X curfile = 0;
- X
- X if (numfiles > 1)
- X fprintf(stderr, "%d files to edit\n", numfiles);
- X
- X windinit();
- X
- X /*
- X * Allocate LPTR structures for all the various position pointers
- X */
- X if ((Filemem = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
- X (Filetop = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
- X (Fileend = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
- X (Topchar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
- X (Botchar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
- X (Curschar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
- X (Insstart = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
- X (screenalloc() == -1) ) {
- X fprintf(stderr, "Can't allocate data structures\n");
- X windexit(0);
- X }
- X
- X filealloc(); /* Initialize Filemem, Filetop, and Fileend */
- X
- X screenclear();
- X
- X if ((initstr = getenv("EXINIT")) != NULL) {
- X char *lp, buf[128];
- X
- X if ((lp = getenv("LINES")) != NULL) {
- X sprintf(buf, "%s lines=%s", initstr, lp);
- X docmdln(buf);
- X } else
- X docmdln(initstr);
- X }
- X
- X if (Filename != NULL) {
- X if (readfile(Filename, Filemem, FALSE))
- X filemess("[New File]");
- X } else if (tag == NULL)
- X msg("Empty Buffer");
- X
- X setpcmark();
- X
- X if (tag) {
- X stuffin(":ta ");
- X stuffin(tag);
- X stuffin("\n");
- X
- X } else if (pat) {
- X stuffin(pat);
- X stuffin("\n");
- X
- X } else if (line >= 0) {
- X if (line > 0)
- X stuffnum(line);
- X stuffin("G");
- X }
- X
- X interactive = TRUE;
- X
- X edit();
- X
- X windexit(0);
- X
- X return 1; /* shouldn't be reached */
- X}
- X
- X#define RBSIZE 1024
- Xstatic char getcbuff[RBSIZE];
- Xstatic char *getcnext = NULL;
- X
- Xvoid
- Xstuffin(s)
- Xchar *s;
- X{
- X if (s == NULL) { /* clear the stuff buffer */
- X getcnext = NULL;
- X return;
- X }
- X
- X if (getcnext == NULL) {
- X strcpy(getcbuff,s);
- X getcnext = getcbuff;
- X } else
- X strcat(getcbuff,s);
- X}
- X
- Xvoid
- Xstuffnum(n)
- Xint n;
- X{
- X char buf[32];
- X
- X sprintf(buf, "%d", n);
- X stuffin(buf);
- X}
- X
- Xint
- Xvgetc()
- X{
- X register int c;
- X
- X /*
- X * inchar() may map special keys by using stuffin(). If it does
- X * so, it returns -1 so we know to loop here to get a real char.
- X */
- X do {
- X if ( getcnext != NULL ) {
- X int nextc = *getcnext++;
- X if ( *getcnext == NUL ) {
- X *getcbuff = NUL;
- X getcnext = NULL;
- X }
- X return(nextc);
- X }
- X c = inchar();
- X } while (c == -1);
- X
- X return c;
- X}
- X
- X/*
- X * anyinput
- X *
- X * Return non-zero if input is pending.
- X */
- X
- Xbool_t
- Xanyinput()
- X{
- X return (getcnext != NULL);
- X}
- X
- X/*
- X * do_mlines() - process mode lines for the current file
- X *
- X * Returns immediately if the "ml" parameter isn't set.
- X */
- X#define NMLINES 5 /* no. of lines at start/end to check for modelines */
- X
- Xvoid
- Xdo_mlines()
- X{
- X void chk_mline();
- X int i;
- X register LPTR *p;
- X
- X if (!P(P_ML))
- X return;
- X
- X p = Filemem;
- X for (i=0; i < NMLINES ;i++) {
- X chk_mline(p->linep->s);
- X if ((p = nextline(p)) == NULL)
- X break;
- X }
- X
- X if ((p = prevline(Fileend)) == NULL)
- X return;
- X
- X for (i=0; i < NMLINES ;i++) {
- X chk_mline(p->linep->s);
- X if ((p = prevline(p)) == NULL)
- X break;
- X }
- X}
- X
- X/*
- X * chk_mline() - check a single line for a mode string
- X */
- Xstatic void
- Xchk_mline(s)
- Xregister char *s;
- X{
- X register char *cs; /* local copy of any modeline found */
- X register char *e;
- X
- X for (; *s != NUL ;s++) {
- X if (strncmp(s, "vi:", 3) == 0 || strncmp(s, "ex:", 3) == 0) {
- X cs = strsave(s+3);
- X if ((e = strchr(cs, ':')) != NULL) {
- X *e = NUL;
- X stuffin(mkstr(CTRL('o')));
- X docmdln(cs);
- X }
- X free(cs);
- X }
- X }
- X}
- !EOR!
- echo extracting - mark.c
- sed 's/^X//' > mark.c << '!EOR!'
- X/* $Header: /nw/tony/src/stevie/src/RCS/mark.c,v 1.3 89/03/11 22:42:39 tony Exp $
- X *
- X * Routines to save and retrieve marks.
- X */
- X
- X#include "stevie.h"
- X
- X#define NMARKS 10 /* max. # of marks that can be saved */
- X
- Xstruct mark {
- X char name;
- X LPTR pos;
- X};
- X
- Xstatic struct mark mlist[NMARKS];
- Xstatic struct mark pcmark; /* previous context mark */
- Xstatic bool_t pcvalid = FALSE; /* true if pcmark is valid */
- X
- X/*
- X * setmark(c) - set mark 'c' at current cursor position
- X *
- X * Returns TRUE on success, FALSE if no room for mark or bad name given.
- X */
- Xbool_t
- Xsetmark(c)
- Xregister char c;
- X{
- X register int i;
- X
- X if (!isalpha(c))
- X return FALSE;
- X
- X /*
- X * If there is already a mark of this name, then just use the
- X * existing mark entry.
- X */
- X for (i=0; i < NMARKS ;i++) {
- X if (mlist[i].name == c) {
- X mlist[i].pos = *Curschar;
- X return TRUE;
- X }
- X }
- X
- X /*
- X * There wasn't a mark of the given name, so find a free slot
- X */
- X for (i=0; i < NMARKS ;i++) {
- X if (mlist[i].name == NUL) { /* got a free one */
- X mlist[i].name = c;
- X mlist[i].pos = *Curschar;
- X return TRUE;
- X }
- X }
- X return FALSE;
- X}
- X
- X/*
- X * setpcmark() - set the previous context mark to the current position
- X */
- Xvoid
- Xsetpcmark()
- X{
- X pcmark.pos = *Curschar;
- X pcvalid = TRUE;
- X}
- X
- X/*
- X * getmark(c) - find mark for char 'c'
- X *
- X * Return pointer to LPTR or NULL if no such mark.
- X */
- XLPTR *
- Xgetmark(c)
- Xregister char c;
- X{
- X register int i;
- X
- X if (c == '\'' || c == '`') /* previous context mark */
- X return pcvalid ? &(pcmark.pos) : (LPTR *) NULL;
- X
- X for (i=0; i < NMARKS ;i++) {
- X if (mlist[i].name == c)
- X return &(mlist[i].pos);
- X }
- X return (LPTR *) NULL;
- X}
- X
- X/*
- X * clrall() - clear all marks
- X *
- X * Used mainly when trashing the entire buffer during ":e" type commands
- X */
- Xvoid
- Xclrall()
- X{
- X register int i;
- X
- X for (i=0; i < NMARKS ;i++)
- X mlist[i].name = NUL;
- X pcvalid = FALSE;
- X}
- X
- X/*
- X * clrmark(line) - clear any marks for 'line'
- X *
- X * Used any time a line is deleted so we don't have marks pointing to
- X * non-existent lines.
- X */
- Xvoid
- Xclrmark(line)
- Xregister LINE *line;
- X{
- X register int i;
- X
- X for (i=0; i < NMARKS ;i++) {
- X if (mlist[i].pos.linep == line)
- X mlist[i].name = NUL;
- X }
- X if (pcvalid && (pcmark.pos.linep == line))
- X pcvalid = FALSE;
- X}
- !EOR!
- echo extracting - misccmds.c
- sed 's/^X//' > misccmds.c << '!EOR!'
- X/* $Header: /nw/tony/src/stevie/src/RCS/misccmds.c,v 1.14 89/08/06 09:50:17 tony Exp $
- X *
- X * Various routines to perform specific editing operations or return
- X * useful information about the file.
- X */
- X
- X#include "stevie.h"
- X
- Xstatic void openfwd(), openbwd();
- X
- Xextern bool_t did_ai;
- X
- X/*
- X * opencmd
- X *
- X * Add a blank line above or below the current line.
- X */
- X
- Xvoid
- Xopencmd(dir, can_ai)
- Xint dir;
- Xint can_ai; /* if true, consider auto-indent */
- X{
- X if (dir == FORWARD)
- X openfwd(can_ai);
- X else
- X openbwd(can_ai);
- X}
- X
- Xstatic void
- Xopenfwd(can_ai)
- Xint can_ai;
- X{
- X register LINE *l;
- X LPTR *next;
- X register char *s; /* string to be moved to new line, if any */
- X int newindex = 0; /* index of the cursor on the new line */
- X
- X /*
- X * If we're in insert mode, we need to move the remainder of the
- X * current line onto the new line. Otherwise the new line is left
- X * blank.
- X */
- X if (State == INSERT || State == REPLACE)
- X s = &Curschar->linep->s[Curschar->index];
- X else
- X s = "";
- X
- X if ((next = nextline(Curschar)) == NULL) /* open on last line */
- X next = Fileend;
- X
- X /*
- X * By asking for as much space as the prior line had we make sure
- X * that we'll have enough space for any auto-indenting.
- X */
- X if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL)
- X return;
- X
- X if (*s != NUL)
- X strcpy(l->s, s); /* copy string to new line */
- X
- X else if (can_ai && P(P_AI) && !anyinput()) {
- X char *p;
- X
- X /*
- X * Copy prior line, and truncate after white space
- X */
- X strcpy(l->s, Curschar->linep->s);
- X
- X for (p = l->s; *p == ' ' || *p == TAB ;p++)
- X ;
- X *p = NUL;
- X newindex = p - l->s;
- X
- X /*
- X * If we just did an auto-indent, then we didn't type
- X * anything on the prior line, and it should be truncated.
- X */
- X if (did_ai)
- X Curschar->linep->s[0] = NUL;
- X
- X did_ai = TRUE;
- X }
- X
- X /* truncate current line at cursor */
- X if (State == INSERT || State == REPLACE)
- X *s = NUL;
- X
- X
- X Curschar->linep->next = l; /* link neighbors to new line */
- X next->linep->prev = l;
- X
- X l->prev = Curschar->linep; /* link new line to neighbors */
- X l->next = next->linep;
- X
- X if (next == Fileend) /* new line at end */
- X l->num = Curschar->linep->num + LINEINC;
- X
- X else if ((l->prev->num) + 1 == l->next->num) /* no gap, renumber */
- X renum();
- X
- X else { /* stick it in the middle */
- X unsigned long lnum;
- X lnum = ((long)l->prev->num + (long)l->next->num) / 2;
- X l->num = lnum;
- X }
- X
- X /*
- X * Get the cursor to the start of the line, so that 'Cursrow'
- X * gets set to the right physical line number for the stuff
- X * that follows...
- X */
- X Curschar->index = 0;
- X cursupdate();
- X
- X /*
- X * If we're doing an open on the last logical line, then
- X * go ahead and scroll the screen up. Otherwise, just insert
- X * a blank line at the right place. We use calls to plines()
- X * in case the cursor is resting on a long line.
- X */
- X if (Cursrow + plines(Curschar) == (Rows - 1))
- X scrollup(1);
- X else
- X s_ins(Cursrow+plines(Curschar), 1);
- X
- X *Curschar = *nextline(Curschar); /* cursor moves down */
- X Curschar->index = newindex;
- X
- X updatescreen(); /* because Botchar is now invalid... */
- X
- X cursupdate(); /* update Cursrow before insert */
- X}
- X
- Xstatic void
- Xopenbwd(can_ai)
- Xint can_ai;
- X{
- X register LINE *l;
- X LINE *prev;
- X int newindex = 0;
- X
- X prev = Curschar->linep->prev;
- X
- X if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL)
- X return;
- X
- X Curschar->linep->prev = l; /* link neighbors to new line */
- X prev->next = l;
- X
- X l->next = Curschar->linep; /* link new line to neighbors */
- X l->prev = prev;
- X
- X if (can_ai && P(P_AI) && !anyinput()) {
- X char *p;
- X
- X /*
- X * Copy current line, and truncate after white space
- X */
- X strcpy(l->s, Curschar->linep->s);
- X
- X for (p = l->s; *p == ' ' || *p == TAB ;p++)
- X ;
- X *p = NUL;
- X newindex = p - l->s;
- X
- X did_ai = TRUE;
- X }
- X
- X Curschar->linep = Curschar->linep->prev;
- X Curschar->index = newindex;
- X
- X if (prev == Filetop->linep) /* new start of file */
- X Filemem->linep = l;
- X
- X renum(); /* keep it simple - we don't do this often */
- X
- X cursupdate(); /* update Cursrow before insert */
- X if (Cursrow != 0)
- X s_ins(Cursrow, 1); /* insert a physical line */
- X
- X updatescreen();
- X}
- X
- Xint
- Xcntllines(pbegin,pend)
- Xregister LPTR *pbegin, *pend;
- X{
- X register LINE *lp;
- X int lnum = 1;
- X
- X for (lp = pbegin->linep; lp != pend->linep ;lp = lp->next)
- X lnum++;
- X
- X return(lnum);
- X}
- X
- X/*
- X * plines(p) - return the number of physical screen lines taken by line 'p'
- X */
- Xint
- Xplines(p)
- XLPTR *p;
- X{
- X register int col = 0;
- X register char *s;
- X
- X s = p->linep->s;
- X
- X if (*s == NUL) /* empty line */
- X return 1;
- X
- X for (; *s != NUL ;s++) {
- X if ( *s == TAB && !P(P_LS))
- X col += P(P_TS) - (col % P(P_TS));
- X else
- X col += chars[(unsigned)(*s & 0xff)].ch_size;
- X }
- X
- X /*
- X * If list mode is on, then the '$' at the end of
- X * the line takes up one extra column.
- X */
- X if (P(P_LS))
- X col += 1;
- X /*
- X * If 'number' mode is on, add another 8.
- X */
- X if (P(P_NU))
- X col += 8;
- X
- X return ((col + (Columns-1)) / Columns);
- X}
- X
- Xvoid
- Xfileinfo()
- X{
- X extern int numfiles, curfile;
- X register long l1, l2;
- X
- X if (bufempty()) {
- X l1 = 0;
- X l2 = 1; /* don't div by zero */
- X } else {
- X l1 = cntllines(Filemem, Curschar);
- X l2 = cntllines(Filemem, Fileend) - 1;
- X }
- X
- X if (numfiles > 1)
- X smsg("\"%s\"%s line %ld of %ld -- %ld %% -- (file %d of %d)",
- X (Filename != NULL) ? Filename : "No File",
- X Changed ? " [Modified]" : "",
- X l1, l2, (l1 * 100)/l2,
- X curfile+1, numfiles);
- X else
- X smsg("\"%s\"%s line %ld of %ld -- %ld %% --",
- X (Filename != NULL) ? Filename : "No File",
- X Changed ? " [Modified]" : "",
- X l1, l2, (l1 * 100)/l2);
- X}
- X
- X/*
- X * gotoline(n) - return a pointer to line 'n'
- X *
- X * Returns a pointer to the last line of the file if n is zero, or
- X * beyond the end of the file.
- X */
- XLPTR *
- Xgotoline(n)
- Xregister int n;
- X{
- X static LPTR l;
- X
- X l.index = 0;
- X
- X if ( n == 0 )
- X l = *prevline(Fileend);
- X else {
- X LPTR *p;
- X
- X for (l = *Filemem; --n > 0 ;l = *p)
- X if ((p = nextline(&l)) == NULL)
- X break;
- X }
- X return &l;
- X}
- X
- Xvoid
- Xinschar(c)
- Xint c;
- X{
- X register char *p, *pend;
- X
- X /* make room for the new char. */
- X if ( ! canincrease(1) )
- X return;
- X
- X if (State != REPLACE) {
- X p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1];
- X pend = &Curschar->linep->s[Curschar->index];
- X
- X for (; p > pend ;p--)
- X *p = *(p-1);
- X
- X *p = c;
- X
- X } else { /* replace mode */
- X /*
- X * Once we reach the end of the line, we are effectively
- X * inserting new text, so make sure the string terminator
- X * stays out there.
- X */
- X if (gchar(Curschar) == NUL)
- X Curschar->linep->s[Curschar->index+1] = NUL;
- X pchar(Curschar, c);
- X }
- X
- X /*
- X * If we're in insert mode and showmatch mode is set, then
- X * check for right parens and braces. If there isn't a match,
- X * then beep. If there is a match AND it's on the screen, then
- X * flash to it briefly. If it isn't on the screen, don't do anything.
- X */
- X if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) {
- X LPTR *lpos, csave;
- X
- X if ((lpos = showmatch()) == NULL) /* no match, so beep */
- X beep();
- X else if (LINEOF(lpos) >= LINEOF(Topchar)) {
- X updatescreen(); /* show the new char first */
- X csave = *Curschar;
- X *Curschar = *lpos; /* move to matching char */
- X cursupdate();
- X windgoto(Cursrow, Curscol);
- X pause(); /* brief pause */
- X *Curschar = csave; /* restore cursor position */
- X cursupdate();
- X }
- X }
- X
- X inc(Curschar);
- X CHANGED;
- X}
- X
- Xbool_t
- Xdelchar(fixpos)
- Xbool_t fixpos; /* if TRUE, fix the cursor position when done */
- X{
- X register int i;
- X
- X /* Check for degenerate case; there's nothing in the file. */
- X if (bufempty())
- X return FALSE;
- X
- X if (lineempty()) /* can't do anything */
- X return FALSE;
- X
- X /* Delete the char. at Curschar by shifting everything */
- X /* in the line down. */
- X for ( i=Curschar->index+1; i < Curschar->linep->size ;i++)
- X Curschar->linep->s[i-1] = Curschar->linep->s[i];
- X
- X /* If we just took off the last character of a non-blank line, */
- X /* we don't want to end up positioned at the newline. */
- X if (fixpos) {
- X if (gchar(Curschar)==NUL && Curschar->index>0 && State!=INSERT)
- X Curschar->index--;
- X }
- X CHANGED;
- X
- X return TRUE;
- X}
- X
- X
- Xvoid
- Xdelline(nlines, can_update)
- Xint nlines;
- Xbool_t can_update;
- X{
- X register LINE *p, *q;
- X int doscreen; /* if true, update the screen */
- X
- X doscreen = can_update;
- X /*
- X * There's no point in keeping the screen updated if we're
- X * deleting more than a screen's worth of lines.
- X */
- X if (nlines > (Rows - 1) && can_update) {
- X doscreen = FALSE;
- X s_del(Cursrow, Rows-1); /* flaky way to clear rest of screen */
- X }
- X
- X while ( nlines-- > 0 ) {
- X
- X if (bufempty()) /* nothing to delete */
- X break;
- X
- X if (buf1line()) { /* just clear the line */
- X Curschar->linep->s[0] = NUL;
- X Curschar->index = 0;
- X break;
- X }
- X
- X p = Curschar->linep->prev;
- X q = Curschar->linep->next;
- X
- X if (p == Filetop->linep) { /* first line of file so... */
- X Filemem->linep = q; /* adjust start of file */
- X Topchar->linep = q; /* and screen */
- X }
- X p->next = q;
- X q->prev = p;
- X
- X clrmark(Curschar->linep); /* clear marks for the line */
- X
- X /*
- X * Delete the correct number of physical lines on the screen
- X */
- X if (doscreen)
- X s_del(Cursrow, plines(Curschar));
- X
- X /*
- X * If deleting the top line on the screen, adjust Topchar
- X */
- X if (Topchar->linep == Curschar->linep)
- X Topchar->linep = q;
- X
- X free(Curschar->linep->s);
- X free((char *) Curschar->linep);
- X
- X Curschar->linep = q;
- X Curschar->index = 0; /* is this right? */
- X CHANGED;
- X
- X /* If we delete the last line in the file, back up */
- X if ( Curschar->linep == Fileend->linep) {
- X Curschar->linep = Curschar->linep->prev;
- X /* and don't try to delete any more lines */
- X break;
- X }
- X }
- X}
- !EOR!
- echo extracting - normal.c
- sed 's/^X//' > normal.c << '!EOR!'
- 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_CCIRCM: /* shorthand command */
- X CLEAROP;
- X#ifdef TAGSTACK
- X /* If tag stacking compiled in & enabled, this is an untag.
- X * Otherwise, or if tag stack empty, edit alternate file.
- X * "untage" is so interpreted by dountag().
- X */
- X if (P(P_TG))
- X stuffin(":untage\n");
- X else
- X#endif
- 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 '(': /* sentence searches */
- X dir = BACKWARD;
- X /* fall through */
- X
- X case ')':
- X mtype = MCHAR;
- X mincl = FALSE;
- X set_want_col = TRUE;
- X if (findsent(dir) == NULL) {
- X beep();
- X CLEAROP;
- X }
- X break;
- X
- X case '{': /* paragraph searches */
- X dir = BACKWARD;
- X /* fall through */
- X
- X case '}':
- X mtype = MCHAR;
- X mincl = FALSE;
- X set_want_col = TRUE;
- X if (!findpara(dir)) {
- X beep();
- X CLEAROP;
- 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==CR || nchar==NL) {
- X stuffin("R\n\033");
- X break;
- X }
- X
- X if (nchar & 0x80) {
- 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;
- X
- X mark = getmark(vgetc());
- 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 = FALSE; /* 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}
- !EOR!
- echo extracting - ops.c
- sed 's/^X//' > ops.c << '!EOR!'
- 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 int botindex;
- 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 delete anyway");
- 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 /* Don't leave even the potential for orphan marks */
- X clrmark (top.linep);
- X
- X nlines = cntllines(&top, &bot);
- X *Curschar = top;
- X cursupdate();
- X
- X if (mtype == MLINE) {
- X delline(nlines, TRUE);
- X } else {
- X botindex = -1;
- X if (!mincl) {
- X botindex = bot.index; /* where it WAS */
- X if (bot.index != 0)
- X dec(&bot);
- X }
- 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-- && botindex)
- 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 else /* already first column */
- X bot = *( prevchar (&bot));
- 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 if (!s) return;
- 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}
- !EOR!
-
-
-