Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 3a9bb55684
Fetching contributors…

Cannot retrieve contributors at this time

766 lines (653 sloc) 18.649 kB
/* exedit.c */
/* Copyright 1995 by Steve Kirkendall */
char id_exedit[] = "$Id: exedit.c,v 2.50 1999/02/26 21:28:39 steve Exp $";
#include "elvis.h"
/* This command implements the :insert, :append, and :change commands */
RESULT ex_append(xinf)
EXINFO *xinf;
{
MARK where;
assert(xinf->command == EX_APPEND || xinf->command == EX_CHANGE
|| xinf->command == EX_INSERT);
/* different behavior, depending on the command... */
if (xinf->command == EX_CHANGE)
{
cutyank('\0', xinf->fromaddr, xinf->toaddr, 'L', True);
where = markdup(xinf->fromaddr);
}
else if (xinf->command == EX_APPEND)
{
where = markdup(xinf->toaddr);
}
else
{
where = markdup(xinf->fromaddr);
}
/* Was the new text given as an argument to the command? */
if (xinf->rhs)
{
/* yes, insert the new text at the appropriate place */
bufreplace(where, where, xinf->rhs, (long)CHARlen(xinf->rhs));
markaddoffset(where, CHARlen(xinf->rhs));
}
xinf->newcurs = where;
return RESULT_COMPLETE;
}
/* This function implements the :delete and :yank command */
RESULT ex_delete(xinf)
EXINFO *xinf;
{
/* check the cut buffer name */
if (xinf->cutbuf && !isalnum(xinf->cutbuf) &&
xinf->cutbuf != '^' && xinf->cutbuf != '<' && xinf->cutbuf != '>')
{
msg(MSG_ERROR, "bad cut buffer");
return RESULT_ERROR;
}
/* do the command */
cutyank(xinf->cutbuf, xinf->fromaddr, xinf->toaddr, (CHAR)'L', (BOOLEAN)(xinf->command == EX_DELETE));
return RESULT_COMPLETE;
}
RESULT ex_global(xinf)
EXINFO *xinf;
{
CHAR *cp;
long lenln, endln;
RESULT ret = RESULT_COMPLETE;
MARK cursor;
MARK orig;
MARKBUF thisln;
long origlines;
assert(xinf->command == EX_GLOBAL || xinf->command == EX_VGLOBAL);
/* Default command is p, which should NOT be required */
if (!xinf->rhs)
xinf->rhs = CHARdup(toCHAR("p"));
/* a command is required */
if (!xinf->rhs)
{
msg(MSG_ERROR, "[s]$1 requires a command", xinf->cmdname);
return RESULT_ERROR;
}
#if 0
/* only works when applied to window's main buffer */
if (!xinf->window->state->acton || xinf->window->state->acton->acton)
{
msg(MSG_ERROR, "[s]$1 only works on main buffer", xinf->cmdname);
return RESULT_ERROR;
}
#endif
/* ":g!" is like ":v" */
if (xinf->bang)
xinf->command = EX_VGLOBAL;
/* remember the cursor's original position. Inside the following loop,
* we'll force the cursor onto each matching line; when we're done,
* we'll move the cursor back and return the final position so the
* experform() function can move the cursor there in the conventional
* way -- that will be important if we switch buffers.
*/
if (xinf->window->state->acton)
cursor = xinf->window->state->acton->cursor;
else
cursor = xinf->window->cursor;
if (markbuffer(cursor) != markbuffer(xinf->fromaddr))
{
marksetbuffer(cursor, markbuffer(xinf->fromaddr));
marksetoffset(cursor, markoffset(xinf->fromaddr));
}
orig = markdup(cursor);
/* remember the number of lines before any changes... */
origlines = o_buflines(markbuffer(xinf->fromaddr));
/* for each line... */
(void)scanalloc(&cp, xinf->fromaddr);
ret = RESULT_COMPLETE;
while (cp && markoffset(xinf->fromaddr) < markoffset(xinf->toaddr))
{
/* find the end of this line */
for (lenln = 0; cp && *cp != '\n'; lenln++)
(void)scannext(&cp);
if (cp)
{
(void)scannext(&cp);
lenln++;
}
/* move "fromaddr" to end of this line (start of next line) */
thisln = *xinf->fromaddr;
endln = cp ? markoffset(scanmark(&cp)) : markoffset(xinf->toaddr);
marksetoffset(xinf->fromaddr, endln);
/* is this a selected line? */
if ((regexec(xinf->re, &thisln, True) ? EX_GLOBAL : EX_VGLOBAL)
== xinf->command)
{
/* move the cursor to the matching line */
marksetoffset(cursor, endln - lenln);
/* free the scan pointer -- can't make changes
* while scanning.
*/
scanfree(&cp);
/* execute the command */
ret = exstring(xinf->window, xinf->rhs, NULL);
/* reallocate the scan pointer */
(void)scanalloc(&cp, xinf->fromaddr);
/* if the ex command failed, then exit */
if (ret != RESULT_COMPLETE)
break;
}
/* if user wants to abort operation, then exit */
if (guipoll(False))
break;
}
scanfree(&cp);
/* report any change in the number of lines */
if (o_report != 0)
{
origlines -= o_buflines(markbuffer(cursor));
if (origlines >= o_report)
msg(MSG_INFO, "[d]$1 fewer lines", origlines);
else if (-origlines >= o_report)
msg(MSG_INFO, "[d]$1 more lines", -origlines);
}
/* move the cursor back to its original position, and then return
* the final position so the cursor will be moved there in a graceful
* way.
*/
xinf->newcurs = markdup(cursor);
if (markbuffer(cursor) != markbuffer(orig))
marksetbuffer(cursor, markbuffer(orig));
marksetoffset(cursor, markoffset(orig));
markfree(orig);
return ret;
}
RESULT ex_join(xinf)
EXINFO *xinf;
{
CHAR prevchar; /* character before newline */
CHAR *cp; /* used while scanning for newlines */
long njoined; /* number of lines joined */
long newlines; /* number of newlines to be clobbered */
long nspaces; /* number of spaces to insert */
MARK start, end; /* region around a newline */
long offset; /* position of last change */
CHAR *endlist; /* string of sentence ending punctuation */
static CHAR spaces[3] = {' ', ' ', ' '};
/* initialize "offset" just to silence a compiler warning */
offset = 0;
/* initialize endlist from options (if set) or literals */
endlist = (o_sentenceend ? o_sentenceend : toCHAR(".!?"));
/* We're going to be replacing newlines with blanks. The number of
* newlines we want to replace is equal to the number lines affected
* minus one... except that if the user requested a "join" of a single
* line then we should assume we're supposed to join two lines.
*/
newlines = xinf->to - xinf->from;
if (xinf->from + newlines > o_buflines(markbuffer(xinf->fromaddr)))
{
msg(MSG_ERROR, "nothing to join with this line");
return RESULT_ERROR;;
}
njoined = newlines + 1;
/* scan the text for newlines */
prevchar = ' ';
for (scanalloc(&cp, xinf->fromaddr); cp && newlines > 0; scannext(&cp))
{
/* if newline, then clobber it */
if (*cp == '\n')
{
start = markdup(scanmark(&cp));
offset = markoffset(start);
if (scannext(&cp))
{
/* figure out how many spaces to insert */
if (xinf->bang || *cp == ')' || isspace(prevchar))
nspaces = 0;
else if (CHARchr(toCHAR(endlist), prevchar))
nspaces = o_sentencegap;
else
nspaces = 1;
/* skip any leading whitespace in next line */
while (!xinf->bang && cp && (*cp == ' ' || *cp == '\t'))
{
scannext(&cp);
}
/* Find the end mark */
end = (cp ? markdup(scanmark(&cp))
: markalloc(markbuffer(start), o_bufchars(markbuffer(start))));
/* free the scan context during the change;
* can't mix scanning & updates.
*/
scanfree(&cp);
/* replace the newline (and trailing whitespace)
* with a given number of spaces.
*/
bufreplace(start, end, spaces, nspaces);
marksetoffset(end, offset + nspaces - 1);
/* NOTE: the "- 1" is to compensate for
* the scannext() at the top of the loop
*/
/* resume scanning */
scanalloc(&cp, end);
markfree(end);
if (nspaces > 0)
{
prevchar = ' ';
}
}
markfree(start);
/* All that to clobber one newline! */
newlines--;
}
else if ((*cp != '"' && *cp != ')') || isspace(prevchar))
{
/* remember the character. When we hit a newline,
* the previous character will be checked to determine
* how many spaces to insert. Note that we ignore
* quote and parenthesis characters except after a
* blank.
*/
prevchar = *cp;
}
}
scanfree(&cp);
/* Choose a new cursor position: at the start of last joint */
xinf->newcurs = markalloc(markbuffer(xinf->fromaddr), offset);
/* Report it */
if (o_report != 0 && njoined >= o_report)
{
msg(MSG_INFO, "[d]$1 lines joined", njoined);
}
return RESULT_COMPLETE;
}
RESULT ex_move(xinf)
EXINFO *xinf;
{
long oldfrom, oldto, olddest, len;
/* detect errors */
oldfrom = markoffset(xinf->fromaddr);
oldto = markoffset(xinf->toaddr);
olddest = markoffset(xinf->destaddr);
if (markbuffer(xinf->fromaddr) == markbuffer(xinf->destaddr)
&& oldfrom < olddest
&& olddest < oldto)
{
msg(MSG_ERROR, "destination can't be inside source");
return RESULT_ERROR;
}
/* copy the text. Adjust the offsets explicitly */
bufpaste(xinf->destaddr, xinf->fromaddr, xinf->toaddr);
len = oldto - oldfrom;
if (olddest <= oldfrom)
{
oldfrom += len;
oldto += len;
}
/* leave the cursor on the last line of the destination */
xinf->newcurs = markalloc(markbuffer(xinf->fromaddr), olddest+len-1);
marksetoffset(xinf->newcurs, markoffset(
(*dmnormal.move)(xinf->window, xinf->newcurs, 0L, 0L, False)));
/* If moving (not copying) then delete source */
if (xinf->command == EX_MOVE)
{
marksetoffset(xinf->fromaddr, oldfrom);
marksetoffset(xinf->toaddr, oldto);
bufreplace(xinf->fromaddr, xinf->toaddr, NULL, 0);
}
return RESULT_COMPLETE;
}
RESULT ex_print(xinf)
EXINFO *xinf;
{
long last; /* offset of start of last line */
PFLAG pflag; /* how to print */
/* generate a pflag from the command name and any supplied pflag */
switch (xinf->pflag)
{
case PF_NONE:
case PF_PRINT:
pflag = (xinf->command == EX_NUMBER || o_number(xinf->window))
? ((xinf->command == EX_LIST || o_list(xinf->window)) ? PF_NUMLIST : PF_NUMBER)
: ((xinf->command == EX_LIST || o_list(xinf->window)) ? PF_LIST : PF_PRINT);
break;
case PF_LIST:
pflag = (xinf->command == EX_NUMBER || o_number(xinf->window)) ? PF_NUMLIST : PF_LIST;
break;
case PF_NUMBER:
pflag = (xinf->command == EX_LIST || o_list(xinf->window))? PF_NUMLIST : PF_NUMBER;
break;
default:
pflag = PF_NUMLIST;
break;
}
/* print the lines */
last = exprintlines(xinf->window, xinf->fromaddr, xinf->to - xinf->from + 1, pflag);
/* disable autoprinting for this command */
xinf->pflag = PF_NONE;
/* leave the cursor at the start of the last line */
xinf->newcurs = markalloc(markbuffer(xinf->fromaddr), last);
return RESULT_COMPLETE;
}
RESULT ex_put(xinf)
EXINFO *xinf;
{
MARK newcurs;
newcurs = cutput(xinf->cutbuf, xinf->window, xinf->fromaddr, (BOOLEAN)(xinf->from > 0), False, False);
if (newcurs)
{
xinf->newcurs = markdup(newcurs);
return RESULT_COMPLETE;
}
return RESULT_ERROR;
}
RESULT ex_read(xinf)
EXINFO *xinf;
{
long offset;
if (!xinf->rhs && xinf->nfiles != 1)
{
msg(MSG_ERROR, "filename required");
return RESULT_ERROR;
}
/* remember where we started inserting */
offset = markoffset(xinf->toaddr);
xinf->newcurs = markdup(xinf->toaddr);
/* read in the text */
if (!bufread(xinf->toaddr, xinf->rhs ? tochar8(xinf->rhs) : xinf->file[0]))
{
return RESULT_ERROR;
}
/* Choose a place to leave the cursor. If reading due to visual <:>
* command, this should be the start of the first line read; else it
* should be the start of the last line read.
*/
if (xinf->window->state->flags & ELVIS_1LINE)
{
marksetoffset(xinf->newcurs, offset);
}
else
{
marksetoffset(xinf->newcurs, markoffset(
(*xinf->window->md->move)(xinf->window, xinf->newcurs, -1, 0, False)));
}
return RESULT_COMPLETE;
}
/* This function implements the :< and :> commands. It is also used to do
* the real work for the visual <<> and <>> operators.
*/
RESULT ex_shift(xinf)
EXINFO *xinf;
{
long shift; /* amount to shift by */
long ws; /* amount of whitespace currently */
CHAR *cp; /* used for scanning through a line's whitespace */
long line; /* used for counting through line numbers */
MARKBUF start; /* start of the line */
MARKBUF end; /* end of the line's whitespace */
CHAR str[50];/* buffer for holding whitespace */
long i;
/* compute the amount of shifting required */
shift = o_shiftwidth(markbuffer(&xinf->defaddr)) * xinf->multi;
if (xinf->command == EX_SHIFTL)
{
shift = -shift;
}
/* for each line... */
start = xinf->defaddr;
for (line = xinf->from; line <= xinf->to; line++)
{
/* count the current whitespace */
scanalloc(&cp, marksetline(&start, line));
for (ws = 0; cp && (*cp == ' ' || *cp == '\t'); scannext(&cp))
{
if (*cp == ' ')
{
ws++;
}
else
{
ws = ws + o_tabstop(markbuffer(&xinf->defaddr))
- ws % o_tabstop(markbuffer(&xinf->defaddr));
}
}
end = *scanmark(&cp);
/* if this is an empty line, and no ! was given on the command
* line, then do nothing to this line.
*/
if (ws == 0 && *cp == '\n' && !xinf->bang)
{
scanfree(&cp);
continue;
}
scanfree(&cp);
/* compute the amount of whitespace we want to have */
ws += shift;
if (ws < 0)
{
ws = 0;
}
/* Replace the old whitespace with new whitespace. Since our
* buffer for holding new whitespace is of limited size, we
* may need to make several bufreplace() calls to do this.
*/
while (markoffset(&start) != markoffset(&end) || ws > 0)
{
/* build new whitespace (as much of it as possible) */
i = 0;
if (o_autotab(markbuffer(&xinf->defaddr)))
{
for (;
ws >= o_tabstop(markbuffer(&xinf->defaddr))
&& i < QTY(str);
i++, ws -= o_tabstop(markbuffer(&xinf->defaddr)))
{
str[i] = '\t';
}
}
for (; ws > 0 && i < QTY(str); i++, ws--)
{
str[i] = ' ';
}
/* replace old whitespace with new */
bufreplace(&start, &end, str, i);
markaddoffset(&start, i);
marksetoffset(&end, markoffset(&start));
}
}
if (o_report != 0 && xinf->to - xinf->from + 1 >= o_report)
msg(MSG_INFO,"[ds]$1 lines $2ed",
xinf->to - xinf->from + 1, xinf->cmdname);
return RESULT_COMPLETE;
}
RESULT ex_undo(xinf)
EXINFO *xinf;
{
long l = 1;
assert(xinf->command == EX_UNDO || xinf->command == EX_REDO);
/* choose an undo/redo level to recover */
if (xinf->lhs)
{
if (!calcnumber(xinf->lhs) || (l = CHAR2long(xinf->lhs)) < 1)
{
msg(MSG_ERROR, "bad undo level");
return RESULT_ERROR;
}
}
/* if redo, then negate the undo value */
if (xinf->command == EX_REDO)
l = -l;
/* try to revert to the undo level */
l = bufundo(xinf->window->cursor, l);
/* if successful, adjust the cursor position */
if (l >= 0)
{
marksetoffset(xinf->window->cursor, l);
return RESULT_COMPLETE;
}
return RESULT_ERROR;
}
RESULT ex_write(xinf)
EXINFO *xinf;
{
char *name;
BOOLEAN success;
if (xinf->rhs)
{
name = tochar8(xinf->rhs);
}
else if (xinf->nfiles >= 1)
{
assert(xinf->nfiles == 1);
name = xinf->file[0];
}
else
{
name = tochar8(o_filename(markbuffer(xinf->fromaddr)));
if (!name)
{
msg(MSG_ERROR, "no file name");
return RESULT_ERROR; /* nishi */
}
}
/* if writing to a different filename, remember that name */
if (name[0] != '!'
&& o_filename(markbuffer(xinf->fromaddr))
&& CHARcmp(name, o_filename(markbuffer(xinf->fromaddr))))
{
if (name[0] == '>' && name[1] == '>')
optprevfile(toCHAR(name + 2), 1);
else
optprevfile(toCHAR(name), 1);
}
/* actually write the file */
success = bufwrite(xinf->fromaddr, xinf->toaddr, name, xinf->bang);
return success ? RESULT_COMPLETE : RESULT_ERROR;
}
RESULT ex_z(xinf)
EXINFO *xinf;
{
CHAR type = '+'; /* type of window to show */
long count = o_window; /* number of lines to show */
PFLAG pflag; /* how to show */
long line = xinf->from; /* first line to show */
long offset;
CHAR *scan;
/* choose a default printing style, from options */
if (o_number(xinf->window))
if (o_list(xinf->window))
pflag = PF_NUMLIST;
else
pflag = PF_NUMBER;
else
if (o_list(xinf->window))
pflag = PF_LIST;
else
pflag = PF_NUMBER;
/* If we were given arguments, then parse them */
for (scan = xinf->rhs; scan && *scan; scan++)
{
switch (*scan)
{
case '-':
case '+':
case '.':
case '^':
case '=':
type = *scan;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
for (count = 0; isdigit(*scan); scan++)
{
count = count * 10 + *scan - '0';
}
scan--; /* we went one character too far */
break;
case 'l':
if (pflag == PF_NUMBER || pflag == PF_NUMLIST)
pflag = PF_NUMLIST;
else
pflag = PF_LIST;
break;
case '#':
if (pflag == PF_LIST || pflag == PF_NUMLIST)
pflag = PF_NUMLIST;
else
pflag = PF_NUMBER;
break;
case ' ':
case '\t':
case 'p':
/* ignore */
break;
default:
msg(MSG_ERROR, "bad argument to :z");
return RESULT_ERROR;
}
}
/* choose the first line, based on the type */
switch (type)
{
case '-': /* show the given line at the bottom */
line = xinf->from - count + 1;
break;
case '+': /* show the given line at the top */
line = xinf->from;
break;
case '.': /* show the given line in the middle */
line = xinf->from - count/2;
break;
case '^': /* show the window before the current line */
line = xinf->from - count * 2 + 1;
break;
case '=': /* show it in the middle, surrounded by lines of hyphens */
count -= 2;
line = xinf->from - count / 2;
break;
}
/* protect against readed past top or bottom of buffer */
if (line < 1)
{
count -= 1 - line;
line = 1;
}
if (line + count > o_buflines(markbuffer(xinf->fromaddr)))
{
count -= line - o_buflines(markbuffer(xinf->fromaddr));
}
/* construct a mark for the first line */
xinf->newcurs = markdup(xinf->fromaddr);
marksetline(xinf->newcurs, line);
/* print the lines */
if (type == '=')
{
/* for '=', we need to add lines of hyphens around given line */
if (line < xinf->from)
{
exprintlines(xinf->window, xinf->newcurs, xinf->from - line, pflag);
}
drawextext(xinf->window, toCHAR("-------------------------------------------------------------------------------\n"), 80);
exprintlines(xinf->window, xinf->fromaddr, 1, pflag);
drawextext(xinf->window, toCHAR("-------------------------------------------------------------------------------\n"), 80);
count -= (xinf->from - line) + 1;
if (count > 0)
{
marksetline(xinf->newcurs, xinf->from + 1);
exprintlines(xinf->window, xinf->newcurs, count, pflag);
}
/* leave the cursor on the given line */
marksetoffset(xinf->newcurs, markoffset(xinf->fromaddr));
}
else
{
/* print the lines all at once */
offset = exprintlines(xinf->window, xinf->newcurs, count, pflag);
/* leave the cursor at the start of the last line */
marksetoffset(xinf->newcurs, offset);
}
return RESULT_COMPLETE;
}
Jump to Line
Something went wrong with that request. Please try again.