1 /* $OpenBSD: buffer.c,v 1.94 2014/06/12 16:29:41 bcallah Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Buffer handling. 7 */ 8 9 #include "def.h" 10 #include "kbd.h" /* needed for modes */ 11 12 #include <libgen.h> 13 #include <stdarg.h> 14 15 #ifndef DIFFTOOL 16 #define DIFFTOOL "/usr/bin/diff" 17 #endif /* !DIFFTOOL */ 18 19 static struct buffer *makelist(void); 20 static struct buffer *bnew(const char *); 21 22 static int usebufname(const char *); 23 24 /* Flag for global working dir */ 25 extern int globalwd; 26 27 /* ARGSUSED */ 28 int 29 togglereadonly(int f, int n) 30 { 31 int s; 32 33 if ((s = checkdirty(curbp)) != TRUE) 34 return (s); 35 if (!(curbp->b_flag & BFREADONLY)) 36 curbp->b_flag |= BFREADONLY; 37 else { 38 curbp->b_flag &= ~BFREADONLY; 39 if (curbp->b_flag & BFCHG) 40 ewprintf("Warning: Buffer was modified"); 41 } 42 curwp->w_rflag |= WFMODE; 43 44 return (TRUE); 45 } 46 47 /* Switch to the named buffer. 48 * If no name supplied, switch to the default (alternate) buffer. 49 */ 50 int 51 usebufname(const char *bufp) 52 { 53 struct buffer *bp; 54 55 if (bufp == NULL) 56 return (ABORT); 57 if (bufp[0] == '\0' && curbp->b_altb != NULL) 58 bp = curbp->b_altb; 59 else if ((bp = bfind(bufp, TRUE)) == NULL) 60 return (FALSE); 61 62 /* and put it in current window */ 63 curbp = bp; 64 return (showbuffer(bp, curwp, WFFRAME | WFFULL)); 65 } 66 67 /* 68 * Attach a buffer to a window. The values of dot and mark come 69 * from the buffer if the use count is 0. Otherwise, they come 70 * from some other window. *scratch* is the default alternate 71 * buffer. 72 */ 73 /* ARGSUSED */ 74 int 75 usebuffer(int f, int n) 76 { 77 char bufn[NBUFN], *bufp; 78 79 /* Get buffer to use from user */ 80 if ((curbp->b_altb == NULL) && 81 ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL)) 82 bufp = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF); 83 else 84 bufp = eread("Switch to buffer: (default %s) ", bufn, NBUFN, 85 EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); 86 87 return (usebufname(bufp)); 88 } 89 90 /* 91 * pop to buffer asked for by the user. 92 */ 93 /* ARGSUSED */ 94 int 95 poptobuffer(int f, int n) 96 { 97 struct buffer *bp; 98 struct mgwin *wp; 99 char bufn[NBUFN], *bufp; 100 101 /* Get buffer to use from user */ 102 if ((curbp->b_altb == NULL) && 103 ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL)) 104 bufp = eread("Switch to buffer in other window: ", bufn, NBUFN, 105 EFNEW | EFBUF); 106 else 107 bufp = eread("Switch to buffer in other window: (default %s) ", 108 bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); 109 if (bufp == NULL) 110 return (ABORT); 111 if (bufp[0] == '\0' && curbp->b_altb != NULL) 112 bp = curbp->b_altb; 113 else if ((bp = bfind(bufn, TRUE)) == NULL) 114 return (FALSE); 115 if (bp == curbp) 116 return (splitwind(f, n)); 117 /* and put it in a new, non-ephemeral window */ 118 if ((wp = popbuf(bp, WNONE)) == NULL) 119 return (FALSE); 120 curbp = bp; 121 curwp = wp; 122 return (TRUE); 123 } 124 125 /* 126 * Dispose of a buffer, by name. 127 * Ask for the name (unless called by dired mode). Look it up (don't 128 * get too upset if it isn't there at all!). Clear the buffer (ask 129 * if the buffer has been changed). Then free the header 130 * line and the buffer header. Bound to "C-x k". 131 */ 132 /* ARGSUSED */ 133 int 134 killbuffer_cmd(int f, int n) 135 { 136 struct buffer *bp; 137 char bufn[NBUFN], *bufp; 138 139 if (f & FFRAND) /* dired mode 'q' */ 140 bp = curbp; 141 else if ((bufp = eread("Kill buffer: (default %s) ", bufn, NBUFN, 142 EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL) 143 return (ABORT); 144 else if (bufp[0] == '\0') 145 bp = curbp; 146 else if ((bp = bfind(bufn, FALSE)) == NULL) 147 return (FALSE); 148 return (killbuffer(bp)); 149 } 150 151 int 152 killbuffer(struct buffer *bp) 153 { 154 struct buffer *bp1; 155 struct buffer *bp2; 156 struct mgwin *wp; 157 int s; 158 struct undo_rec *rec; 159 160 /* 161 * Find some other buffer to display. Try the alternate buffer, 162 * then the first different buffer in the buffer list. If there's 163 * only one buffer, create buffer *scratch* and make it the alternate 164 * buffer. Return if *scratch* is only buffer... 165 */ 166 if ((bp1 = bp->b_altb) == NULL) { 167 bp1 = (bp == bheadp) ? bp->b_bufp : bheadp; 168 if (bp1 == NULL) { 169 /* only one buffer. see if it's *scratch* */ 170 if (bp == bfind("*scratch*", FALSE)) 171 return (TRUE); 172 /* create *scratch* for alternate buffer */ 173 if ((bp1 = bfind("*scratch*", TRUE)) == NULL) 174 return (FALSE); 175 } 176 } 177 if ((s = bclear(bp)) != TRUE) 178 return (s); 179 for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) { 180 if (wp->w_bufp == bp) { 181 bp2 = bp1->b_altb; /* save alternate buffer */ 182 if (showbuffer(bp1, wp, WFMODE | WFFRAME | WFFULL)) 183 bp1->b_altb = bp2; 184 else 185 bp1 = bp2; 186 } 187 } 188 if (bp == curbp) 189 curbp = bp1; 190 free(bp->b_headp); /* Release header line. */ 191 bp2 = NULL; /* Find the header. */ 192 bp1 = bheadp; 193 while (bp1 != bp) { 194 if (bp1->b_altb == bp) 195 bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb; 196 bp2 = bp1; 197 bp1 = bp1->b_bufp; 198 } 199 bp1 = bp1->b_bufp; /* Next one in chain. */ 200 if (bp2 == NULL) /* Unlink it. */ 201 bheadp = bp1; 202 else 203 bp2->b_bufp = bp1; 204 while (bp1 != NULL) { /* Finish with altb's */ 205 if (bp1->b_altb == bp) 206 bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb; 207 bp1 = bp1->b_bufp; 208 } 209 210 while ((rec = TAILQ_FIRST(&bp->b_undo))) { 211 TAILQ_REMOVE(&bp->b_undo, rec, next); 212 free_undo_record(rec); 213 } 214 215 free(bp->b_bname); /* Release name block */ 216 free(bp); /* Release buffer block */ 217 return (TRUE); 218 } 219 220 /* 221 * Save some buffers - just call anycb with the arg flag. 222 */ 223 /* ARGSUSED */ 224 int 225 savebuffers(int f, int n) 226 { 227 if (anycb(f) == ABORT) 228 return (ABORT); 229 return (TRUE); 230 } 231 232 /* 233 * Listing buffers. 234 */ 235 static int listbuf_ncol; 236 237 static int listbuf_goto_buffer(int f, int n); 238 static int listbuf_goto_buffer_one(int f, int n); 239 static int listbuf_goto_buffer_helper(int f, int n, int only); 240 241 static PF listbuf_pf[] = { 242 listbuf_goto_buffer 243 }; 244 static PF listbuf_one[] = { 245 listbuf_goto_buffer_one 246 }; 247 248 249 static struct KEYMAPE (2 + IMAPEXT) listbufmap = { 250 2, 251 2 + IMAPEXT, 252 rescan, 253 { 254 { 255 CCHR('M'), CCHR('M'), listbuf_pf, NULL 256 }, 257 { 258 '1', '1', listbuf_one, NULL 259 } 260 } 261 }; 262 263 /* 264 * Display the buffer list. This is done 265 * in two parts. The "makelist" routine figures out 266 * the text, and puts it in a buffer. "popbuf" 267 * then pops the data onto the screen. Bound to 268 * "C-X C-B". 269 */ 270 /* ARGSUSED */ 271 int 272 listbuffers(int f, int n) 273 { 274 static int initialized = 0; 275 struct buffer *bp; 276 struct mgwin *wp; 277 278 if (!initialized) { 279 maps_add((KEYMAP *)&listbufmap, "listbufmap"); 280 initialized = 1; 281 } 282 283 if ((bp = makelist()) == NULL || (wp = popbuf(bp, WNONE)) == NULL) 284 return (FALSE); 285 wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */ 286 wp->w_doto = bp->b_doto; 287 bp->b_modes[0] = name_mode("fundamental"); 288 bp->b_modes[1] = name_mode("listbufmap"); 289 bp->b_nmodes = 1; 290 291 return (TRUE); 292 } 293 294 /* 295 * This routine rebuilds the text for the 296 * list buffers command. Return pointer 297 * to new list if everything works. 298 * Return NULL if there is an error (if 299 * there is no memory). 300 */ 301 static struct buffer * 302 makelist(void) 303 { 304 int w = ncol / 2; 305 struct buffer *bp, *blp; 306 struct line *lp; 307 308 if ((blp = bfind("*Buffer List*", TRUE)) == NULL) 309 return (NULL); 310 if (bclear(blp) != TRUE) 311 return (NULL); 312 blp->b_flag &= ~BFCHG; /* Blow away old. */ 313 blp->b_flag |= BFREADONLY; 314 315 listbuf_ncol = ncol; /* cache ncol for listbuf_goto_buffer */ 316 317 if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size File") == FALSE || 318 addlinef(blp, "%-*s%s", w, " -- ------", "---- ----") == FALSE) 319 return (NULL); 320 321 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 322 RSIZE nbytes; 323 324 nbytes = 0; /* Count bytes in buf. */ 325 if (bp != blp) { 326 lp = bfirstlp(bp); 327 while (lp != bp->b_headp) { 328 nbytes += llength(lp) + 1; 329 lp = lforw(lp); 330 } 331 if (nbytes) 332 nbytes--; /* no bonus newline */ 333 } 334 335 if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s", 336 (bp == curbp) ? '.' : ' ', /* current buffer ? */ 337 ((bp->b_flag & BFCHG) != 0) ? '*' : ' ', /* changed ? */ 338 ((bp->b_flag & BFREADONLY) != 0) ? ' ' : '*', 339 w - 5, /* four chars already written */ 340 w - 5, /* four chars already written */ 341 bp->b_bname, /* buffer name */ 342 strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */ 343 nbytes, /* buffer size */ 344 w - 7, /* seven chars already written */ 345 bp->b_fname) == FALSE) 346 return (NULL); 347 } 348 blp->b_dotp = bfirstlp(blp); /* put dot at beginning of 349 * buffer */ 350 blp->b_doto = 0; 351 return (blp); /* All done */ 352 } 353 354 static int 355 listbuf_goto_buffer(int f, int n) 356 { 357 return (listbuf_goto_buffer_helper(f, n, 0)); 358 } 359 360 static int 361 listbuf_goto_buffer_one(int f, int n) 362 { 363 return (listbuf_goto_buffer_helper(f, n, 1)); 364 } 365 366 static int 367 listbuf_goto_buffer_helper(int f, int n, int only) 368 { 369 struct buffer *bp; 370 struct mgwin *wp; 371 char *line = NULL; 372 int i, ret = FALSE; 373 374 if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$') { 375 dobeep(); 376 ewprintf("buffer name truncated"); 377 return (FALSE); 378 } 379 380 if ((line = malloc(listbuf_ncol/2)) == NULL) 381 return (FALSE); 382 383 memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5); 384 for (i = listbuf_ncol/2 - 6; i > 0; i--) { 385 if (line[i] != ' ') { 386 line[i + 1] = '\0'; 387 break; 388 } 389 } 390 if (i == 0) 391 goto cleanup; 392 393 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 394 if (strcmp(bp->b_bname, line) == 0) 395 break; 396 } 397 if (bp == NULL) 398 goto cleanup; 399 400 if ((wp = popbuf(bp, WNONE)) == NULL) 401 goto cleanup; 402 curbp = bp; 403 curwp = wp; 404 405 if (only) 406 ret = (onlywind(f, n)); 407 else 408 ret = TRUE; 409 410 cleanup: 411 free(line); 412 413 return (ret); 414 } 415 416 /* 417 * The argument "fmt" points to a format string. Append this line to the 418 * buffer. Handcraft the EOL on the end. Return TRUE if it worked and 419 * FALSE if you ran out of room. 420 */ 421 int 422 addlinef(struct buffer *bp, char *fmt, ...) 423 { 424 va_list ap; 425 struct line *lp; 426 427 if ((lp = lalloc(0)) == NULL) 428 return (FALSE); 429 va_start(ap, fmt); 430 if (vasprintf(&lp->l_text, fmt, ap) == -1) { 431 lfree(lp); 432 va_end(ap); 433 return (FALSE); 434 } 435 lp->l_used = strlen(lp->l_text); 436 va_end(ap); 437 438 bp->b_headp->l_bp->l_fp = lp; /* Hook onto the end */ 439 lp->l_bp = bp->b_headp->l_bp; 440 bp->b_headp->l_bp = lp; 441 lp->l_fp = bp->b_headp; 442 bp->b_lines++; 443 444 return (TRUE); 445 } 446 447 /* 448 * Look through the list of buffers, giving the user a chance to save them. 449 * Return TRUE if there are any changed buffers afterwards. Buffers that don't 450 * have an associated file don't count. Return FALSE if there are no changed 451 * buffers. Return ABORT if an error occurs or if the user presses c-g. 452 */ 453 int 454 anycb(int f) 455 { 456 struct buffer *bp; 457 int s = FALSE, save = FALSE, save2 = FALSE, ret; 458 char pbuf[NFILEN + 11]; 459 460 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 461 if (*(bp->b_fname) != '\0' && (bp->b_flag & BFCHG) != 0) { 462 ret = snprintf(pbuf, sizeof(pbuf), "Save file %s", 463 bp->b_fname); 464 if (ret < 0 || ret >= sizeof(pbuf)) { 465 dobeep(); 466 ewprintf("Error: filename too long!"); 467 return (UERROR); 468 } 469 if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) && 470 (save2 = buffsave(bp)) == TRUE) { 471 bp->b_flag &= ~BFCHG; 472 upmodes(bp); 473 } else { 474 if (save2 == FIOERR) 475 return (save2); 476 s = TRUE; 477 } 478 if (save == ABORT) 479 return (save); 480 save = TRUE; 481 } 482 } 483 if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */ 484 ewprintf("(No files need saving)"); 485 return (s); 486 } 487 488 /* 489 * Search for a buffer, by name. 490 * If not found, and the "cflag" is TRUE, 491 * create a new buffer. Return pointer to the found 492 * (or new) buffer. 493 */ 494 struct buffer * 495 bfind(const char *bname, int cflag) 496 { 497 struct buffer *bp; 498 499 bp = bheadp; 500 while (bp != NULL) { 501 if (strcmp(bname, bp->b_bname) == 0) 502 return (bp); 503 bp = bp->b_bufp; 504 } 505 if (cflag != TRUE) 506 return (NULL); 507 508 bp = bnew(bname); 509 510 return (bp); 511 } 512 513 /* 514 * Create a new buffer and put it in the list of 515 * all buffers. 516 */ 517 static struct buffer * 518 bnew(const char *bname) 519 { 520 struct buffer *bp; 521 struct line *lp; 522 int i; 523 size_t len; 524 525 bp = calloc(1, sizeof(struct buffer)); 526 if (bp == NULL) { 527 dobeep(); 528 ewprintf("Can't get %d bytes", sizeof(struct buffer)); 529 return (NULL); 530 } 531 if ((lp = lalloc(0)) == NULL) { 532 free(bp); 533 return (NULL); 534 } 535 bp->b_altb = bp->b_bufp = NULL; 536 bp->b_dotp = lp; 537 bp->b_doto = 0; 538 bp->b_markp = NULL; 539 bp->b_marko = 0; 540 bp->b_flag = defb_flag; 541 /* if buffer name starts and ends with '*', we ignore changes */ 542 len = strlen(bname); 543 if (len) { 544 if (bname[0] == '*' && bname[len - 1] == '*') 545 bp->b_flag |= BFIGNDIRTY; 546 } 547 bp->b_nwnd = 0; 548 bp->b_headp = lp; 549 bp->b_nmodes = defb_nmodes; 550 TAILQ_INIT(&bp->b_undo); 551 bp->b_undoptr = NULL; 552 i = 0; 553 do { 554 bp->b_modes[i] = defb_modes[i]; 555 } while (i++ < defb_nmodes); 556 bp->b_fname[0] = '\0'; 557 bp->b_cwd[0] = '\0'; 558 bzero(&bp->b_fi, sizeof(bp->b_fi)); 559 lp->l_fp = lp; 560 lp->l_bp = lp; 561 bp->b_bufp = bheadp; 562 bheadp = bp; 563 bp->b_dotline = bp->b_markline = 1; 564 bp->b_lines = 1; 565 if ((bp->b_bname = strdup(bname)) == NULL) { 566 dobeep(); 567 ewprintf("Can't get %d bytes", strlen(bname) + 1); 568 return (NULL); 569 } 570 571 return (bp); 572 } 573 574 /* 575 * This routine blows away all of the text 576 * in a buffer. If the buffer is marked as changed 577 * then we ask if it is ok to blow it away; this is 578 * to save the user the grief of losing text. The 579 * window chain is nearly always wrong if this gets 580 * called; the caller must arrange for the updates 581 * that are required. Return TRUE if everything 582 * looks good. 583 */ 584 int 585 bclear(struct buffer *bp) 586 { 587 struct line *lp; 588 int s; 589 590 /* Has buffer changed, and do we care? */ 591 if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 && 592 (s = eyesno("Buffer modified; kill anyway")) != TRUE) 593 return (s); 594 bp->b_flag &= ~BFCHG; /* Not changed */ 595 while ((lp = lforw(bp->b_headp)) != bp->b_headp) 596 lfree(lp); 597 bp->b_dotp = bp->b_headp; /* Fix dot */ 598 bp->b_doto = 0; 599 bp->b_markp = NULL; /* Invalidate "mark" */ 600 bp->b_marko = 0; 601 bp->b_dotline = bp->b_markline = 1; 602 bp->b_lines = 1; 603 604 return (TRUE); 605 } 606 607 /* 608 * Display the given buffer in the given window. Flags indicated 609 * action on redisplay. Update modified flag so insert loop can check it. 610 */ 611 int 612 showbuffer(struct buffer *bp, struct mgwin *wp, int flags) 613 { 614 struct buffer *obp; 615 struct mgwin *owp; 616 617 /* Ensure file has not been modified elsewhere */ 618 if (fchecktime(bp) != TRUE) 619 bp->b_flag |= BFDIRTY; 620 621 if (wp->w_bufp == bp) { /* Easy case! */ 622 wp->w_rflag |= flags; 623 return (TRUE); 624 } 625 /* First, detach the old buffer from the window */ 626 if ((bp->b_altb = obp = wp->w_bufp) != NULL) { 627 if (--obp->b_nwnd == 0) { 628 obp->b_dotp = wp->w_dotp; 629 obp->b_doto = wp->w_doto; 630 obp->b_markp = wp->w_markp; 631 obp->b_marko = wp->w_marko; 632 obp->b_dotline = wp->w_dotline; 633 obp->b_markline = wp->w_markline; 634 } 635 } 636 /* Now, attach the new buffer to the window */ 637 wp->w_bufp = bp; 638 639 if (bp->b_nwnd++ == 0) { /* First use. */ 640 wp->w_dotp = bp->b_dotp; 641 wp->w_doto = bp->b_doto; 642 wp->w_markp = bp->b_markp; 643 wp->w_marko = bp->b_marko; 644 wp->w_dotline = bp->b_dotline; 645 wp->w_markline = bp->b_markline; 646 } else 647 /* already on screen, steal values from other window */ 648 for (owp = wheadp; owp != NULL; owp = wp->w_wndp) 649 if (wp->w_bufp == bp && owp != wp) { 650 wp->w_dotp = owp->w_dotp; 651 wp->w_doto = owp->w_doto; 652 wp->w_markp = owp->w_markp; 653 wp->w_marko = owp->w_marko; 654 wp->w_dotline = owp->w_dotline; 655 wp->w_markline = owp->w_markline; 656 break; 657 } 658 wp->w_rflag |= WFMODE | flags; 659 return (TRUE); 660 } 661 662 /* 663 * Augment a buffer name with a number, if necessary 664 * 665 * If more than one file of the same basename() is open, 666 * the additional buffers are named "file<2>", "file<3>", and 667 * so forth. This function adjusts a buffer name to 668 * include the number, if necessary. 669 */ 670 int 671 augbname(char *bn, const char *fn, size_t bs) 672 { 673 int count; 674 size_t remain, len; 675 676 if ((len = xbasename(bn, fn, bs)) >= bs) 677 return (FALSE); 678 679 remain = bs - len; 680 for (count = 2; bfind(bn, FALSE) != NULL; count++) 681 snprintf(bn + len, remain, "<%d>", count); 682 683 return (TRUE); 684 } 685 686 /* 687 * Pop the buffer we got passed onto the screen. 688 * Returns a status. 689 */ 690 struct mgwin * 691 popbuf(struct buffer *bp, int flags) 692 { 693 struct mgwin *wp; 694 695 if (bp->b_nwnd == 0) { /* Not on screen yet. */ 696 /* 697 * Pick a window for a pop-up. 698 * If only one window, split the screen. 699 * Flag the new window as ephemeral 700 */ 701 if (wheadp->w_wndp == NULL && 702 splitwind(FFOTHARG, flags) == FALSE) 703 return (NULL); 704 705 /* 706 * Pick the uppermost window that isn't 707 * the current window. An LRU algorithm 708 * might be better. Return a pointer, or NULL on error. 709 */ 710 wp = wheadp; 711 712 while (wp != NULL && wp == curwp) 713 wp = wp->w_wndp; 714 } else 715 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 716 if (wp->w_bufp == bp) { 717 wp->w_rflag |= WFFULL | WFFRAME; 718 return (wp); 719 } 720 if (showbuffer(bp, wp, WFFULL) != TRUE) 721 return (NULL); 722 return (wp); 723 } 724 725 /* 726 * Insert another buffer at dot. Very useful. 727 */ 728 /* ARGSUSED */ 729 int 730 bufferinsert(int f, int n) 731 { 732 struct buffer *bp; 733 struct line *clp; 734 int clo, nline; 735 char bufn[NBUFN], *bufp; 736 737 /* Get buffer to use from user */ 738 if (curbp->b_altb != NULL) 739 bufp = eread("Insert buffer: (default %s) ", bufn, NBUFN, 740 EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); 741 else 742 bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF); 743 if (bufp == NULL) 744 return (ABORT); 745 if (bufp[0] == '\0' && curbp->b_altb != NULL) 746 bp = curbp->b_altb; 747 else if ((bp = bfind(bufn, FALSE)) == NULL) 748 return (FALSE); 749 750 if (bp == curbp) { 751 dobeep(); 752 ewprintf("Cannot insert buffer into self"); 753 return (FALSE); 754 } 755 /* insert the buffer */ 756 nline = 0; 757 clp = bfirstlp(bp); 758 for (;;) { 759 for (clo = 0; clo < llength(clp); clo++) 760 if (linsert(1, lgetc(clp, clo)) == FALSE) 761 return (FALSE); 762 if ((clp = lforw(clp)) == bp->b_headp) 763 break; 764 if (newline(FFRAND, 1) == FALSE) /* fake newline */ 765 return (FALSE); 766 nline++; 767 } 768 if (nline == 1) 769 ewprintf("[Inserted 1 line]"); 770 else 771 ewprintf("[Inserted %d lines]", nline); 772 773 clp = curwp->w_linep; /* cosmetic adjustment */ 774 if (curwp->w_dotp == clp) { /* for offscreen insert */ 775 while (nline-- && lback(clp) != curbp->b_headp) 776 clp = lback(clp); 777 curwp->w_linep = clp; /* adjust framing. */ 778 curwp->w_rflag |= WFFULL; 779 } 780 return (TRUE); 781 } 782 783 /* 784 * Turn off the dirty bit on this buffer. 785 */ 786 /* ARGSUSED */ 787 int 788 notmodified(int f, int n) 789 { 790 struct mgwin *wp; 791 792 curbp->b_flag &= ~BFCHG; 793 wp = wheadp; /* Update mode lines. */ 794 while (wp != NULL) { 795 if (wp->w_bufp == curbp) 796 wp->w_rflag |= WFMODE; 797 wp = wp->w_wndp; 798 } 799 ewprintf("Modification-flag cleared"); 800 return (TRUE); 801 } 802 803 /* 804 * Popbuf and set all windows to top of buffer. 805 */ 806 int 807 popbuftop(struct buffer *bp, int flags) 808 { 809 struct mgwin *wp; 810 811 bp->b_dotp = bfirstlp(bp); 812 bp->b_doto = 0; 813 if (bp->b_nwnd != 0) { 814 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 815 if (wp->w_bufp == bp) { 816 wp->w_dotp = bp->b_dotp; 817 wp->w_doto = 0; 818 wp->w_rflag |= WFFULL; 819 } 820 } 821 return (popbuf(bp, flags) != NULL); 822 } 823 824 /* 825 * Return the working directory for the current buffer, terminated 826 * with a '/'. First, try to extract it from the current buffer's 827 * filename. If that fails, use global cwd. 828 */ 829 int 830 getbufcwd(char *path, size_t plen) 831 { 832 char cwd[NFILEN]; 833 834 if (plen == 0) 835 return (FALSE); 836 837 if (globalwd == FALSE && curbp->b_cwd[0] != '\0') { 838 (void)strlcpy(path, curbp->b_cwd, plen); 839 } else { 840 if (getcwdir(cwd, sizeof(cwd)) == FALSE) 841 goto error; 842 (void)strlcpy(path, cwd, plen); 843 } 844 return (TRUE); 845 error: 846 path[0] = '\0'; 847 return (FALSE); 848 } 849 850 /* 851 * Ensures a buffer has not been modified elsewhere; e.g. on disk. 852 * Prompt the user if it has. 853 * Returns TRUE if it has NOT (i.e. buffer is ok to edit). 854 * FALSE or ABORT otherwise 855 */ 856 int 857 checkdirty(struct buffer *bp) 858 { 859 int s; 860 861 if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0) 862 if (fchecktime(bp) != TRUE) 863 bp->b_flag |= BFDIRTY; 864 865 if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) { 866 s = eynorr("File changed on disk; really edit the buffer"); 867 switch (s) { 868 case TRUE: 869 bp->b_flag &= ~BFDIRTY; 870 bp->b_flag |= BFIGNDIRTY; 871 return (TRUE); 872 case REVERT: 873 dorevert(); 874 return (FALSE); 875 default: 876 return (s); 877 } 878 } 879 880 return (TRUE); 881 } 882 883 /* 884 * Revert the current buffer to whatever is on disk. 885 */ 886 /* ARGSUSED */ 887 int 888 revertbuffer(int f, int n) 889 { 890 char fbuf[NFILEN + 32]; 891 892 if (curbp->b_fname[0] == 0) { 893 dobeep(); 894 ewprintf("Cannot revert buffer not associated with any files."); 895 return (FALSE); 896 } 897 898 snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s", 899 curbp->b_fname); 900 901 if (eyorn(fbuf) == TRUE) 902 return dorevert(); 903 904 return (FALSE); 905 } 906 907 int 908 dorevert(void) 909 { 910 int lineno; 911 struct undo_rec *rec; 912 913 if (access(curbp->b_fname, F_OK|R_OK) != 0) { 914 dobeep(); 915 if (errno == ENOENT) 916 ewprintf("File %s no longer exists!", 917 curbp->b_fname); 918 else 919 ewprintf("File %s is no longer readable!", 920 curbp->b_fname); 921 return (FALSE); 922 } 923 924 /* Save our current line, so we can go back after reloading. */ 925 lineno = curwp->w_dotline; 926 927 /* Prevent readin from asking if we want to kill the buffer. */ 928 curbp->b_flag &= ~BFCHG; 929 930 /* Clean up undo memory */ 931 while ((rec = TAILQ_FIRST(&curbp->b_undo))) { 932 TAILQ_REMOVE(&curbp->b_undo, rec, next); 933 free_undo_record(rec); 934 } 935 936 if (readin(curbp->b_fname)) 937 return(setlineno(lineno)); 938 return (FALSE); 939 } 940 941 /* 942 * Diff the current buffer to what is on disk. 943 */ 944 /*ARGSUSED */ 945 int 946 diffbuffer(int f, int n) 947 { 948 struct buffer *bp; 949 struct line *lp, *lpend; 950 size_t len; 951 int ret; 952 char *text, *ttext; 953 char * const argv[] = 954 {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL}; 955 956 len = 0; 957 958 /* C-u is not supported */ 959 if (n > 1) 960 return (ABORT); 961 962 if (access(DIFFTOOL, X_OK) != 0) { 963 dobeep(); 964 ewprintf("%s not found or not executable.", DIFFTOOL); 965 return (FALSE); 966 } 967 968 if (curbp->b_fname[0] == 0) { 969 dobeep(); 970 ewprintf("Cannot diff buffer not associated with any files."); 971 return (FALSE); 972 } 973 974 lpend = curbp->b_headp; 975 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 976 len+=llength(lp); 977 if (lforw(lp) != lpend) /* no implied \n on last line */ 978 len++; 979 } 980 if ((text = calloc(len + 1, sizeof(char))) == NULL) { 981 dobeep(); 982 ewprintf("Cannot allocate memory."); 983 return (FALSE); 984 } 985 ttext = text; 986 987 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 988 if (llength(lp) != 0) { 989 memcpy(ttext, ltext(lp), llength(lp)); 990 ttext += llength(lp); 991 } 992 if (lforw(lp) != lpend) /* no implied \n on last line */ 993 *ttext++ = '\n'; 994 } 995 996 bp = bfind("*Diff*", TRUE); 997 bp->b_flag |= BFREADONLY; 998 if (bclear(bp) != TRUE) { 999 free(text); 1000 return (FALSE); 1001 } 1002 1003 ret = pipeio(DIFFTOOL, argv, text, len, bp); 1004 1005 if (ret == TRUE) { 1006 eerase(); 1007 if (lforw(bp->b_headp) == bp->b_headp) 1008 addline(bp, "Diff finished (no differences)."); 1009 } 1010 1011 free(text); 1012 return (ret); 1013 } 1014 1015 /* 1016 * Given a file name, either find the buffer it uses, or create a new 1017 * empty buffer to put it in. 1018 */ 1019 struct buffer * 1020 findbuffer(char *fn) 1021 { 1022 struct buffer *bp; 1023 char bname[NBUFN], fname[NBUFN]; 1024 1025 if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) { 1026 dobeep(); 1027 ewprintf("filename too long"); 1028 return (NULL); 1029 } 1030 1031 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 1032 if (strcmp(bp->b_fname, fname) == 0) 1033 return (bp); 1034 } 1035 /* Not found. Create a new one, adjusting name first */ 1036 if (augbname(bname, fname, sizeof(bname)) == FALSE) 1037 return (NULL); 1038 1039 bp = bfind(bname, TRUE); 1040 return (bp); 1041 } 1042