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