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