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