|
|
/* $Header: /nw/tony/src/stevie/src/RCS/ops.c,v 1.5 89/08/06 09:50:42 tony Exp $
* * Contains routines that implement the operators in vi. Everything in this * file is called only from code in normal.c */
#include "stevie.h"
#include <io.h>
#include "ops.h"
char *lastcmd = NULL;/* the last thing we did */
static void inslines(); static void tabinout();
/*
* doshift - handle a shift operation */ void doshift(op, c1, c2, num) int op; char c1, c2; int num; { LNPTR top, bot; int nlines; char opchar;
top = startop; bot = *Curschar;
if (lt(&bot, &top)) pswap(&top, &bot);
u_save(top.linep->prev, bot.linep->next);
nlines = cntllines(&top, &bot); *Curschar = top; tabinout((op == LSHIFT), nlines);
/* construct Redo buff */ opchar = (char)((op == LSHIFT) ? '<' : '>'); if (num != 0) sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2); else sprintf(Redobuff, "%c%c%c", opchar, c1, c2);
/*
* The cursor position afterward is the prior of the two positions. */ *Curschar = top;
/*
* If we were on the last char of a line that got shifted left, * then move left one so we aren't beyond the end of the line */ if (gchar(Curschar) == NUL && Curschar->index > 0) Curschar->index--;
updatescreen();
if (nlines > P(P_RP)) smsg("%d lines %ced", nlines, opchar); }
/*
* dodelete - handle a delete operation */ void dodelete(c1, c2, num) char c1, c2; int num; { LNPTR top, bot; int nlines; register int n;
/*
* Do a yank of whatever we're about to delete. If there's too much * stuff to fit in the yank buffer, then get a confirmation before * doing the delete. This is crude, but simple. And it avoids doing * a delete of something we can't put back if we want. */ if (!doyank()) { msg("yank buffer exceeded: press <y> to confirm"); if (vgetc() != 'y') { msg("delete aborted"); *Curschar = startop; return; } }
top = startop; bot = *Curschar;
if (lt(&bot, &top)) pswap(&top, &bot);
u_save(top.linep->prev, bot.linep->next);
nlines = cntllines(&top, &bot); *Curschar = top; cursupdate();
if (mtype == MLINE) { delline(nlines, TRUE); } else { if (!mincl && bot.index != 0) dec(&bot);
if (top.linep == bot.linep) { /* del. within line */ n = bot.index - top.index + 1; while (n--) if (!delchar(TRUE)) break; } else { /* del. between lines */ n = Curschar->index; while (Curschar->index >= n) if (!delchar(TRUE)) break;
top = *Curschar; *Curschar = *nextline(Curschar); delline(nlines-2, TRUE); Curschar->index = 0; n = bot.index + 1; while (n--) if (!delchar(TRUE)) break; *Curschar = top; (void) dojoin(FALSE); oneright(); /* we got bumped left up above */ } }
/* construct Redo buff */ if (num != 0) sprintf(Redobuff, "d%d%c%c", num, c1, c2); else sprintf(Redobuff, "d%c%c", c1, c2);
if (mtype == MCHAR && nlines == 1) updateline(); else updatescreen();
if (nlines > P(P_RP)) smsg("%d fewer lines", nlines); }
/*
* dofilter - handle a filter operation */
#define ITMP "viXXXXXX"
#define OTMP "voXXXXXX"
static char itmp[32]; static char otmp[32];
/*
* dofilter - filter lines through a command given by the user * * We use temp files and the system() routine here. This would normally * be done using pipes on a UNIX machine, but this is more portable to * the machines we usually run on. The system() routine needs to be able * to deal with redirection somehow, and should handle things like looking * at the PATH env. variable, and adding reasonable extensions to the * command name given by the user. All reasonable versions of system() * do this. */ void dofilter(c1, c2, num) char c1, c2; int num; { char *buff; /* cmd buffer from getcmdln() */ char cmdln[200]; /* filtering command line */ LNPTR top, bot; int nlines;
top = startop; bot = *Curschar;
buff = getcmdln('!');
if (buff == NULL) /* user backed out of the command prompt */ return;
if (*buff == '!') { /* use the 'last' command */ if (lastcmd == NULL) { emsg("No previous command"); return; } buff = lastcmd; }
/*
* Remember the current command */ if (lastcmd != NULL) free(lastcmd); lastcmd = strsave(buff);
if (lt(&bot, &top)) pswap(&top, &bot);
u_save(top.linep->prev, bot.linep->next);
nlines = cntllines(&top, &bot); *Curschar = top; cursupdate();
/*
* 1. Form temp file names * 2. Write the lines to a temp file * 3. Run the filter command on the temp file * 4. Read the output of the command into the buffer * 5. Delete the original lines to be filtered * 6. Remove the temp files */
#ifdef TMPDIR
strcpy(itmp, TMPDIR); strcpy(otmp, TMPDIR); #else
itmp[0] = otmp[0] = NUL; #endif
strcat(itmp, ITMP); strcat(otmp, OTMP);
if (_mktemp(itmp) == NULL || _mktemp(otmp) == NULL) { emsg("Can't get temp file names"); return; }
if (!writeit(itmp, &top, &bot)) { emsg("Can't create input temp file"); return; }
sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp);
if (system(cmdln) != 0) { emsg("Filter command failed"); remove(ITMP); return; }
if (readfile(otmp, &bot, TRUE)) { emsg("Can't read filter output"); return; }
delline(nlines, TRUE);
remove(itmp); remove(otmp);
/* construct Redo buff */ if (num != 0) sprintf(Redobuff, "d%d%c%c", num, c1, c2); else sprintf(Redobuff, "d%c%c", c1, c2);
updatescreen();
if (nlines > P(P_RP)) smsg("%d lines filtered", nlines); }
#ifdef TILDEOP
void dotilde(c1, c2, num) char c1, c2; int num; { LNPTR top, bot; register char c;
/* construct Redo buff */ if (num != 0) sprintf(Redobuff, "~%d%c%c", num, c1, c2); else sprintf(Redobuff, "~%c%c", c1, c2);
top = startop; bot = *Curschar;
if (lt(&bot, &top)) pswap(&top, &bot);
u_save(top.linep->prev, bot.linep->next);
if (mtype == MLINE) { top.index = 0; bot.index = strlen(bot.linep->s); } else { if (!mincl) { if (bot.index) bot.index--; } }
for (; ltoreq(&top, &bot) ;inc(&top)) { /*
* Swap case through the range */ c = (char)gchar(&top); if (isalpha(c)) { if (islower(c)) c = (char)toupper(c); else c = (char)tolower(c);
pchar(&top, c); /* Change current character. */ CHANGED; } } *Curschar = startop; updatescreen(); } #endif
/*
* dochange - handle a change operation */ void dochange(c1, c2, num) char c1, c2; int num; { char sbuf[16]; bool_t doappend; /* true if we should do append, not insert */ bool_t at_eof; /* changing through the end of file */ LNPTR top, bot;
top = startop; bot = *Curschar;
if (lt(&bot, &top)) pswap(&top, &bot);
doappend = endofline(&bot); at_eof = (bot.linep->next == Fileend->linep);
dodelete(c1, c2, num);
if (mtype == MLINE) { /*
* If we made a change through the last line of the file, * then the cursor got backed up, and we need to open a * new line forward, otherwise we go backward. */ if (at_eof) opencmd(FORWARD, FALSE); else opencmd(BACKWARD, FALSE); } else { if (doappend && !lineempty()) inc(Curschar); }
if (num) sprintf(sbuf, "c%d%c%c", num, c1, c2); else sprintf(sbuf, "c%c%c", c1, c2);
startinsert(sbuf, mtype == MLINE); }
/*
* docasechange - handle a case change operation */ void docasechange(char c1, char c2, int num, bool_t fToUpper) { LNPTR top, bot; register char c;
/* construct Redo buff */ if (num != 0) sprintf(Redobuff, "%c%d%c%c", fToUpper ? 'V' : 'v',num, c1, c2); else sprintf(Redobuff, "%c%c%c", fToUpper ? 'V' : 'v',c1, c2);
top = startop; bot = *Curschar;
if (lt(&bot, &top)) pswap(&top, &bot);
u_save(top.linep->prev, bot.linep->next);
if (mtype == MLINE) { top.index = 0; bot.index = strlen(bot.linep->s); } else { if (!mincl) { if (bot.index) bot.index--; } }
for (; ltoreq(&top, &bot) ;inc(&top)) { /*
* change case through the range */ c = (char)gchar(&top); if (isalpha(c)) {
c = fToUpper ? (char)toupper(c) : (char)tolower(c);
pchar(&top, c); /* Change current character. */ CHANGED; } } *Curschar = startop; updatescreen(); }
#define YBSLOP 2048 // yank buffer initial and incr size
char *YankBuffers[27]; int CurrentYBSize[27]; int ybtype[27];
void inityank() { int i;
for(i=0; i<27; i++) { ybtype[i] = MBAD; if((YankBuffers[i] = malloc(CurrentYBSize[i] = YBSLOP)) == NULL) { fprintf(stderr,"Cannot allocate initial yank buffers\n"); windexit(1); } YankBuffers[i][0] = '\0'; } }
void GetBufferIndex(int *index,bool_t *append) { int i; bool_t a = FALSE;
if(namedbuff == -1) { i = 26; } else if(islower(namedbuff)) { i = namedbuff - (int)'a'; } else { i = namedbuff - (int)'A'; a = TRUE; } *index = i; *append = a; return; }
bool_t doyank() { LNPTR top, bot; char *ybuf; char *ybstart; char *ybend; char *yptr; int nlines; int buffindex; bool_t buffappend;
GetBufferIndex(&buffindex,&buffappend); namedbuff = -1;
if(!buffappend) { // the given buffer may have grown huge. Shrink it here. The
// realloc should never fail because the buffer is either being
// shrunk or is staying the same size.
YankBuffers[buffindex] = ralloc(YankBuffers[buffindex],YBSLOP); CurrentYBSize[buffindex] = YBSLOP; }
ybuf = YankBuffers[buffindex];
ybstart = ybuf; yptr = ybstart; if(buffappend) { yptr += strlen(ybstart); } ybend = &ybuf[CurrentYBSize[buffindex]-1];
top = startop; bot = *Curschar;
if (lt(&bot, &top)) pswap(&top, &bot);
nlines = cntllines(&top, &bot);
ybtype[buffindex] = mtype; /* set the yank buffer type */
if (mtype == MLINE) { top.index = 0; bot.index = strlen(bot.linep->s); /*
* The following statement checks for the special case of * yanking a blank line at the beginning of the file. If * not handled right, we yank an extra char (a newline). */ if (dec(&bot) == -1) { *yptr = NUL; if (operator == YANK) *Curschar = startop; return TRUE; } } else { if (!mincl) { if (bot.index) bot.index--; } }
for (; ltoreq(&top, &bot) ;inc(&top)) {
// See if we've filled the buffer as currently
// allocated. If so, reallocate the buffer and
// update pointers accordingly before we store the
// current character. This is necessary because we will
// always be storing at least one more char (the NUL)
// and probably more.
if(yptr == ybend) { ybstart = ralloc(ybuf,CurrentYBSize[buffindex] + YBSLOP); if(ybstart == NULL) { ybtype[buffindex] = MBAD; return(FALSE); } CurrentYBSize[buffindex] += YBSLOP; yptr += ybstart - ybuf; ybend = &ybstart[CurrentYBSize[buffindex] - 1]; ybuf = ybstart; YankBuffers[buffindex] = ybuf; }
*yptr++ = (char)((gchar(&top) != NUL) ? gchar(&top) : NL); }
*yptr = NUL;
if (operator == YANK) { /* restore Curschar if really doing yank */ *Curschar = startop;
if (nlines > P(P_RP)) smsg("%d lines yanked", nlines); } return TRUE; }
/*
* doput(dir) * * Put the yank buffer at the current location, using the direction given * by 'dir'. */ void doput(dir) int dir; { int buffindex; bool_t buffappend; char *ybuf; int nb = namedbuff;
GetBufferIndex(&buffindex,&buffappend); namedbuff = -1; ybuf = YankBuffers[buffindex];
if (ybtype[buffindex] == MBAD) { char msgbuff[30]; sprintf(msgbuff,"Nothing in register %c",nb); emsg(msgbuff); return; }
u_saveline();
if (ybtype[buffindex] == MLINE) inslines(Curschar->linep, dir, ybuf); else { /*
* If we did a character-oriented yank, and the buffer * contains multiple lines, the situation is more complex. * For the moment, we punt, and pretend the user did a * line-oriented yank. This doesn't actually happen that * often. */ if (strchr(ybuf, NL) != NULL) inslines(Curschar->linep, dir, ybuf); else { char *s; int len;
len = strlen(Curschar->linep->s) + strlen(ybuf) + 1; s = alloc((unsigned) len); strcpy(s, Curschar->linep->s); if (dir == FORWARD) Curschar->index++; strcpy(s + Curschar->index, ybuf); strcat(s, &Curschar->linep->s[Curschar->index]); free(Curschar->linep->s); Curschar->linep->s = s; Curschar->linep->size = len; updateline(); } }
CHANGED; }
bool_t dojoin(join_cmd) bool_t join_cmd; /* handling a real "join" command? */ { int scol; /* save cursor column */ int size; /* size of the joined line */
if (nextline(Curschar) == NULL) /* on last line */ return FALSE;
if (!canincrease(size = strlen(Curschar->linep->next->s))) return FALSE;
while (oneright()) /* to end of line */ ;
strcat(Curschar->linep->s, Curschar->linep->next->s);
/*
* Delete the following line. To do this we move the cursor * there briefly, and then move it back. Don't back up if the * delete made us the last line. */ Curschar->linep = Curschar->linep->next; scol = Curschar->index;
if (nextline(Curschar) != NULL) { delline(1, TRUE); Curschar->linep = Curschar->linep->prev; } else delline(1, TRUE);
Curschar->index = scol;
if (join_cmd) oneright(); /* go to first char. of joined line */
if (join_cmd && size != 0) { /*
* Delete leading white space on the joined line * and insert a single space. */ while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) delchar(TRUE); inschar(' '); }
return TRUE; }
void startinsert(initstr, startln) char *initstr; int startln; /* if set, insert point really at start of line */ { register char *p, c;
*Insstart = *Curschar; if (startln) Insstart->index = 0; Ninsert = 0; Insptr = Insbuff; for (p=initstr; (c=(*p++))!='\0'; ) { *Insptr++ = c; Ninsert++; }
if (*initstr == 'R') State = REPLACE; else State = INSERT;
if (P(P_MO)) msg((State == INSERT) ? "Insert Mode" : "Replace Mode"); } /*
* tabinout(inout,num) * * If inout==0, add a tab to the begining of the next num lines. * If inout==1, delete a tab from the beginning of the next num lines. */ static void tabinout(inout, num) int inout; int num; { int ntodo = num; int c; int col; LNPTR *p;
while (ntodo-- > 0) {
beginline(FALSE);
/*
* eat leading space, calc the column of first non-white */ col = 0; while ((c = gchar(Curschar)) == ' ' || c == TAB) { if (c == ' ') { ++col; } else { col += P(P_TS); col -= (col % P(P_TS)); } delchar(TRUE); }
/*
* add or subtract shiftwidth spaces */
if (inout == 0) { col += P(P_SW); } else { col -= P(P_SW); }
if (col < 0) { col = 0; }
/*
* insert space, using TABS if hardtabs is set */ while (col % P(P_TS)) { inschar(' '); col--; } if (P(P_HT)) { while (col) { inschar(TAB); col -= P(P_TS); } } else { while (col--) { inschar(' '); } }
/*
* next line */ if ( ntodo > 0 ) { if ((p = nextline(Curschar)) != NULL) { *Curschar = *p; } } } }
/*
* inslines(lp, dir, buf) * * Inserts lines in the file from the given buffer. Lines are inserted * before or after "lp" according to the given direction flag. Newlines * in the buffer result in multiple lines being inserted. The cursor * is left on the first of the inserted lines. */ static void inslines(lp, dir, buf) LINE *lp; int dir; char *buf; { register char *cp = buf; register size_t len; char *ep; LINE *l, *nc = NULL;
if (dir == BACKWARD) lp = lp->prev;
do { if ((ep = strchr(cp, NL)) == NULL) len = strlen(cp); else len = (size_t)(ep - cp);
l = newline(len); if (len != 0) strncpy(l->s, cp, len); l->s[len] = NUL;
l->next = lp->next; l->prev = lp; lp->next->prev = l; lp->next = l;
if (nc == NULL) nc = l;
lp = lp->next;
cp = ep + 1; } while (ep != NULL);
if (dir == BACKWARD) /* fix the top line in case we were there */ Filemem->linep = Filetop->linep->next;
renum();
updatescreen(); Curschar->linep = nc; Curschar->index = 0; }
|