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