1 /* $OpenBSD: buffer.c,v 1.93 2014/03/20 07:47:29 lum 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 != NULL && *(bp->b_fname) != '\0' && 462 (bp->b_flag & BFCHG) != 0) { 463 ret = snprintf(pbuf, sizeof(pbuf), "Save file %s", 464 bp->b_fname); 465 if (ret < 0 || ret >= sizeof(pbuf)) { 466 dobeep(); 467 ewprintf("Error: filename too long!"); 468 return (UERROR); 469 } 470 if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) && 471 (save2 = buffsave(bp)) == TRUE) { 472 bp->b_flag &= ~BFCHG; 473 upmodes(bp); 474 } else { 475 if (save2 == FIOERR) 476 return (save2); 477 s = TRUE; 478 } 479 if (save == ABORT) 480 return (save); 481 save = TRUE; 482 } 483 } 484 if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */ 485 ewprintf("(No files need saving)"); 486 return (s); 487 } 488 489 /* 490 * Search for a buffer, by name. 491 * If not found, and the "cflag" is TRUE, 492 * create a new buffer. Return pointer to the found 493 * (or new) buffer. 494 */ 495 struct buffer * 496 bfind(const char *bname, int cflag) 497 { 498 struct buffer *bp; 499 500 bp = bheadp; 501 while (bp != NULL) { 502 if (strcmp(bname, bp->b_bname) == 0) 503 return (bp); 504 bp = bp->b_bufp; 505 } 506 if (cflag != TRUE) 507 return (NULL); 508 509 bp = bnew(bname); 510 511 return (bp); 512 } 513 514 /* 515 * Create a new buffer and put it in the list of 516 * all buffers. 517 */ 518 static struct buffer * 519 bnew(const char *bname) 520 { 521 struct buffer *bp; 522 struct line *lp; 523 int i; 524 size_t len; 525 526 bp = calloc(1, sizeof(struct buffer)); 527 if (bp == NULL) { 528 dobeep(); 529 ewprintf("Can't get %d bytes", sizeof(struct buffer)); 530 return (NULL); 531 } 532 if ((lp = lalloc(0)) == NULL) { 533 free(bp); 534 return (NULL); 535 } 536 bp->b_altb = bp->b_bufp = NULL; 537 bp->b_dotp = lp; 538 bp->b_doto = 0; 539 bp->b_markp = NULL; 540 bp->b_marko = 0; 541 bp->b_flag = defb_flag; 542 /* if buffer name starts and ends with '*', we ignore changes */ 543 len = strlen(bname); 544 if (len) { 545 if (bname[0] == '*' && bname[len - 1] == '*') 546 bp->b_flag |= BFIGNDIRTY; 547 } 548 bp->b_nwnd = 0; 549 bp->b_headp = lp; 550 bp->b_nmodes = defb_nmodes; 551 TAILQ_INIT(&bp->b_undo); 552 bp->b_undoptr = NULL; 553 i = 0; 554 do { 555 bp->b_modes[i] = defb_modes[i]; 556 } while (i++ < defb_nmodes); 557 bp->b_fname[0] = '\0'; 558 bp->b_cwd[0] = '\0'; 559 bzero(&bp->b_fi, sizeof(bp->b_fi)); 560 lp->l_fp = lp; 561 lp->l_bp = lp; 562 bp->b_bufp = bheadp; 563 bheadp = bp; 564 bp->b_dotline = bp->b_markline = 1; 565 bp->b_lines = 1; 566 if ((bp->b_bname = strdup(bname)) == NULL) { 567 dobeep(); 568 ewprintf("Can't get %d bytes", strlen(bname) + 1); 569 return (NULL); 570 } 571 572 return (bp); 573 } 574 575 /* 576 * This routine blows away all of the text 577 * in a buffer. If the buffer is marked as changed 578 * then we ask if it is ok to blow it away; this is 579 * to save the user the grief of losing text. The 580 * window chain is nearly always wrong if this gets 581 * called; the caller must arrange for the updates 582 * that are required. Return TRUE if everything 583 * looks good. 584 */ 585 int 586 bclear(struct buffer *bp) 587 { 588 struct line *lp; 589 int s; 590 591 /* Has buffer changed, and do we care? */ 592 if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 && 593 (s = eyesno("Buffer modified; kill anyway")) != TRUE) 594 return (s); 595 bp->b_flag &= ~BFCHG; /* Not changed */ 596 while ((lp = lforw(bp->b_headp)) != bp->b_headp) 597 lfree(lp); 598 bp->b_dotp = bp->b_headp; /* Fix dot */ 599 bp->b_doto = 0; 600 bp->b_markp = NULL; /* Invalidate "mark" */ 601 bp->b_marko = 0; 602 bp->b_dotline = bp->b_markline = 1; 603 bp->b_lines = 1; 604 605 return (TRUE); 606 } 607 608 /* 609 * Display the given buffer in the given window. Flags indicated 610 * action on redisplay. Update modified flag so insert loop can check it. 611 */ 612 int 613 showbuffer(struct buffer *bp, struct mgwin *wp, int flags) 614 { 615 struct buffer *obp; 616 struct mgwin *owp; 617 618 /* Ensure file has not been modified elsewhere */ 619 if (fchecktime(bp) != TRUE) 620 bp->b_flag |= BFDIRTY; 621 622 if (wp->w_bufp == bp) { /* Easy case! */ 623 wp->w_rflag |= flags; 624 return (TRUE); 625 } 626 /* First, detach the old buffer from the window */ 627 if ((bp->b_altb = obp = wp->w_bufp) != NULL) { 628 if (--obp->b_nwnd == 0) { 629 obp->b_dotp = wp->w_dotp; 630 obp->b_doto = wp->w_doto; 631 obp->b_markp = wp->w_markp; 632 obp->b_marko = wp->w_marko; 633 obp->b_dotline = wp->w_dotline; 634 obp->b_markline = wp->w_markline; 635 } 636 } 637 /* Now, attach the new buffer to the window */ 638 wp->w_bufp = bp; 639 640 if (bp->b_nwnd++ == 0) { /* First use. */ 641 wp->w_dotp = bp->b_dotp; 642 wp->w_doto = bp->b_doto; 643 wp->w_markp = bp->b_markp; 644 wp->w_marko = bp->b_marko; 645 wp->w_dotline = bp->b_dotline; 646 wp->w_markline = bp->b_markline; 647 } else 648 /* already on screen, steal values from other window */ 649 for (owp = wheadp; owp != NULL; owp = wp->w_wndp) 650 if (wp->w_bufp == bp && owp != wp) { 651 wp->w_dotp = owp->w_dotp; 652 wp->w_doto = owp->w_doto; 653 wp->w_markp = owp->w_markp; 654 wp->w_marko = owp->w_marko; 655 wp->w_dotline = owp->w_dotline; 656 wp->w_markline = owp->w_markline; 657 break; 658 } 659 wp->w_rflag |= WFMODE | flags; 660 return (TRUE); 661 } 662 663 /* 664 * Augment a buffer name with a number, if necessary 665 * 666 * If more than one file of the same basename() is open, 667 * the additional buffers are named "file<2>", "file<3>", and 668 * so forth. This function adjusts a buffer name to 669 * include the number, if necessary. 670 */ 671 int 672 augbname(char *bn, const char *fn, size_t bs) 673 { 674 int count; 675 size_t remain, len; 676 677 if ((len = xbasename(bn, fn, bs)) >= bs) 678 return (FALSE); 679 680 remain = bs - len; 681 for (count = 2; bfind(bn, FALSE) != NULL; count++) 682 snprintf(bn + len, remain, "<%d>", count); 683 684 return (TRUE); 685 } 686 687 /* 688 * Pop the buffer we got passed onto the screen. 689 * Returns a status. 690 */ 691 struct mgwin * 692 popbuf(struct buffer *bp, int flags) 693 { 694 struct mgwin *wp; 695 696 if (bp->b_nwnd == 0) { /* Not on screen yet. */ 697 /* 698 * Pick a window for a pop-up. 699 * If only one window, split the screen. 700 * Flag the new window as ephemeral 701 */ 702 if (wheadp->w_wndp == NULL && 703 splitwind(FFOTHARG, flags) == FALSE) 704 return (NULL); 705 706 /* 707 * Pick the uppermost window that isn't 708 * the current window. An LRU algorithm 709 * might be better. Return a pointer, or NULL on error. 710 */ 711 wp = wheadp; 712 713 while (wp != NULL && wp == curwp) 714 wp = wp->w_wndp; 715 } else 716 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 717 if (wp->w_bufp == bp) { 718 wp->w_rflag |= WFFULL | WFFRAME; 719 return (wp); 720 } 721 if (showbuffer(bp, wp, WFFULL) != TRUE) 722 return (NULL); 723 return (wp); 724 } 725 726 /* 727 * Insert another buffer at dot. Very useful. 728 */ 729 /* ARGSUSED */ 730 int 731 bufferinsert(int f, int n) 732 { 733 struct buffer *bp; 734 struct line *clp; 735 int clo, nline; 736 char bufn[NBUFN], *bufp; 737 738 /* Get buffer to use from user */ 739 if (curbp->b_altb != NULL) 740 bufp = eread("Insert buffer: (default %s) ", bufn, NBUFN, 741 EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); 742 else 743 bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF); 744 if (bufp == NULL) 745 return (ABORT); 746 if (bufp[0] == '\0' && curbp->b_altb != NULL) 747 bp = curbp->b_altb; 748 else if ((bp = bfind(bufn, FALSE)) == NULL) 749 return (FALSE); 750 751 if (bp == curbp) { 752 dobeep(); 753 ewprintf("Cannot insert buffer into self"); 754 return (FALSE); 755 } 756 /* insert the buffer */ 757 nline = 0; 758 clp = bfirstlp(bp); 759 for (;;) { 760 for (clo = 0; clo < llength(clp); clo++) 761 if (linsert(1, lgetc(clp, clo)) == FALSE) 762 return (FALSE); 763 if ((clp = lforw(clp)) == bp->b_headp) 764 break; 765 if (newline(FFRAND, 1) == FALSE) /* fake newline */ 766 return (FALSE); 767 nline++; 768 } 769 if (nline == 1) 770 ewprintf("[Inserted 1 line]"); 771 else 772 ewprintf("[Inserted %d lines]", nline); 773 774 clp = curwp->w_linep; /* cosmetic adjustment */ 775 if (curwp->w_dotp == clp) { /* for offscreen insert */ 776 while (nline-- && lback(clp) != curbp->b_headp) 777 clp = lback(clp); 778 curwp->w_linep = clp; /* adjust framing. */ 779 curwp->w_rflag |= WFFULL; 780 } 781 return (TRUE); 782 } 783 784 /* 785 * Turn off the dirty bit on this buffer. 786 */ 787 /* ARGSUSED */ 788 int 789 notmodified(int f, int n) 790 { 791 struct mgwin *wp; 792 793 curbp->b_flag &= ~BFCHG; 794 wp = wheadp; /* Update mode lines. */ 795 while (wp != NULL) { 796 if (wp->w_bufp == curbp) 797 wp->w_rflag |= WFMODE; 798 wp = wp->w_wndp; 799 } 800 ewprintf("Modification-flag cleared"); 801 return (TRUE); 802 } 803 804 /* 805 * Popbuf and set all windows to top of buffer. 806 */ 807 int 808 popbuftop(struct buffer *bp, int flags) 809 { 810 struct mgwin *wp; 811 812 bp->b_dotp = bfirstlp(bp); 813 bp->b_doto = 0; 814 if (bp->b_nwnd != 0) { 815 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 816 if (wp->w_bufp == bp) { 817 wp->w_dotp = bp->b_dotp; 818 wp->w_doto = 0; 819 wp->w_rflag |= WFFULL; 820 } 821 } 822 return (popbuf(bp, flags) != NULL); 823 } 824 825 /* 826 * Return the working directory for the current buffer, terminated 827 * with a '/'. First, try to extract it from the current buffer's 828 * filename. If that fails, use global cwd. 829 */ 830 int 831 getbufcwd(char *path, size_t plen) 832 { 833 char cwd[NFILEN]; 834 835 if (plen == 0) 836 return (FALSE); 837 838 if (globalwd == FALSE && curbp->b_cwd[0] != '\0') { 839 (void)strlcpy(path, curbp->b_cwd, plen); 840 } else { 841 if (getcwdir(cwd, sizeof(cwd)) == FALSE) 842 goto error; 843 (void)strlcpy(path, cwd, plen); 844 } 845 return (TRUE); 846 error: 847 path[0] = '\0'; 848 return (FALSE); 849 } 850 851 /* 852 * Ensures a buffer has not been modified elsewhere; e.g. on disk. 853 * Prompt the user if it has. 854 * Returns TRUE if it has NOT (i.e. buffer is ok to edit). 855 * FALSE or ABORT otherwise 856 */ 857 int 858 checkdirty(struct buffer *bp) 859 { 860 int s; 861 862 if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0) 863 if (fchecktime(bp) != TRUE) 864 bp->b_flag |= BFDIRTY; 865 866 if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) { 867 s = eynorr("File changed on disk; really edit the buffer"); 868 switch (s) { 869 case TRUE: 870 bp->b_flag &= ~BFDIRTY; 871 bp->b_flag |= BFIGNDIRTY; 872 return (TRUE); 873 case REVERT: 874 dorevert(); 875 return (FALSE); 876 default: 877 return (s); 878 } 879 } 880 881 return (TRUE); 882 } 883 884 /* 885 * Revert the current buffer to whatever is on disk. 886 */ 887 /* ARGSUSED */ 888 int 889 revertbuffer(int f, int n) 890 { 891 char fbuf[NFILEN + 32]; 892 893 if (curbp->b_fname[0] == 0) { 894 dobeep(); 895 ewprintf("Cannot revert buffer not associated with any files."); 896 return (FALSE); 897 } 898 899 snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s", 900 curbp->b_fname); 901 902 if (eyorn(fbuf) == TRUE) 903 return dorevert(); 904 905 return (FALSE); 906 } 907 908 int 909 dorevert(void) 910 { 911 int lineno; 912 struct undo_rec *rec; 913 914 if (access(curbp->b_fname, F_OK|R_OK) != 0) { 915 dobeep(); 916 if (errno == ENOENT) 917 ewprintf("File %s no longer exists!", 918 curbp->b_fname); 919 else 920 ewprintf("File %s is no longer readable!", 921 curbp->b_fname); 922 return (FALSE); 923 } 924 925 /* Save our current line, so we can go back after reloading. */ 926 lineno = curwp->w_dotline; 927 928 /* Prevent readin from asking if we want to kill the buffer. */ 929 curbp->b_flag &= ~BFCHG; 930 931 /* Clean up undo memory */ 932 while ((rec = TAILQ_FIRST(&curbp->b_undo))) { 933 TAILQ_REMOVE(&curbp->b_undo, rec, next); 934 free_undo_record(rec); 935 } 936 937 if (readin(curbp->b_fname)) 938 return(setlineno(lineno)); 939 return (FALSE); 940 } 941 942 /* 943 * Diff the current buffer to what is on disk. 944 */ 945 /*ARGSUSED */ 946 int 947 diffbuffer(int f, int n) 948 { 949 struct buffer *bp; 950 struct line *lp, *lpend; 951 size_t len; 952 int ret; 953 char *text, *ttext; 954 char * const argv[] = 955 {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL}; 956 957 len = 0; 958 959 /* C-u is not supported */ 960 if (n > 1) 961 return (ABORT); 962 963 if (access(DIFFTOOL, X_OK) != 0) { 964 dobeep(); 965 ewprintf("%s not found or not executable.", DIFFTOOL); 966 return (FALSE); 967 } 968 969 if (curbp->b_fname[0] == 0) { 970 dobeep(); 971 ewprintf("Cannot diff buffer not associated with any files."); 972 return (FALSE); 973 } 974 975 lpend = curbp->b_headp; 976 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 977 len+=llength(lp); 978 if (lforw(lp) != lpend) /* no implied \n on last line */ 979 len++; 980 } 981 if ((text = calloc(len + 1, sizeof(char))) == NULL) { 982 dobeep(); 983 ewprintf("Cannot allocate memory."); 984 return (FALSE); 985 } 986 ttext = text; 987 988 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 989 if (llength(lp) != 0) { 990 memcpy(ttext, ltext(lp), llength(lp)); 991 ttext += llength(lp); 992 } 993 if (lforw(lp) != lpend) /* no implied \n on last line */ 994 *ttext++ = '\n'; 995 } 996 997 bp = bfind("*Diff*", TRUE); 998 bp->b_flag |= BFREADONLY; 999 if (bclear(bp) != TRUE) { 1000 free(text); 1001 return (FALSE); 1002 } 1003 1004 ret = pipeio(DIFFTOOL, argv, text, len, bp); 1005 1006 if (ret == TRUE) { 1007 eerase(); 1008 if (lforw(bp->b_headp) == bp->b_headp) 1009 addline(bp, "Diff finished (no differences)."); 1010 } 1011 1012 free(text); 1013 return (ret); 1014 } 1015 1016 /* 1017 * Given a file name, either find the buffer it uses, or create a new 1018 * empty buffer to put it in. 1019 */ 1020 struct buffer * 1021 findbuffer(char *fn) 1022 { 1023 struct buffer *bp; 1024 char bname[NBUFN], fname[NBUFN]; 1025 1026 if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) { 1027 dobeep(); 1028 ewprintf("filename too long"); 1029 return (NULL); 1030 } 1031 1032 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 1033 if (strcmp(bp->b_fname, fname) == 0) 1034 return (bp); 1035 } 1036 /* Not found. Create a new one, adjusting name first */ 1037 if (augbname(bname, fname, sizeof(bname)) == FALSE) 1038 return (NULL); 1039 1040 bp = bfind(bname, TRUE); 1041 return (bp); 1042 } 1043