1 /* $OpenBSD: buffer.c,v 1.33 2003/10/21 22:48:07 vincent Exp $ */ 2 3 /* 4 * Buffer handling. 5 */ 6 7 #include "def.h" 8 #include "kbd.h" /* needed for modes */ 9 #include <stdarg.h> 10 11 static BUFFER *makelist(void); 12 13 int 14 togglereadonly(int f, int n) 15 { 16 if (!(curbp->b_flag & BFREADONLY)) { 17 curbp->b_flag |= BFREADONLY; 18 ewprintf("Now readonly"); 19 } else { 20 curbp->b_flag &=~ BFREADONLY; 21 if (curbp->b_flag & BFCHG) 22 ewprintf("Warning: Buffer was modified"); 23 } 24 curwp->w_flag |= WFMODE; 25 26 return(1); 27 } 28 29 /* 30 * Attach a buffer to a window. The values of dot and mark come 31 * from the buffer if the use count is 0. Otherwise, they come 32 * from some other window. *scratch* is the default alternate 33 * buffer. 34 */ 35 /* ARGSUSED */ 36 int 37 usebuffer(int f, int n) 38 { 39 BUFFER *bp; 40 int s; 41 char bufn[NBUFN]; 42 43 /* Get buffer to use from user */ 44 if ((curbp->b_altb == NULL) && 45 ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL)) 46 s = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF); 47 else 48 s = eread("Switch to buffer: (default %s) ", bufn, NBUFN, 49 EFNEW | EFBUF, curbp->b_altb->b_bname); 50 51 if (s == ABORT) 52 return s; 53 if (s == FALSE && curbp->b_altb != NULL) 54 bp = curbp->b_altb; 55 else if ((bp = bfind(bufn, TRUE)) == NULL) 56 return FALSE; 57 58 /* and put it in current window */ 59 curbp = bp; 60 return showbuffer(bp, curwp, WFFORCE | WFHARD); 61 } 62 63 /* 64 * pop to buffer asked for by the user. 65 */ 66 /* ARGSUSED */ 67 int 68 poptobuffer(int f, int n) 69 { 70 BUFFER *bp; 71 MGWIN *wp; 72 int s; 73 char bufn[NBUFN]; 74 75 /* Get buffer to use from user */ 76 if ((curbp->b_altb == NULL) && 77 ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL)) 78 s = eread("Switch to buffer in other window: ", bufn, NBUFN, 79 EFNEW | EFBUF); 80 else 81 s = eread("Switch to buffer in other window: (default %s) ", 82 bufn, NBUFN, EFNEW | EFBUF, curbp->b_altb->b_bname); 83 if (s == ABORT) 84 return s; 85 if (s == FALSE && curbp->b_altb != NULL) 86 bp = curbp->b_altb; 87 else if ((bp = bfind(bufn, TRUE)) == NULL) 88 return FALSE; 89 90 /* and put it in a new window */ 91 if ((wp = popbuf(bp)) == NULL) 92 return FALSE; 93 curbp = bp; 94 curwp = wp; 95 return TRUE; 96 } 97 98 /* 99 * Dispose of a buffer, by name. 100 * Ask for the name. Look it up (don't get too 101 * upset if it isn't there at all!). Clear the buffer (ask 102 * if the buffer has been changed). Then free the header 103 * line and the buffer header. Bound to "C-X K". 104 */ 105 /* ARGSUSED */ 106 int 107 killbuffer(int f, int n) 108 { 109 BUFFER *bp; 110 BUFFER *bp1; 111 BUFFER *bp2; 112 MGWIN *wp; 113 int s; 114 char bufn[NBUFN]; 115 116 if ((s = eread("Kill buffer: (default %s) ", bufn, NBUFN, EFNEW | EFBUF, 117 curbp->b_bname)) == ABORT) 118 return (s); 119 else if (s == FALSE) 120 bp = curbp; 121 else if ((bp = bfind(bufn, FALSE)) == NULL) 122 return FALSE; 123 124 /* 125 * Find some other buffer to display. try the alternate buffer, 126 * then the first different buffer in the buffer list. If there's 127 * only one buffer, create buffer *scratch* and make it the alternate 128 * buffer. Return if *scratch* is only buffer... 129 */ 130 if ((bp1 = bp->b_altb) == NULL) { 131 bp1 = (bp == bheadp) ? bp->b_bufp : bheadp; 132 if (bp1 == NULL) { 133 /* only one buffer. see if it's *scratch* */ 134 if (bp == bfind("*scratch*", FALSE)) 135 return FALSE; 136 /* create *scratch* for alternate buffer */ 137 if ((bp1 = bfind("*scratch*", TRUE)) == NULL) 138 return FALSE; 139 } 140 } 141 if (bclear(bp) != TRUE) 142 return TRUE; 143 for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) { 144 if (wp->w_bufp == bp) { 145 bp2 = bp1->b_altb; /* save alternate buffer */ 146 if (showbuffer(bp1, wp, WFMODE | WFFORCE | WFHARD)) 147 bp1->b_altb = bp2; 148 else 149 bp1 = bp2; 150 } 151 } 152 if (bp == curbp) 153 curbp = bp1; 154 free(bp->b_linep); /* Release header line. */ 155 bp2 = NULL; /* Find the header. */ 156 bp1 = bheadp; 157 while (bp1 != bp) { 158 if (bp1->b_altb == bp) 159 bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb; 160 bp2 = bp1; 161 bp1 = bp1->b_bufp; 162 } 163 bp1 = bp1->b_bufp; /* Next one in chain. */ 164 if (bp2 == NULL) /* Unlink it. */ 165 bheadp = bp1; 166 else 167 bp2->b_bufp = bp1; 168 while (bp1 != NULL) { /* Finish with altb's */ 169 if (bp1->b_altb == bp) 170 bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb; 171 bp1 = bp1->b_bufp; 172 } 173 free((char *)bp->b_bname); /* Release name block */ 174 free(bp); /* Release buffer block */ 175 return TRUE; 176 } 177 178 /* 179 * Save some buffers - just call anycb with the arg flag. 180 */ 181 /* ARGSUSED */ 182 int 183 savebuffers(int f, int n) 184 { 185 if (anycb(f) == ABORT) 186 return ABORT; 187 return TRUE; 188 } 189 190 /* 191 * Listing buffers. 192 */ 193 static int listbuf_ncol; 194 195 static int listbuf_goto_buffer(int f, int n); 196 197 static PF listbuf_pf[] = { 198 listbuf_goto_buffer, 199 }; 200 201 static struct KEYMAPE (1 + IMAPEXT) listbufmap = { 202 1, 203 1 + IMAPEXT, 204 rescan, 205 { 206 { CCHR('M'), CCHR('M'), listbuf_pf, NULL }, 207 } 208 }; 209 210 /* 211 * Display the buffer list. This is done 212 * in two parts. The "makelist" routine figures out 213 * the text, and puts it in a buffer. "popbuf" 214 * then pops the data onto the screen. Bound to 215 * "C-X C-B". 216 */ 217 /* ARGSUSED */ 218 int 219 listbuffers(int f, int n) 220 { 221 static int initialized = 0; 222 BUFFER *bp; 223 MGWIN *wp; 224 225 if (!initialized) { 226 maps_add((KEYMAP *)&listbufmap, "listbufmap"); 227 initialized = 1; 228 } 229 230 if ((bp = makelist()) == NULL || (wp = popbuf(bp)) == NULL) 231 return FALSE; 232 wp->w_dotp = bp->b_dotp;/* fix up if window already on screen */ 233 wp->w_doto = bp->b_doto; 234 bp->b_modes[0] = name_mode("fundamental"); 235 bp->b_modes[1] = name_mode("listbufmap"); 236 bp->b_nmodes = 1; 237 238 return TRUE; 239 } 240 241 /* 242 * This routine rebuilds the text for the 243 * list buffers command. Return TRUE if 244 * everything works. Return FALSE if there 245 * is an error (if there is no memory). 246 */ 247 static BUFFER * 248 makelist(void) 249 { 250 int w = ncol / 2; 251 BUFFER *bp, *blp; 252 LINE *lp; 253 254 255 if ((blp = bfind("*Buffer List*", TRUE)) == NULL) 256 return NULL; 257 if (bclear(blp) != TRUE) 258 return NULL; 259 blp->b_flag &= ~BFCHG; /* Blow away old. */ 260 blp->b_flag |= BFREADONLY; 261 262 listbuf_ncol = ncol; /* cache ncol for listbuf_goto_buffer */ 263 264 if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size File") == FALSE || 265 addlinef(blp, "%-*s%s", w, " -- ------", "---- ----") == FALSE) 266 return NULL; 267 268 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 269 RSIZE nbytes; 270 271 nbytes = 0; /* Count bytes in buf. */ 272 if (bp != blp) { 273 lp = lforw(bp->b_linep); 274 while (lp != bp->b_linep) { 275 nbytes += llength(lp) + 1; 276 lp = lforw(lp); 277 } 278 if (nbytes) 279 nbytes--; /* no bonus newline */ 280 } 281 282 if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s", 283 (bp == curbp) ? '.' : ' ', /* current buffer ? */ 284 ((bp->b_flag & BFCHG) != 0) ? '*' : ' ', /* changed ? */ 285 ((bp->b_flag & BFREADONLY) != 0) ? ' ' : '*', 286 w - 5, /* four chars already written */ 287 w - 5, /* four chars already written */ 288 bp->b_bname, /* buffer name */ 289 strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */ 290 nbytes, /* buffer size */ 291 w - 7, /* seven chars already written */ 292 bp->b_fname) == FALSE) 293 return NULL; 294 } 295 blp->b_dotp = lforw(blp->b_linep); /* put dot at beginning of 296 * buffer */ 297 blp->b_doto = 0; 298 return blp; /* All done */ 299 } 300 301 static int 302 listbuf_goto_buffer(int f, int n) 303 { 304 BUFFER *bp; 305 MGWIN *wp; 306 char *line; 307 int i; 308 309 if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$') { 310 ewprintf("buffer name truncated"); 311 return FALSE; 312 } 313 314 if ((line = malloc(listbuf_ncol/2)) == NULL) 315 return FALSE; 316 317 memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5); 318 for (i = listbuf_ncol/2 - 6; i > 0; i--) { 319 if (line[i] != ' ') { 320 line[i + 1] = '\0'; 321 break; 322 } 323 } 324 if (i == 0) { 325 return FALSE; 326 } 327 328 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 329 if (strcmp(bp->b_bname, line) == 0) 330 break; 331 } 332 if (bp == NULL) { 333 return FALSE; 334 } 335 if ((wp = popbuf(bp)) == NULL) 336 return FALSE; 337 curbp = bp; 338 curwp = wp; 339 340 return TRUE; 341 } 342 343 /* 344 * The argument "text" points to a format string. Append this line to the 345 * buffer. Handcraft the EOL on the end. Return TRUE if it worked and 346 * FALSE if you ran out of room. 347 */ 348 int 349 addlinef(BUFFER *bp, char *fmt, ...) 350 { 351 va_list ap; 352 LINE *lp; 353 354 if ((lp = lalloc(0)) == NULL) 355 return (FALSE); 356 va_start(ap, fmt); 357 if (vasprintf(&lp->l_text, fmt, ap) == -1) { 358 lfree(lp); 359 va_end(ap); 360 return (FALSE); 361 } 362 lp->l_used = strlen(lp->l_text); 363 va_end(ap); 364 365 bp->b_linep->l_bp->l_fp = lp; /* Hook onto the end */ 366 lp->l_bp = bp->b_linep->l_bp; 367 bp->b_linep->l_bp = lp; 368 lp->l_fp = bp->b_linep; 369 370 return TRUE; 371 } 372 373 /* 374 * Look through the list of buffers, giving the user a chance to save them. 375 * Return TRUE if there are any changed buffers afterwards. Buffers that 376 * don't have an associated file don't count. Return FALSE if there are 377 * no changed buffers. 378 */ 379 int 380 anycb(int f) 381 { 382 BUFFER *bp; 383 int s = FALSE, save = FALSE; 384 char prompt[NFILEN + 11]; 385 386 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 387 if (bp->b_fname != NULL && *(bp->b_fname) != '\0' && 388 (bp->b_flag & BFCHG) != 0) { 389 snprintf(prompt, sizeof prompt, "Save file %s", 390 bp->b_fname); 391 if ((f == TRUE || (save = eyorn(prompt)) == TRUE) && 392 buffsave(bp) == TRUE) { 393 bp->b_flag &= ~BFCHG; 394 upmodes(bp); 395 } else 396 s = TRUE; 397 if (save == ABORT) 398 return (save); 399 save = TRUE; 400 } 401 } 402 if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */ 403 ewprintf("(No files need saving)"); 404 return s; 405 } 406 407 /* 408 * Search for a buffer, by name. 409 * If not found, and the "cflag" is TRUE, 410 * create a buffer and put it in the list of 411 * all buffers. Return pointer to the BUFFER 412 * block for the buffer. 413 */ 414 BUFFER * 415 bfind(const char *bname, int cflag) 416 { 417 BUFFER *bp; 418 LINE *lp; 419 int i; 420 421 bp = bheadp; 422 while (bp != NULL) { 423 if (strcmp(bname, bp->b_bname) == 0) 424 return bp; 425 bp = bp->b_bufp; 426 } 427 if (cflag != TRUE) 428 return NULL; 429 430 bp = calloc(1, sizeof(BUFFER)); 431 if (bp == NULL) { 432 ewprintf("Can't get %d bytes", sizeof(BUFFER)); 433 return NULL; 434 } 435 if ((bp->b_bname = strdup(bname)) == NULL) { 436 ewprintf("Can't get %d bytes", strlen(bname) + 1); 437 free(bp); 438 return NULL; 439 } 440 if ((lp = lalloc(0)) == NULL) { 441 free((char *) bp->b_bname); 442 free(bp); 443 return NULL; 444 } 445 bp->b_altb = bp->b_bufp = NULL; 446 bp->b_dotp = lp; 447 bp->b_doto = 0; 448 bp->b_markp = NULL; 449 bp->b_marko = 0; 450 bp->b_flag = defb_flag; 451 bp->b_nwnd = 0; 452 bp->b_linep = lp; 453 bp->b_nmodes = defb_nmodes; 454 i = 0; 455 do { 456 bp->b_modes[i] = defb_modes[i]; 457 } while (i++ < defb_nmodes); 458 bp->b_fname[0] = '\0'; 459 bzero(&bp->b_fi, sizeof(bp->b_fi)); 460 lp->l_fp = lp; 461 lp->l_bp = lp; 462 bp->b_bufp = bheadp; 463 bheadp = bp; 464 return bp; 465 } 466 467 /* 468 * This routine blows away all of the text 469 * in a buffer. If the buffer is marked as changed 470 * then we ask if it is ok to blow it away; this is 471 * to save the user the grief of losing text. The 472 * window chain is nearly always wrong if this gets 473 * called; the caller must arrange for the updates 474 * that are required. Return TRUE if everything 475 * looks good. 476 */ 477 int 478 bclear(BUFFER *bp) 479 { 480 LINE *lp; 481 int s; 482 483 if ((bp->b_flag & BFCHG) != 0 && /* Changed. */ 484 (s = eyesno("Buffer modified; kill anyway")) != TRUE) 485 return (s); 486 bp->b_flag &= ~BFCHG; /* Not changed */ 487 while ((lp = lforw(bp->b_linep)) != bp->b_linep) 488 lfree(lp); 489 bp->b_dotp = bp->b_linep; /* Fix dot */ 490 bp->b_doto = 0; 491 bp->b_markp = NULL; /* Invalidate "mark" */ 492 bp->b_marko = 0; 493 return TRUE; 494 } 495 496 /* 497 * Display the given buffer in the given window. Flags indicated 498 * action on redisplay. 499 */ 500 int 501 showbuffer(BUFFER *bp, MGWIN *wp, int flags) 502 { 503 BUFFER *obp; 504 MGWIN *owp; 505 506 if (wp->w_bufp == bp) { /* Easy case! */ 507 wp->w_flag |= flags; 508 wp->w_dotp = bp->b_dotp; 509 wp->w_doto = bp->b_doto; 510 return TRUE; 511 } 512 /* First, dettach the old buffer from the window */ 513 if ((bp->b_altb = obp = wp->w_bufp) != NULL) { 514 if (--obp->b_nwnd == 0) { 515 obp->b_dotp = wp->w_dotp; 516 obp->b_doto = wp->w_doto; 517 obp->b_markp = wp->w_markp; 518 obp->b_marko = wp->w_marko; 519 } 520 } 521 /* Now, attach the new buffer to the window */ 522 wp->w_bufp = bp; 523 524 if (bp->b_nwnd++ == 0) { /* First use. */ 525 wp->w_dotp = bp->b_dotp; 526 wp->w_doto = bp->b_doto; 527 wp->w_markp = bp->b_markp; 528 wp->w_marko = bp->b_marko; 529 } else 530 /* already on screen, steal values from other window */ 531 for (owp = wheadp; owp != NULL; owp = wp->w_wndp) 532 if (wp->w_bufp == bp && owp != wp) { 533 wp->w_dotp = owp->w_dotp; 534 wp->w_doto = owp->w_doto; 535 wp->w_markp = owp->w_markp; 536 wp->w_marko = owp->w_marko; 537 break; 538 } 539 wp->w_flag |= WFMODE | flags; 540 return TRUE; 541 } 542 543 /* 544 * Pop the buffer we got passed onto the screen. 545 * Returns a status. 546 */ 547 MGWIN * 548 popbuf(BUFFER *bp) 549 { 550 MGWIN *wp; 551 552 if (bp->b_nwnd == 0) { /* Not on screen yet. */ 553 if ((wp = wpopup()) == NULL) 554 return NULL; 555 } else 556 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 557 if (wp->w_bufp == bp) { 558 wp->w_flag |= WFHARD | WFFORCE; 559 return wp; 560 } 561 if (showbuffer(bp, wp, WFHARD) != TRUE) 562 return NULL; 563 return wp; 564 } 565 566 /* 567 * Insert another buffer at dot. Very useful. 568 */ 569 /* ARGSUSED */ 570 int 571 bufferinsert(int f, int n) 572 { 573 BUFFER *bp; 574 LINE *clp; 575 int clo; 576 int nline; 577 int s; 578 char bufn[NBUFN]; 579 580 /* Get buffer to use from user */ 581 if (curbp->b_altb != NULL) 582 s = eread("Insert buffer: (default %s) ", bufn, NBUFN, 583 EFNEW | EFBUF, &(curbp->b_altb->b_bname), NULL); 584 else 585 s = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF, NULL); 586 if (s == ABORT) 587 return (s); 588 if (s == FALSE && curbp->b_altb != NULL) 589 bp = curbp->b_altb; 590 else if ((bp = bfind(bufn, FALSE)) == NULL) 591 return FALSE; 592 593 if (bp == curbp) { 594 ewprintf("Cannot insert buffer into self"); 595 return FALSE; 596 } 597 /* insert the buffer */ 598 nline = 0; 599 clp = lforw(bp->b_linep); 600 for (;;) { 601 for (clo = 0; clo < llength(clp); clo++) 602 if (linsert(1, lgetc(clp, clo)) == FALSE) 603 return FALSE; 604 if ((clp = lforw(clp)) == bp->b_linep) 605 break; 606 if (newline(FFRAND, 1) == FALSE) /* fake newline */ 607 return FALSE; 608 nline++; 609 } 610 if (nline == 1) 611 ewprintf("[Inserted 1 line]"); 612 else 613 ewprintf("[Inserted %d lines]", nline); 614 615 clp = curwp->w_linep; /* cosmetic adjustment */ 616 if (curwp->w_dotp == clp) { /* for offscreen insert */ 617 while (nline-- && lback(clp) != curbp->b_linep) 618 clp = lback(clp); 619 curwp->w_linep = clp; /* adjust framing. */ 620 curwp->w_flag |= WFHARD; 621 } 622 return (TRUE); 623 } 624 625 /* 626 * Turn off the dirty bit on this buffer. 627 */ 628 /* ARGSUSED */ 629 int 630 notmodified(int f, int n) 631 { 632 MGWIN *wp; 633 634 curbp->b_flag &= ~BFCHG; 635 wp = wheadp; /* Update mode lines. */ 636 while (wp != NULL) { 637 if (wp->w_bufp == curbp) 638 wp->w_flag |= WFMODE; 639 wp = wp->w_wndp; 640 } 641 ewprintf("Modification-flag cleared"); 642 return TRUE; 643 } 644 645 #ifndef NO_HELP 646 /* 647 * Popbuf and set all windows to top of buffer. Currently only used by 648 * help functions. 649 */ 650 int 651 popbuftop(BUFFER *bp) 652 { 653 MGWIN *wp; 654 655 bp->b_dotp = lforw(bp->b_linep); 656 bp->b_doto = 0; 657 if (bp->b_nwnd != 0) { 658 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 659 if (wp->w_bufp == bp) { 660 wp->w_dotp = bp->b_dotp; 661 wp->w_doto = 0; 662 wp->w_flag |= WFHARD; 663 } 664 } 665 return popbuf(bp) != NULL; 666 } 667 #endif 668