1 /* $OpenBSD: buffer.c,v 1.17 2001/11/25 07:34:17 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 snprintf(prompt, sizeof prompt, "Save file %s", 307 bp->b_fname); 308 if ((f == TRUE || (save = eyorn(prompt)) == TRUE) 309 && buffsave(bp) == TRUE) { 310 bp->b_flag &= ~BFCHG; 311 upmodes(bp); 312 } else 313 s = TRUE; 314 if (save == ABORT) 315 return (save); 316 save = TRUE; 317 } 318 } 319 if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */ 320 ewprintf("(No files need saving)"); 321 return s; 322 } 323 324 /* 325 * Search for a buffer, by name. 326 * If not found, and the "cflag" is TRUE, 327 * create a buffer and put it in the list of 328 * all buffers. Return pointer to the BUFFER 329 * block for the buffer. 330 */ 331 BUFFER * 332 bfind(bname, cflag) 333 char *bname; 334 int cflag; 335 { 336 BUFFER *bp; 337 LINE *lp; 338 int i; 339 340 bp = bheadp; 341 while (bp != NULL) { 342 if (strcmp(bname, bp->b_bname) == 0) 343 return bp; 344 bp = bp->b_bufp; 345 } 346 if (cflag != TRUE) 347 return NULL; 348 /* NOSTRICT */ 349 if ((bp = (BUFFER *) malloc(sizeof(BUFFER))) == NULL) { 350 ewprintf("Can't get %d bytes", sizeof(BUFFER)); 351 return NULL; 352 } 353 if ((bp->b_bname = malloc((strlen(bname) + 1))) == NULL) { 354 ewprintf("Can't get %d bytes", strlen(bname) + 1); 355 free((char *) bp); 356 return NULL; 357 } 358 if ((lp = lalloc(0)) == NULL) { 359 free(bp->b_bname); 360 free((char *) bp); 361 return NULL; 362 } 363 bp->b_altb = bp->b_bufp = NULL; 364 bp->b_dotp = lp; 365 bp->b_doto = 0; 366 bp->b_markp = NULL; 367 bp->b_marko = 0; 368 bp->b_flag = defb_flag; 369 bp->b_nwnd = 0; 370 bp->b_linep = lp; 371 bp->b_nmodes = defb_nmodes; 372 i = 0; 373 do { 374 bp->b_modes[i] = defb_modes[i]; 375 } while (i++ < defb_nmodes); 376 bp->b_fname[0] = '\0'; 377 bzero(&bp->b_fi, sizeof(bp->b_fi)); 378 (void) strcpy(bp->b_bname, bname); 379 lp->l_fp = lp; 380 lp->l_bp = lp; 381 bp->b_bufp = bheadp; 382 bheadp = bp; 383 return bp; 384 } 385 386 /* 387 * This routine blows away all of the text 388 * in a buffer. If the buffer is marked as changed 389 * then we ask if it is ok to blow it away; this is 390 * to save the user the grief of losing text. The 391 * window chain is nearly always wrong if this gets 392 * called; the caller must arrange for the updates 393 * that are required. Return TRUE if everything 394 * looks good. 395 */ 396 int 397 bclear(bp) 398 BUFFER *bp; 399 { 400 LINE *lp; 401 int s; 402 403 if ((bp->b_flag & BFCHG) != 0 /* Changed. */ 404 && (s = eyesno("Buffer modified; kill anyway")) != TRUE) 405 return (s); 406 bp->b_flag &= ~BFCHG; /* Not changed */ 407 while ((lp = lforw(bp->b_linep)) != bp->b_linep) 408 lfree(lp); 409 bp->b_dotp = bp->b_linep; /* Fix "." */ 410 bp->b_doto = 0; 411 bp->b_markp = NULL; /* Invalidate "mark" */ 412 bp->b_marko = 0; 413 return TRUE; 414 } 415 416 /* 417 * Display the given buffer in the given window. Flags indicated 418 * action on redisplay. 419 */ 420 int 421 showbuffer(bp, wp, flags) 422 BUFFER *bp; 423 MGWIN *wp; 424 int flags; 425 { 426 BUFFER *obp; 427 MGWIN *owp; 428 429 if (wp->w_bufp == bp) { /* Easy case! */ 430 wp->w_flag |= flags; 431 return TRUE; 432 } 433 /* First, dettach the old buffer from the window */ 434 if ((bp->b_altb = obp = wp->w_bufp) != NULL) { 435 if (--obp->b_nwnd == 0) { 436 obp->b_dotp = wp->w_dotp; 437 obp->b_doto = wp->w_doto; 438 obp->b_markp = wp->w_markp; 439 obp->b_marko = wp->w_marko; 440 } 441 } 442 /* Now, attach the new buffer to the window */ 443 wp->w_bufp = bp; 444 445 if (bp->b_nwnd++ == 0) { /* First use. */ 446 wp->w_dotp = bp->b_dotp; 447 wp->w_doto = bp->b_doto; 448 wp->w_markp = bp->b_markp; 449 wp->w_marko = bp->b_marko; 450 } else 451 /* already on screen, steal values from other window */ 452 for (owp = wheadp; owp != NULL; owp = wp->w_wndp) 453 if (wp->w_bufp == bp && owp != wp) { 454 wp->w_dotp = owp->w_dotp; 455 wp->w_doto = owp->w_doto; 456 wp->w_markp = owp->w_markp; 457 wp->w_marko = owp->w_marko; 458 break; 459 } 460 wp->w_flag |= WFMODE | flags; 461 return TRUE; 462 } 463 464 /* 465 * Pop the buffer we got passed onto the screen. 466 * Returns a status. 467 */ 468 MGWIN * 469 popbuf(bp) 470 BUFFER *bp; 471 { 472 MGWIN *wp; 473 474 if (bp->b_nwnd == 0) { /* Not on screen yet. */ 475 if ((wp = wpopup()) == NULL) 476 return NULL; 477 } else 478 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 479 if (wp->w_bufp == bp) { 480 wp->w_flag |= WFHARD | WFFORCE; 481 return wp; 482 } 483 if (showbuffer(bp, wp, WFHARD) != TRUE) 484 return NULL; 485 return wp; 486 } 487 488 /* 489 * Insert another buffer at dot. Very useful. 490 */ 491 /* ARGSUSED */ 492 int 493 bufferinsert(f, n) 494 { 495 BUFFER *bp; 496 LINE *clp; 497 int clo; 498 int nline; 499 int s; 500 char bufn[NBUFN]; 501 502 /* Get buffer to use from user */ 503 if (curbp->b_altb != NULL) 504 s = eread("Insert buffer: (default %s) ", bufn, NBUFN, 505 EFNEW | EFBUF, &(curbp->b_altb->b_bname), NULL); 506 else 507 s = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF, NULL); 508 if (s == ABORT) 509 return (s); 510 if (s == FALSE && curbp->b_altb != NULL) 511 bp = curbp->b_altb; 512 else if ((bp = bfind(bufn, FALSE)) == NULL) 513 return FALSE; 514 515 if (bp == curbp) { 516 ewprintf("Cannot insert buffer into self"); 517 return FALSE; 518 } 519 /* insert the buffer */ 520 nline = 0; 521 clp = lforw(bp->b_linep); 522 for (;;) { 523 for (clo = 0; clo < llength(clp); clo++) 524 if (linsert(1, lgetc(clp, clo)) == FALSE) 525 return FALSE; 526 if ((clp = lforw(clp)) == bp->b_linep) 527 break; 528 if (newline(FFRAND, 1) == FALSE) /* fake newline */ 529 return FALSE; 530 nline++; 531 } 532 if (nline == 1) 533 ewprintf("[Inserted 1 line]"); 534 else 535 ewprintf("[Inserted %d lines]", nline); 536 537 clp = curwp->w_linep; /* cosmetic adjustment */ 538 if (curwp->w_dotp == clp) { /* for offscreen insert */ 539 while (nline-- && lback(clp) != curbp->b_linep) 540 clp = lback(clp); 541 curwp->w_linep = clp; /* adjust framing. */ 542 curwp->w_flag |= WFHARD; 543 } 544 return (TRUE); 545 } 546 547 /* 548 * Turn off the dirty bit on this buffer. 549 */ 550 /* ARGSUSED */ 551 int 552 notmodified(f, n) 553 { 554 MGWIN *wp; 555 556 curbp->b_flag &= ~BFCHG; 557 wp = wheadp; /* Update mode lines. */ 558 while (wp != NULL) { 559 if (wp->w_bufp == curbp) 560 wp->w_flag |= WFMODE; 561 wp = wp->w_wndp; 562 } 563 ewprintf("Modification-flag cleared"); 564 return TRUE; 565 } 566 567 #ifndef NO_HELP 568 /* 569 * Popbuf and set all windows to top of buffer. Currently only used by 570 * help functions. 571 */ 572 int 573 popbuftop(bp) 574 BUFFER *bp; 575 { 576 MGWIN *wp; 577 578 bp->b_dotp = lforw(bp->b_linep); 579 bp->b_doto = 0; 580 if (bp->b_nwnd != 0) { 581 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 582 if (wp->w_bufp == bp) { 583 wp->w_dotp = bp->b_dotp; 584 wp->w_doto = 0; 585 wp->w_flag |= WFHARD; 586 } 587 } 588 return popbuf(bp) != NULL; 589 } 590 #endif 591