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