1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
|
/* cut.c */
/* Copyright 1995 by Steve Kirkendall */
char id_cut[] = "$Id: cut.c,v 2.29 1997/11/23 19:51:32 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 '>':
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, True) : buffind(toCHAR(bufname)));
if (buf)
{
o_internal(buf) = True; /* probably already set */
o_locked(buf) = True;
o_bufid(buf) = 0;
}
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 == '>' || 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_report != 0
&& o_buflines(dest) - origlines >= o_report
&& cbname != '.'
&& ((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;
long location = CUT_TYPELEN;
/* 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 == '<' || 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, location), &sfrom, iobuf, i);
location += 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_report != 0 && 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 context, 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 == '<' || 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)));
}
|