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