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