1 /* $OpenBSD: buffer.c,v 1.98 2015/03/23 12:31:19 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; 59 60 if (bufp == NULL) 61 return (ABORT); 62 if (bufp[0] == '\0' && curbp->b_altb != NULL) 63 bp = curbp->b_altb; 64 else if ((bp = bfind(bufp, TRUE)) == NULL) 65 return (FALSE); 66 67 /* and put it in current window */ 68 curbp = bp; 69 return (showbuffer(bp, curwp, WFFRAME | WFFULL)); 70 } 71 72 /* 73 * Attach a buffer to a window. The values of dot and mark come 74 * from the buffer if the use count is 0. Otherwise, they come 75 * from some other window. *scratch* is the default alternate 76 * buffer. 77 */ 78 /* ARGSUSED */ 79 int 80 usebuffer(int f, int n) 81 { 82 char bufn[NBUFN], *bufp; 83 84 /* Get buffer to use from user */ 85 if ((curbp->b_altb == NULL) && 86 ((curbp->b_altb = bfind("*scratch*", TRUE)) == 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 bp1 = (bp == bheadp) ? bp->b_bufp : bheadp; 173 if (bp1 == NULL) { 174 /* only one buffer. see if it's *scratch* */ 175 if (bp == bfind("*scratch*", FALSE)) 176 return (TRUE); 177 /* create *scratch* for alternate buffer */ 178 if ((bp1 = bfind("*scratch*", TRUE)) == NULL) 179 return (FALSE); 180 } 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(f, n)); 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 if (showbuffer(bp, wp, WFFULL) != TRUE) 726 return (NULL); 727 return (wp); 728 } 729 730 /* 731 * Insert another buffer at dot. Very useful. 732 */ 733 /* ARGSUSED */ 734 int 735 bufferinsert(int f, int n) 736 { 737 struct buffer *bp; 738 struct line *clp; 739 int clo, nline; 740 char bufn[NBUFN], *bufp; 741 742 /* Get buffer to use from user */ 743 if (curbp->b_altb != NULL) 744 bufp = eread("Insert buffer: (default %s) ", bufn, NBUFN, 745 EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); 746 else 747 bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF); 748 if (bufp == NULL) 749 return (ABORT); 750 if (bufp[0] == '\0' && curbp->b_altb != NULL) 751 bp = curbp->b_altb; 752 else if ((bp = bfind(bufn, FALSE)) == NULL) 753 return (FALSE); 754 755 if (bp == curbp) { 756 dobeep(); 757 ewprintf("Cannot insert buffer into self"); 758 return (FALSE); 759 } 760 /* insert the buffer */ 761 nline = 0; 762 clp = bfirstlp(bp); 763 for (;;) { 764 for (clo = 0; clo < llength(clp); clo++) 765 if (linsert(1, lgetc(clp, clo)) == FALSE) 766 return (FALSE); 767 if ((clp = lforw(clp)) == bp->b_headp) 768 break; 769 if (enewline(FFRAND, 1) == FALSE) /* fake newline */ 770 return (FALSE); 771 nline++; 772 } 773 if (nline == 1) 774 ewprintf("[Inserted 1 line]"); 775 else 776 ewprintf("[Inserted %d lines]", nline); 777 778 clp = curwp->w_linep; /* cosmetic adjustment */ 779 if (curwp->w_dotp == clp) { /* for offscreen insert */ 780 while (nline-- && lback(clp) != curbp->b_headp) 781 clp = lback(clp); 782 curwp->w_linep = clp; /* adjust framing. */ 783 curwp->w_rflag |= WFFULL; 784 } 785 return (TRUE); 786 } 787 788 /* 789 * Turn off the dirty bit on this buffer. 790 */ 791 /* ARGSUSED */ 792 int 793 notmodified(int f, int n) 794 { 795 struct mgwin *wp; 796 797 curbp->b_flag &= ~BFCHG; 798 wp = wheadp; /* Update mode lines. */ 799 while (wp != NULL) { 800 if (wp->w_bufp == curbp) 801 wp->w_rflag |= WFMODE; 802 wp = wp->w_wndp; 803 } 804 ewprintf("Modification-flag cleared"); 805 return (TRUE); 806 } 807 808 /* 809 * Popbuf and set all windows to top of buffer. 810 */ 811 int 812 popbuftop(struct buffer *bp, int flags) 813 { 814 struct mgwin *wp; 815 816 bp->b_dotp = bfirstlp(bp); 817 bp->b_doto = 0; 818 if (bp->b_nwnd != 0) { 819 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 820 if (wp->w_bufp == bp) { 821 wp->w_dotp = bp->b_dotp; 822 wp->w_doto = 0; 823 wp->w_rflag |= WFFULL; 824 } 825 } 826 return (popbuf(bp, flags) != NULL); 827 } 828 829 /* 830 * Return the working directory for the current buffer, terminated 831 * with a '/'. First, try to extract it from the current buffer's 832 * filename. If that fails, use global cwd. 833 */ 834 int 835 getbufcwd(char *path, size_t plen) 836 { 837 char cwd[NFILEN]; 838 839 if (plen == 0) 840 return (FALSE); 841 842 if (globalwd == FALSE && curbp->b_cwd[0] != '\0') { 843 (void)strlcpy(path, curbp->b_cwd, plen); 844 } else { 845 if (getcwdir(cwd, sizeof(cwd)) == FALSE) 846 goto error; 847 (void)strlcpy(path, cwd, plen); 848 } 849 return (TRUE); 850 error: 851 path[0] = '\0'; 852 return (FALSE); 853 } 854 855 /* 856 * Ensures a buffer has not been modified elsewhere; e.g. on disk. 857 * Prompt the user if it has. 858 * Returns TRUE if it has NOT (i.e. buffer is ok to edit). 859 * FALSE or ABORT otherwise 860 */ 861 int 862 checkdirty(struct buffer *bp) 863 { 864 int s; 865 866 if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0) 867 if (fchecktime(bp) != TRUE) 868 bp->b_flag |= BFDIRTY; 869 870 if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) { 871 s = eynorr("File changed on disk; really edit the buffer"); 872 switch (s) { 873 case TRUE: 874 bp->b_flag &= ~BFDIRTY; 875 bp->b_flag |= BFIGNDIRTY; 876 return (TRUE); 877 case REVERT: 878 dorevert(); 879 return (FALSE); 880 default: 881 return (s); 882 } 883 } 884 885 return (TRUE); 886 } 887 888 /* 889 * Revert the current buffer to whatever is on disk. 890 */ 891 /* ARGSUSED */ 892 int 893 revertbuffer(int f, int n) 894 { 895 char fbuf[NFILEN + 32]; 896 897 if (curbp->b_fname[0] == 0) { 898 dobeep(); 899 ewprintf("Cannot revert buffer not associated with any files."); 900 return (FALSE); 901 } 902 903 snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s", 904 curbp->b_fname); 905 906 if (eyorn(fbuf) == TRUE) 907 return dorevert(); 908 909 return (FALSE); 910 } 911 912 int 913 dorevert(void) 914 { 915 int lineno; 916 struct undo_rec *rec; 917 918 if (access(curbp->b_fname, F_OK|R_OK) != 0) { 919 dobeep(); 920 if (errno == ENOENT) 921 ewprintf("File %s no longer exists!", 922 curbp->b_fname); 923 else 924 ewprintf("File %s is no longer readable!", 925 curbp->b_fname); 926 return (FALSE); 927 } 928 929 /* Save our current line, so we can go back after reloading. */ 930 lineno = curwp->w_dotline; 931 932 /* Prevent readin from asking if we want to kill the buffer. */ 933 curbp->b_flag &= ~BFCHG; 934 935 /* Clean up undo memory */ 936 while ((rec = TAILQ_FIRST(&curbp->b_undo))) { 937 TAILQ_REMOVE(&curbp->b_undo, rec, next); 938 free_undo_record(rec); 939 } 940 941 if (readin(curbp->b_fname)) 942 return(setlineno(lineno)); 943 return (FALSE); 944 } 945 946 /* 947 * Diff the current buffer to what is on disk. 948 */ 949 /*ARGSUSED */ 950 int 951 diffbuffer(int f, int n) 952 { 953 struct buffer *bp; 954 struct line *lp, *lpend; 955 size_t len; 956 int ret; 957 char *text, *ttext; 958 char * const argv[] = 959 {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL}; 960 961 len = 0; 962 963 /* C-u is not supported */ 964 if (n > 1) 965 return (ABORT); 966 967 if (access(DIFFTOOL, X_OK) != 0) { 968 dobeep(); 969 ewprintf("%s not found or not executable.", DIFFTOOL); 970 return (FALSE); 971 } 972 973 if (curbp->b_fname[0] == 0) { 974 dobeep(); 975 ewprintf("Cannot diff buffer not associated with any files."); 976 return (FALSE); 977 } 978 979 lpend = curbp->b_headp; 980 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 981 len+=llength(lp); 982 if (lforw(lp) != lpend) /* no implied \n on last line */ 983 len++; 984 } 985 if ((text = calloc(len + 1, sizeof(char))) == NULL) { 986 dobeep(); 987 ewprintf("Cannot allocate memory."); 988 return (FALSE); 989 } 990 ttext = text; 991 992 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 993 if (llength(lp) != 0) { 994 memcpy(ttext, ltext(lp), llength(lp)); 995 ttext += llength(lp); 996 } 997 if (lforw(lp) != lpend) /* no implied \n on last line */ 998 *ttext++ = '\n'; 999 } 1000 1001 bp = bfind("*Diff*", TRUE); 1002 bp->b_flag |= BFREADONLY; 1003 if (bclear(bp) != TRUE) { 1004 free(text); 1005 return (FALSE); 1006 } 1007 1008 ret = pipeio(DIFFTOOL, argv, text, len, bp); 1009 1010 if (ret == TRUE) { 1011 eerase(); 1012 if (lforw(bp->b_headp) == bp->b_headp) 1013 addline(bp, "Diff finished (no differences)."); 1014 } 1015 1016 free(text); 1017 return (ret); 1018 } 1019 1020 /* 1021 * Given a file name, either find the buffer it uses, or create a new 1022 * empty buffer to put it in. 1023 */ 1024 struct buffer * 1025 findbuffer(char *fn) 1026 { 1027 struct buffer *bp; 1028 char bname[NBUFN], fname[NBUFN]; 1029 1030 if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) { 1031 dobeep(); 1032 ewprintf("filename too long"); 1033 return (NULL); 1034 } 1035 1036 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 1037 if (strcmp(bp->b_fname, fname) == 0) 1038 return (bp); 1039 } 1040 /* Not found. Create a new one, adjusting name first */ 1041 if (augbname(bname, fname, sizeof(bname)) == FALSE) 1042 return (NULL); 1043 1044 bp = bfind(bname, TRUE); 1045 return (bp); 1046 } 1047