This is cut.c in view mode; [Download] [Up]
/* cut.c */
/* Copyright 1995 by Steve Kirkendall */
char id_cut[] = "$Id: cut.c,v 2.24 1996/06/28 01:33:43 steve Exp $";
#include "elvis.h"
#if USE_PROTOTYPES
static void shiftbufs(void);
#endif
/* This is the name of the most recently named buffer. It is used to
* implement the "" and "@ buffer names, and for incrementing "1 (etc.)
* when pasting from numbered cut buffers.
*/
static CHAR previous;
/* This function locates or creates the BUFFER used for storing the contents
* of a given cut buffer. "cbname" is the single-character name of the cut
* buffer.
*
* The cutyank() and cutput() functions both use this function to locate the
* buffer, and then perform other name-dependent operations to determine how
* the buffer should be used. For example, 'a' and 'A' both refer to the same
* buffer here, but cutyank() will treat them differently.
*/
BUFFER cutbuffer(cbname, create)
_CHAR_ cbname; /* name of cut buffer, or '\0' for anonymous */
BOOLEAN create; /* create the edit buffer if it doesn't already exist? */
{
char tmpname[50];
char *bufname;
BUFFER buf;
/* handle the "" buffer */
if (cbname == '"' || cbname == '@')
{
if (!previous)
{
msg(MSG_ERROR, "no previous cut buffer");
return NULL;
}
cbname = previous;
}
switch (cbname)
{
case '\0':
bufname = CUTANON_BUF;
break;
case '<':
case '>':
bufname = CUTEXTERN_BUF;
break;
case '.':
bufname = CUTINPUT_BUF;
break;
default:
if ((cbname >= '1' && cbname <= '9') || islower(cbname))
{
sprintf(tmpname, CUTNAMED_BUF, cbname);
bufname = tmpname;
}
else if (isupper(cbname))
{
sprintf(tmpname, CUTNAMED_BUF, tolower((char)cbname));
bufname = tmpname;
}
else
{
msg(MSG_ERROR, "[C]bad cutbuf $1", cbname);
return NULL;
}
}
/* find the buffer, or create it */
previous = cbname;
buf = (create ? bufalloc(toCHAR(bufname), 0) : buffind(toCHAR(bufname)));
if (buf)
o_internal(buf) = True;
return buf;
}
/* This function shifts the numbered cut buffers by renaming them. */
static void shiftbufs()
{
CHAR cbname; /* buffer currently being considered. */
BUFFER buf; /* the edit buffer used to store a cut buffer's contents */
char tmpname[50];
/* We would like to delete "9 after this, but if it has any marks
* referring to it then we must leave it, and delete "8 instead.
* But "8 may have marks, forcing us to leave it too... search back
* until we find a buffer we can delete.
*/
for (cbname = '9'; cbname > '1'; cbname--)
{
/* Try to find the buffer. If it doesn't exist then we
* won't really need to delete ANY numbered cut buffer!
*/
buf = cutbuffer(cbname, False);
if (!buf)
break;
/* If any marks refer to this buffer, then we can't
* delete this buffer.
*/
if (buf->marks)
continue;
/* Okay, this is the one! Delete it and break out of loop */
buffree(buf);
break;
}
/* shift the lower-numbered buffers by renaming them */
while (cbname > '1')
{
/* generate the name new name that the buffer should have */
sprintf(tmpname, CUTNAMED_BUF, cbname);
/* find the preceding-numbered buffer */
cbname--;
buf = cutbuffer(cbname, False);
/* if the buffer exists, rename it one number higher */
if (buf)
{
buftitle(buf, toCHAR(tmpname));
}
}
/* At this point, the buffers have been shifted and there probably
* is no "1 buffer. The only way there could be a "1 buffer would be
* if every cut buffer from "1 to "9 was referred to by a mark and
* therefore undeleteable. Even this case should be safe, though,
* since the cutyank() function will just replace the old contents
* of "1 with the new contents, causing the marks to be adjusted...
* to safe (though probably useless) offsets.
*/
}
/* This function copies text between two marks into a cut buffer. "cbname"
* is the single-character name of the cut buffer. "from" and "to" delimit
* the source of the text. "type" is 'c' for character cuts, 'l' for line
* cuts, and 'r' for rectangular cuts; for rectangular cuts only, the left
* and right limits are taken from the current window.
*
* "type" can also be 'L' for line-mode cuts which come from visual command
* mode operators. This is different from 'l' in that 'L' boundaries have
* already been adjusted to match line boundaries, but for 'l' the cutyank()
* function will need to adjust the boundaries itself.
*/
void cutyank(cbname, from, to, type, del)
_CHAR_ cbname; /* name of cut buffer to yank into */
MARK from; /* start of source */
MARK to; /* end of source */
_CHAR_ type; /* yank style: c=character, l=line, r=rectangle */
BOOLEAN del; /* if True, the source text is deleted after it is yanked */
{
BUFFER dest; /* cut buffer we're writing into */
MARKBUF dfrom, dto; /* region of destination buffer */
MARKBUF sfrom, sto; /* region of source buffer */
MARK line; /* end of current line, when type='r' */
long prevline; /* used for detecting failed move of "line" */
long origlines; /* number of lines in cut buffer before yank */
CHAR *cp;
assert(markbuffer(from) == markbuffer(to) && markoffset(from) <= markoffset(to));
assert(type == 'c' || type == 'l' || type == 'r' || type == 'L');
/* if yanking into the anonymous cut buffer, then shift numbered */
if (!cbname)
shiftbufs();
/* If this is a character-mode cut, and both ends happen to be the
* start of lines, then treat this as a line-mode cut. Note that
* we really should know what display mode is being used, but that
* wasn't passed as an argument so we'll have to fudge it a little.
*/
if (type == 'c')
{
if (windefault && markbuffer(from) == markbuffer(windefault->cursor))
{
if (markoffset((*windefault->md->move)(windefault, from, 0L, 0L, True)) == markoffset(from)
&& (markoffset(to) == o_bufchars(markbuffer(to))
|| markoffset((*windefault->md->move)(windefault, to, 0L, 0L, True)) == markoffset(to)))
{
type = 'L';
}
}
else
{
if (markoffset((*dmnormal.move)(windefault, from, 0L, 0L, True)) == markoffset(from)
&& (markoffset(to) == o_bufchars(markbuffer(to))
|| markoffset((*dmnormal.move)(windefault, to, 0L, 0L, True)) == markoffset(to)))
{
type = 'L';
}
}
}
/* find the cut buffer */
dest = cutbuffer(cbname, True);
if (!dest)
{
return;
}
/* discard the old contents, unless we want to append */
if (!isupper(cbname))
{
(void)marktmp(dfrom, dest, 0);
(void)marktmp(dto, dest, o_bufchars(dest));
switch (type)
{
case 'c': bufreplace(&dfrom, &dto, toCHAR("character\n"), CUT_TYPELEN); break;
case 'L':
case 'l': bufreplace(&dfrom, &dto, toCHAR("line \n"), CUT_TYPELEN); break;
case 'r': bufreplace(&dfrom, &dto, toCHAR("rectangle\n"), CUT_TYPELEN); break;
}
origlines = 1;
}
else
{
(void)marktmp(dfrom, dest, o_bufchars(dest));
origlines = o_buflines(dest);
}
/* copy the text into the buffer. */
(void)marktmp(dfrom, dest, o_bufchars(dest));
switch (type)
{
case 'c':
bufpaste(&dfrom, from, to);
if (del)
{
bufreplace(from, to, NULL, 0);
}
break;
case 'l':
sfrom = *(*dmnormal.move)(windefault, from, 0, 0, True);
markaddoffset(to, -1);
sto = *(*dmnormal.move)(windefault, to, 1, INFINITY, True);
markaddoffset(&sto, 1);
bufpaste(&dfrom, &sfrom, &sto);
if (del)
{
bufreplace(&sfrom, &sto, NULL, 0);
}
break;
case 'L':
bufpaste(&dfrom, from, to);
if (del)
{
bufreplace(from, to, NULL, 0);
}
break;
case 'r':
/* NOTE: the only way to yank a rectangle is by visibly
* selecting it. So we know that we're yanking from the
* current window, and can find the left & right limits
* there, and use the window's edit mode to determine how
* the text is formatted.
*/
assert(windefault && from && markbuffer(from) == markbuffer(windefault->cursor));
/* we'll start at the bottom and work backward. All text
* will therefore be inserted into the cut-buffer at what
* is currently its end.
*/
(void)marktmp(dfrom, dest, o_bufchars(dest));
/* The "to" mark is actually the start of the line *AFTER* the
* last line to be included in the cut. This makes display
* updates easier, but we need to decrement the "to" mark
* here or else we'll be cutting one line too many.
*/
line = markdup(to);
marksetoffset(line, markoffset((*windefault->md->move)(windefault, line, -1, INFINITY, True)));
/* for each line of the rectangle... */
do
{
/* Choose the starting point on this line. Make sure
* the left edge of the character is in the rectangle
*/
sfrom = *(*windefault->md->move)(windefault, line, 0, windefault->selleft, False);
if ((*windefault->md->mark2col)(windefault, &sfrom, False) < windefault->selleft)
{
markaddoffset(&sfrom, 1);
}
/* Choose the ending point on this line. Add 1 so that
* the final character is included in the yanking, but
* be careful never to yank a newline.
*/
sto = *(*windefault->md->move)(windefault, line, 0, windefault->selright, False);
if (scanchar(&sto) != '\n')
{
markaddoffset(&sto, 1);
}
/* append this slice of the rectangle */
bufreplace(&dfrom, &dfrom, toCHAR("\n"), 1);
if (markoffset(&sfrom) < markoffset(&sto))
{
bufpaste(&dfrom, &sfrom, &sto);
if (del)
{
bufreplace(&sfrom, &sto, NULL, 0);
}
}
/* locate the next line */
prevline = markoffset(line);
marksetoffset(line, markoffset((*windefault->md->move)(windefault, line, -1, INFINITY, True)));
if (prevline == markoffset(line))
{
marksetoffset(line, markoffset(from));
}
} while (markoffset(line) > markoffset(from));
markfree(line);
break;
}
/* if this the external cut buffer, then write it */
if (cbname == '>' && gui->clipopen && (*gui->clipopen)(True))
{
for (scanalloc(&cp, marktmp(dfrom, dest, CUT_TYPELEN));
cp;
markaddoffset(&dfrom, scanright(&cp)), scanseek(&cp, &dfrom))
{
(*gui->clipwrite)(cp, scanright(&cp));
}
(*gui->clipclose)();
scanfree(&cp);
}
/* Report. Except that we don't need to report how many new input
* lines we've copied to the ELVIS_PREVIOUS_INPUT buffer. Also, when
* the mouse is used to mark text under X11, it is immediately copied
* to the clipboard and we don't want to report that.
*/
if (o_buflines(dest) - origlines >= o_report
&& cbname != '.'
&& (cbname != '>' || !windefault || !windefault->seltop))
{
if (del)
msg(MSG_INFO, "[d]$1 lines deleted", o_buflines(dest) - origlines);
else if (isupper(cbname))
msg(MSG_INFO, "[d]$1 more lines yanked", o_buflines(dest) - origlines);
else
msg(MSG_INFO, "[d]$1 lines yanked", o_buflines(dest) - origlines);
}
}
/* This function pastes text that was yanked by cutyank. Returns NULL on
* errors, or the final cursor position if successful.
*/
MARK cutput(cbname, win, at, after, cretend, lretend)
_CHAR_ cbname; /* cut buffer name */
WINDOW win; /* window showing that buffer */
MARK at; /* where to insert the text */
BOOLEAN after; /* if True, insert after "at"; else insert before */
BOOLEAN cretend;/* if character-mode: True=return first, False=return last */
BOOLEAN lretend;/* if not character-mode: True=return first, False=return last */
{
BUFFER src;
CHAR iobuf[1000];
CHAR type;
CHAR *cp;
MARKBUF sfrom, sto;
static MARKBUF ret;
int i;
long line, col;
BOOLEAN cmd;
/* If anonymous buffer, and most recent paste was from a numbered
* cut buffer, then use the successive numbered buffer by default.
*/
if (!cbname)
{
if (previous >= '1' && previous < '9')
cbname = previous + 1;
else if (previous == '9')
cbname = '9';
}
/* find the cut buffer */
src = cutbuffer(cbname, True);
if (!src)
{
return NULL;
}
/* if external cut buffer, then fill it from GUI */
if (cbname == '<' && gui->clipopen && (*gui->clipopen)(False))
{
bufreplace(marktmp(sfrom, src, 0), marktmp(sto, src, o_bufchars(src)), toCHAR("character\n"), CUT_TYPELEN);
while ((i = (*gui->clipread)(iobuf, sizeof(iobuf))) > 0)
{
bufreplace(marktmp(sfrom, src, CUT_TYPELEN), &sfrom, iobuf, i);
}
(*gui->clipclose)();
}
/* if the buffer is empty, fail */
if (o_bufchars(src) <= CUT_TYPELEN)
{
/* well, the '.' buffer is okay, but all others fail */
if (cbname == '.')
{
ret = *at;
return &ret;
}
msg(MSG_ERROR, "[C]cut buffer $1 empty", cbname);
return NULL;
}
/* figure out what type of yank this was */
type = scanchar(marktmp(sfrom, src, 0));
/* do the paste */
switch (type)
{
case 'c': /* CHARACTER MODE */
/* choose the insertion point */
ret = *at;
if (after && scanchar(at) != '\n')
{
markaddoffset(&ret, 1);
}
/* paste it & set "ret" to the new cursor cursor */
bufpaste(&ret, marktmp(sfrom, src, CUT_TYPELEN), marktmp(sto, src, o_bufchars(src)));
if (cretend)
{
markaddoffset(&ret, o_bufchars(src) - CUT_TYPELEN - 1);
}
break;
case 'l': /* LINE MODE */
/* choose the insertion point */
if (after)
{
ret = *(win->md->move)(win, at, 0, INFINITY, False);
markaddoffset(&ret, 1);
}
else
{
ret = *(win->md->move)(win, at, 0, 0, False);
}
/* paste it & set "ret" to the start of the new cursor line */
bufpaste(&ret, marktmp(sfrom, src, CUT_TYPELEN), marktmp(sto, src, o_bufchars(src)));
if (lretend)
{
markaddoffset(&ret, o_bufchars(src) - CUT_TYPELEN);
ret = *(win->md->move)(win, &ret, -1, 0, True);
}
/* move new cursor past any whitespace at start of line */
for (scanalloc(&cp, &ret);
cp && (*cp == '\t' || *cp == ' ');
scannext(&cp))
{
}
if (cp)
ret = *scanmark(&cp);
scanfree(&cp);
break;
case 'r': /* RECTANGLE MODE */
/* choose a starting point, and a column to try for */
if (after)
{
cmd = True;
col = (*win->md->mark2col)(win, at, cmd) + 1;
}
else
{
cmd = False;
col = (*win->md->mark2col)(win, at, cmd);
}
ret = *(*win->md->move)(win, at, 0, col, cmd);
(void)marktmp(sto, src, lowline(bufbufinfo(src), 2) - 1);
/* for each data line in the cut buffer... */
for (line = 2;
line <= o_buflines(src) && markoffset(&ret) < o_bufchars(markbuffer(&ret));
line++)
{
/* delimit the contents of the next line in this cutbuf */
sfrom = sto;
markaddoffset(&sfrom, 1);
(void)marktmp(sto, src, lowline(bufbufinfo(src), line + 1) - 1);
/* paste it */
bufpaste(&ret, &sfrom, &sto);
/* move to the next line in destination buffer */
ret = *(*win->md->move)(win, &ret, 1, col, cmd);
}
if (!lretend)
{
ret = *at;
}
break;
default:
msg(MSG_ERROR, "[C]cut buffer $1 scrambled", cbname);
return NULL;
}
/* report */
if (o_buflines(src) - 1 >= o_report && cbname != '.')
{
msg(MSG_INFO, "[d]$1 lines pasted", o_buflines(src) - 1);
}
return &ret;
}
/* This function copies the contents of a cut buffer into RAM. The memory
* image contains no hint as to whether it was a line mode cut, or character
* cut, or rectangle. The calling function is responsible for calling
* safefree() when the memory image is no longer needed. Returns NULL if
* the buffer is empty, doesn't exist, or appears to be corrupt. The
* "< cut buffer is illegal in this contents, and will also return NULL.
*/
CHAR *cutmemory(cbname)
_CHAR_ cbname; /* cut buffer name */
{
BUFFER src;
MARKBUF from, to;
/* Find the cut buffer. If it looks wrong, then return NULL. */
src = cutbuffer(cbname, False);
if (cbname == '<' || !src || o_bufchars(src) <= CUT_TYPELEN)
{
return NULL;
}
/* copy the contents into the memory */
return bufmemory(marktmp(from, src, CUT_TYPELEN), marktmp(to, src, o_bufchars(src)));
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.