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 return(dobeep_msg("buffer name truncated")); 403 404 if ((line = malloc(listbuf_ncol/2)) == NULL) 405 return (FALSE); 406 407 memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5); 408 for (i = listbuf_ncol/2 - 6; i > 0; i--) { 409 if (line[i] != ' ') { 410 line[i + 1] = '\0'; 411 break; 412 } 413 } 414 if (i == 0) 415 goto cleanup; 416 417 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 418 if (strcmp(bp->b_bname, line) == 0) 419 break; 420 } 421 if (bp == NULL) 422 goto cleanup; 423 424 if ((wp = popbuf(bp, WNONE)) == NULL) 425 goto cleanup; 426 curbp = bp; 427 curwp = wp; 428 429 if (only) 430 ret = (onlywind(FFRAND, 1)); 431 else 432 ret = TRUE; 433 434 cleanup: 435 free(line); 436 437 return (ret); 438 } 439 440 /* 441 * The argument "fmt" points to a format string. Append this line to the 442 * buffer. Handcraft the EOL on the end. Return TRUE if it worked and 443 * FALSE if you ran out of room. 444 */ 445 int 446 addlinef(struct buffer *bp, char *fmt, ...) 447 { 448 va_list ap; 449 struct line *lp; 450 451 if ((lp = lalloc(0)) == NULL) 452 return (FALSE); 453 va_start(ap, fmt); 454 if (vasprintf(&lp->l_text, fmt, ap) == -1) { 455 lfree(lp); 456 va_end(ap); 457 return (FALSE); 458 } 459 lp->l_used = strlen(lp->l_text); 460 va_end(ap); 461 462 bp->b_headp->l_bp->l_fp = lp; /* Hook onto the end */ 463 lp->l_bp = bp->b_headp->l_bp; 464 bp->b_headp->l_bp = lp; 465 lp->l_fp = bp->b_headp; 466 bp->b_lines++; 467 468 return (TRUE); 469 } 470 471 /* 472 * Look through the list of buffers, giving the user a chance to save them. 473 * Return TRUE if there are any changed buffers afterwards. Buffers that don't 474 * have an associated file don't count. Return FALSE if there are no changed 475 * buffers. Return ABORT if an error occurs or if the user presses c-g. 476 */ 477 int 478 anycb(int f) 479 { 480 struct buffer *bp; 481 int s = FALSE, save = FALSE, save2 = FALSE, ret; 482 char pbuf[NFILEN + 11]; 483 484 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 485 if (*(bp->b_fname) != '\0' && (bp->b_flag & BFCHG) != 0) { 486 ret = snprintf(pbuf, sizeof(pbuf), "Save file %s", 487 bp->b_fname); 488 if (ret < 0 || ret >= sizeof(pbuf)) { 489 (void)dobeep_msg("Error: filename too long!"); 490 return (UERROR); 491 } 492 if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) && 493 (save2 = buffsave(bp)) == TRUE) { 494 bp->b_flag &= ~BFCHG; 495 upmodes(bp); 496 } else { 497 if (save2 == FIOERR) 498 return (save2); 499 s = TRUE; 500 } 501 if (save == ABORT) 502 return (save); 503 save = TRUE; 504 } 505 } 506 if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */ 507 ewprintf("(No files need saving)"); 508 return (s); 509 } 510 511 /* 512 * Search for a buffer, by name. 513 * If not found, and the "cflag" is TRUE, 514 * create a new buffer. Return pointer to the found 515 * (or new) buffer. 516 */ 517 struct buffer * 518 bfind(const char *bname, int cflag) 519 { 520 struct buffer *bp; 521 522 bp = bheadp; 523 while (bp != NULL) { 524 if (strcmp(bname, bp->b_bname) == 0) 525 return (bp); 526 bp = bp->b_bufp; 527 } 528 if (cflag != TRUE) 529 return (NULL); 530 531 bp = bnew(bname); 532 533 return (bp); 534 } 535 536 /* 537 * Create a new buffer and put it in the list of 538 * all buffers. 539 */ 540 static struct buffer * 541 bnew(const char *bname) 542 { 543 struct buffer *bp; 544 struct line *lp; 545 int i; 546 size_t len; 547 548 bp = calloc(1, sizeof(struct buffer)); 549 if (bp == NULL) { 550 dobeep(); 551 ewprintf("Can't get %d bytes", sizeof(struct buffer)); 552 return (NULL); 553 } 554 if ((lp = lalloc(0)) == NULL) { 555 free(bp); 556 return (NULL); 557 } 558 bp->b_altb = bp->b_bufp = NULL; 559 bp->b_dotp = lp; 560 bp->b_doto = 0; 561 bp->b_markp = NULL; 562 bp->b_marko = 0; 563 bp->b_flag = defb_flag; 564 /* if buffer name starts and ends with '*', we ignore changes */ 565 len = strlen(bname); 566 if (len) { 567 if (bname[0] == '*' && bname[len - 1] == '*') 568 bp->b_flag |= BFIGNDIRTY; 569 } 570 bp->b_nwnd = 0; 571 bp->b_headp = lp; 572 bp->b_nmodes = defb_nmodes; 573 TAILQ_INIT(&bp->b_undo); 574 bp->b_undoptr = NULL; 575 i = 0; 576 do { 577 bp->b_modes[i] = defb_modes[i]; 578 } while (i++ < defb_nmodes); 579 bp->b_fname[0] = '\0'; 580 bp->b_cwd[0] = '\0'; 581 bzero(&bp->b_fi, sizeof(bp->b_fi)); 582 lp->l_fp = lp; 583 lp->l_bp = lp; 584 bp->b_bufp = bheadp; 585 bheadp = bp; 586 bp->b_dotline = bp->b_markline = 1; 587 bp->b_lines = 1; 588 bp->b_nlseq = "\n"; /* use unix default */ 589 bp->b_nlchr = bp->b_nlseq; 590 if ((bp->b_bname = strdup(bname)) == NULL) { 591 dobeep(); 592 ewprintf("Can't get %d bytes", strlen(bname) + 1); 593 return (NULL); 594 } 595 596 return (bp); 597 } 598 599 /* 600 * This routine blows away all of the text 601 * in a buffer. If the buffer is marked as changed 602 * then we ask if it is ok to blow it away; this is 603 * to save the user the grief of losing text. The 604 * window chain is nearly always wrong if this gets 605 * called; the caller must arrange for the updates 606 * that are required. Return TRUE if everything 607 * looks good. 608 */ 609 int 610 bclear(struct buffer *bp) 611 { 612 struct line *lp; 613 int s; 614 615 /* Has buffer changed, and do we care? */ 616 if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 && 617 (s = eyesno("Buffer modified; kill anyway")) != TRUE) 618 return (s); 619 bp->b_flag &= ~BFCHG; /* Not changed */ 620 while ((lp = lforw(bp->b_headp)) != bp->b_headp) 621 lfree(lp); 622 bp->b_dotp = bp->b_headp; /* Fix dot */ 623 bp->b_doto = 0; 624 bp->b_markp = NULL; /* Invalidate "mark" */ 625 bp->b_marko = 0; 626 bp->b_dotline = bp->b_markline = 1; 627 bp->b_lines = 1; 628 629 return (TRUE); 630 } 631 632 /* 633 * Display the given buffer in the given window. Flags indicated 634 * action on redisplay. Update modified flag so insert loop can check it. 635 */ 636 int 637 showbuffer(struct buffer *bp, struct mgwin *wp, int flags) 638 { 639 struct buffer *obp; 640 struct mgwin *owp; 641 642 /* Ensure file has not been modified elsewhere */ 643 if (fchecktime(bp) != TRUE) 644 bp->b_flag |= BFDIRTY; 645 646 if (wp->w_bufp == bp) { /* Easy case! */ 647 wp->w_rflag |= flags; 648 return (TRUE); 649 } 650 /* First, detach the old buffer from the window */ 651 if ((bp->b_altb = obp = wp->w_bufp) != NULL) { 652 if (--obp->b_nwnd == 0) { 653 obp->b_dotp = wp->w_dotp; 654 obp->b_doto = wp->w_doto; 655 obp->b_markp = wp->w_markp; 656 obp->b_marko = wp->w_marko; 657 obp->b_dotline = wp->w_dotline; 658 obp->b_markline = wp->w_markline; 659 } 660 } 661 /* Now, attach the new buffer to the window */ 662 wp->w_bufp = bp; 663 664 if (bp->b_nwnd++ == 0) { /* First use. */ 665 wp->w_dotp = bp->b_dotp; 666 wp->w_doto = bp->b_doto; 667 wp->w_markp = bp->b_markp; 668 wp->w_marko = bp->b_marko; 669 wp->w_dotline = bp->b_dotline; 670 wp->w_markline = bp->b_markline; 671 } else 672 /* already on screen, steal values from other window */ 673 for (owp = wheadp; owp != NULL; owp = wp->w_wndp) 674 if (wp->w_bufp == bp && owp != wp) { 675 wp->w_dotp = owp->w_dotp; 676 wp->w_doto = owp->w_doto; 677 wp->w_markp = owp->w_markp; 678 wp->w_marko = owp->w_marko; 679 wp->w_dotline = owp->w_dotline; 680 wp->w_markline = owp->w_markline; 681 break; 682 } 683 wp->w_rflag |= WFMODE | flags; 684 return (TRUE); 685 } 686 687 /* 688 * Augment a buffer name with a number, if necessary 689 * 690 * If more than one file of the same basename() is open, 691 * the additional buffers are named "file<2>", "file<3>", and 692 * so forth. This function adjusts a buffer name to 693 * include the number, if necessary. 694 */ 695 int 696 augbname(char *bn, const char *fn, size_t bs) 697 { 698 int count; 699 size_t remain, len; 700 701 if ((len = xbasename(bn, fn, bs)) >= bs) 702 return (FALSE); 703 704 remain = bs - len; 705 for (count = 2; bfind(bn, FALSE) != NULL; count++) 706 snprintf(bn + len, remain, "<%d>", count); 707 708 return (TRUE); 709 } 710 711 /* 712 * Pop the buffer we got passed onto the screen. 713 * Returns a status. 714 */ 715 struct mgwin * 716 popbuf(struct buffer *bp, int flags) 717 { 718 struct mgwin *wp; 719 720 if (bp->b_nwnd == 0) { /* Not on screen yet. */ 721 /* 722 * Pick a window for a pop-up. 723 * If only one window, split the screen. 724 * Flag the new window as ephemeral 725 */ 726 if (wheadp->w_wndp == NULL && 727 splitwind(FFOTHARG, flags) == FALSE) 728 return (NULL); 729 730 /* 731 * Pick the uppermost window that isn't 732 * the current window. An LRU algorithm 733 * might be better. Return a pointer, or NULL on error. 734 */ 735 wp = wheadp; 736 737 while (wp != NULL && wp == curwp) 738 wp = wp->w_wndp; 739 } else { 740 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 741 if (wp->w_bufp == bp) { 742 wp->w_rflag |= WFFULL | WFFRAME; 743 return (wp); 744 } 745 } 746 } 747 if (!wp) 748 return (NULL); 749 750 if (showbuffer(bp, wp, WFFULL) != TRUE) 751 return (NULL); 752 return (wp); 753 } 754 755 /* 756 * Insert another buffer at dot. Very useful. 757 */ 758 /* ARGSUSED */ 759 int 760 bufferinsert(int f, int n) 761 { 762 struct buffer *bp; 763 struct line *clp; 764 int clo, nline; 765 char bufn[NBUFN], *bufp; 766 767 /* Get buffer to use from user */ 768 if (curbp->b_altb != NULL) 769 bufp = eread("Insert buffer (default %s): ", bufn, NBUFN, 770 EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); 771 else 772 bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF); 773 if (bufp == NULL) 774 return (ABORT); 775 if (bufp[0] == '\0' && curbp->b_altb != NULL) 776 bp = curbp->b_altb; 777 else if ((bp = bfind(bufn, FALSE)) == NULL) 778 return (FALSE); 779 780 if (bp == curbp) 781 return(dobeep_msg("Cannot insert buffer into self")); 782 783 /* insert the buffer */ 784 nline = 0; 785 clp = bfirstlp(bp); 786 for (;;) { 787 for (clo = 0; clo < llength(clp); clo++) 788 if (linsert(1, lgetc(clp, clo)) == FALSE) 789 return (FALSE); 790 if ((clp = lforw(clp)) == bp->b_headp) 791 break; 792 if (enewline(FFRAND, 1) == FALSE) /* fake newline */ 793 return (FALSE); 794 nline++; 795 } 796 if (nline == 1) 797 ewprintf("[Inserted 1 line]"); 798 else 799 ewprintf("[Inserted %d lines]", nline); 800 801 clp = curwp->w_linep; /* cosmetic adjustment */ 802 if (curwp->w_dotp == clp) { /* for offscreen insert */ 803 while (nline-- && lback(clp) != curbp->b_headp) 804 clp = lback(clp); 805 curwp->w_linep = clp; /* adjust framing. */ 806 curwp->w_rflag |= WFFULL; 807 } 808 return (TRUE); 809 } 810 811 /* 812 * Turn off the dirty bit on this buffer. 813 */ 814 /* ARGSUSED */ 815 int 816 notmodified(int f, int n) 817 { 818 struct mgwin *wp; 819 820 curbp->b_flag &= ~BFCHG; 821 wp = wheadp; /* Update mode lines. */ 822 while (wp != NULL) { 823 if (wp->w_bufp == curbp) 824 wp->w_rflag |= WFMODE; 825 wp = wp->w_wndp; 826 } 827 ewprintf("Modification-flag cleared"); 828 return (TRUE); 829 } 830 831 /* 832 * Popbuf and set all windows to top of buffer. 833 */ 834 int 835 popbuftop(struct buffer *bp, int flags) 836 { 837 struct mgwin *wp; 838 839 bp->b_dotp = bfirstlp(bp); 840 bp->b_doto = 0; 841 if (bp->b_nwnd != 0) { 842 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 843 if (wp->w_bufp == bp) { 844 wp->w_dotp = bp->b_dotp; 845 wp->w_doto = 0; 846 wp->w_rflag |= WFFULL; 847 } 848 } 849 return (popbuf(bp, flags) != NULL); 850 } 851 852 /* 853 * Return the working directory for the current buffer, terminated 854 * with a '/'. First, try to extract it from the current buffer's 855 * filename. If that fails, use global cwd. 856 */ 857 int 858 getbufcwd(char *path, size_t plen) 859 { 860 char cwd[NFILEN]; 861 862 if (plen == 0) 863 return (FALSE); 864 865 if (globalwd == FALSE && curbp->b_cwd[0] != '\0') { 866 (void)strlcpy(path, curbp->b_cwd, plen); 867 } else { 868 if (getcwdir(cwd, sizeof(cwd)) == FALSE) 869 goto error; 870 (void)strlcpy(path, cwd, plen); 871 } 872 return (TRUE); 873 error: 874 path[0] = '\0'; 875 return (FALSE); 876 } 877 878 /* 879 * Ensures a buffer has not been modified elsewhere; e.g. on disk. 880 * Prompt the user if it has. 881 * Returns TRUE if it has NOT (i.e. buffer is ok to edit). 882 * FALSE or ABORT otherwise 883 */ 884 int 885 checkdirty(struct buffer *bp) 886 { 887 int s; 888 889 if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0) 890 if (fchecktime(bp) != TRUE) 891 bp->b_flag |= BFDIRTY; 892 893 if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) { 894 s = eynorr("File changed on disk; really edit the buffer"); 895 switch (s) { 896 case TRUE: 897 bp->b_flag &= ~BFDIRTY; 898 bp->b_flag |= BFIGNDIRTY; 899 return (TRUE); 900 case REVERT: 901 dorevert(); 902 return (FALSE); 903 default: 904 return (s); 905 } 906 } 907 908 return (TRUE); 909 } 910 911 /* 912 * Revert the current buffer to whatever is on disk. 913 */ 914 /* ARGSUSED */ 915 int 916 revertbuffer(int f, int n) 917 { 918 char fbuf[NFILEN + 32]; 919 920 if (curbp->b_fname[0] == 0) 921 return(dobeep_msg("Cannot revert buffer not associated " 922 "with any files.")); 923 924 snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s", 925 curbp->b_fname); 926 927 if (eyorn(fbuf) == TRUE) 928 return dorevert(); 929 930 return (FALSE); 931 } 932 933 int 934 dorevert(void) 935 { 936 int lineno; 937 struct undo_rec *rec; 938 939 if (access(curbp->b_fname, F_OK|R_OK) != 0) { 940 dobeep(); 941 if (errno == ENOENT) 942 ewprintf("File %s no longer exists!", 943 curbp->b_fname); 944 else 945 ewprintf("File %s is no longer readable!", 946 curbp->b_fname); 947 return (FALSE); 948 } 949 950 /* Save our current line, so we can go back after reloading. */ 951 lineno = curwp->w_dotline; 952 953 /* Prevent readin from asking if we want to kill the buffer. */ 954 curbp->b_flag &= ~BFCHG; 955 956 /* Clean up undo memory */ 957 while ((rec = TAILQ_FIRST(&curbp->b_undo))) { 958 TAILQ_REMOVE(&curbp->b_undo, rec, next); 959 free_undo_record(rec); 960 } 961 962 if (readin(curbp->b_fname)) 963 return(setlineno(lineno)); 964 return (FALSE); 965 } 966 967 /* 968 * Diff the current buffer to what is on disk. 969 */ 970 /*ARGSUSED */ 971 int 972 diffbuffer(int f, int n) 973 { 974 struct buffer *bp; 975 struct line *lp, *lpend; 976 size_t len; 977 int ret; 978 char *text, *ttext; 979 char * const argv[] = 980 {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL}; 981 982 len = 0; 983 984 /* C-u is not supported */ 985 if (n > 1) 986 return (ABORT); 987 988 if (access(DIFFTOOL, X_OK) != 0) { 989 dobeep(); 990 ewprintf("%s not found or not executable.", DIFFTOOL); 991 return (FALSE); 992 } 993 994 if (curbp->b_fname[0] == 0) 995 return(dobeep_msg("Cannot diff buffer not associated with " 996 "any files.")); 997 998 lpend = curbp->b_headp; 999 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 1000 len+=llength(lp); 1001 if (lforw(lp) != lpend) /* no implied \n on last line */ 1002 len++; 1003 } 1004 if ((text = calloc(len + 1, sizeof(char))) == NULL) 1005 return(dobeep_msg("Cannot allocate memory.")); 1006 1007 ttext = text; 1008 1009 for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { 1010 if (llength(lp) != 0) { 1011 memcpy(ttext, ltext(lp), llength(lp)); 1012 ttext += llength(lp); 1013 } 1014 if (lforw(lp) != lpend) /* no implied \n on last line */ 1015 *ttext++ = *curbp->b_nlchr; 1016 } 1017 1018 bp = bfind("*Diff*", TRUE); 1019 bp->b_flag |= BFREADONLY; 1020 if (bclear(bp) != TRUE) { 1021 free(text); 1022 return (FALSE); 1023 } 1024 1025 ret = pipeio(DIFFTOOL, argv, text, len, bp); 1026 1027 if (ret == TRUE) { 1028 eerase(); 1029 if (lforw(bp->b_headp) == bp->b_headp) 1030 addline(bp, "Diff finished (no differences)."); 1031 } 1032 1033 free(text); 1034 return (ret); 1035 } 1036 1037 /* 1038 * Given a file name, either find the buffer it uses, or create a new 1039 * empty buffer to put it in. 1040 */ 1041 struct buffer * 1042 findbuffer(char *fn) 1043 { 1044 struct buffer *bp; 1045 char bname[NBUFN], fname[NBUFN]; 1046 1047 if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) { 1048 (void)dobeep_msg("filename too long"); 1049 return (NULL); 1050 } 1051 1052 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 1053 if (strcmp(bp->b_fname, fname) == 0) 1054 return (bp); 1055 } 1056 /* Not found. Create a new one, adjusting name first */ 1057 if (augbname(bname, fname, sizeof(bname)) == FALSE) 1058 return (NULL); 1059 1060 bp = bfind(bname, TRUE); 1061 return (bp); 1062 } 1063