/* $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 #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 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; }