1 /* $OpenBSD: line.c,v 1.8 2001/05/24 03:05:23 mickey Exp $ */ 2 3 /* 4 * Text line handling. 5 * 6 * The functions in this file are a general set of line management 7 * utilities. They are the only routines that touch the text. They 8 * also touch the buffer and window structures to make sure that the 9 * necessary updating gets done. There are routines in this file that 10 * handle the kill buffer too. It isn't here for any good reason. 11 * 12 * Note that this code only updates the dot and mark values in the window 13 * list. Since all the code acts on the current window, the buffer that 14 * we are editing must be displayed, which means that "b_nwnd" is non-zero, 15 * which means that the dot and mark values in the buffer headers are 16 * nonsense. 17 */ 18 19 #include "def.h" 20 21 /* 22 * The number of bytes member from the start of the structure type should be 23 * computed at compile time. 24 */ 25 26 #ifndef OFFSET 27 #define OFFSET(type,member) ((char *)&(((type *)0)->member)-(char *)((type *)0)) 28 #endif 29 30 #ifndef NBLOCK 31 #define NBLOCK 16 /* Line block chunk size */ 32 #endif 33 34 #ifndef KBLOCK 35 #define KBLOCK 256 /* Kill buffer block size. */ 36 #endif 37 38 static char *kbufp = NULL; /* Kill buffer data. */ 39 static RSIZE kused = 0; /* # of bytes used in KB. */ 40 static RSIZE ksize = 0; /* # of bytes allocated in KB. */ 41 static RSIZE kstart = 0; /* # of first used byte in KB. */ 42 43 static int kgrow __P((int)); 44 45 /* 46 * This routine allocates a block of memory large enough to hold a LINE 47 * containing "used" characters. The block is rounded up to whatever 48 * needs to be allocated. (use lallocx for lines likely to grow.) 49 * Return a pointer to the new block, or NULL if there isn't any memory 50 * left. Print a message in the message line if no space. 51 */ 52 LINE * 53 lalloc(used) 54 int used; 55 { 56 LINE *lp; 57 int size; 58 59 /* any padding at the end of the structure is used */ 60 if ((size = used + OFFSET(LINE, l_text[0])) < sizeof(LINE)) 61 size = sizeof(LINE); 62 #ifdef MALLOCROUND 63 MALLOCROUND(size); /* round up to a size optimal to malloc */ 64 #endif 65 if ((lp = malloc((unsigned)size)) == NULL) { 66 ewprintf("Can't get %d bytes", size); 67 return NULL; 68 } 69 lp->l_size = size - OFFSET(LINE, l_text[0]); 70 lp->l_used = used; 71 return lp; 72 } 73 74 /* 75 * Like lalloc, only round amount desired up because this line will 76 * probably grow. We always make room for at least one more char. 77 * (thus making 0 not a special case anymore.) 78 */ 79 LINE * 80 lallocx(used) 81 int used; 82 { 83 int size; 84 LINE *lp; 85 86 size = (NBLOCK + used) & ~(NBLOCK - 1); 87 if ((lp = lalloc(size)) != NULL) 88 lp->l_used = used; 89 return lp; 90 } 91 92 /* 93 * Delete line "lp". Fix all of the links that might point to it (they are 94 * moved to offset 0 of the next line. Unlink the line from whatever buffer 95 * it might be in, and release the memory. The buffers are updated too; the 96 * magic conditions described in the above comments don't hold here. 97 */ 98 void 99 lfree(lp) 100 LINE *lp; 101 { 102 BUFFER *bp; 103 MGWIN *wp; 104 105 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 106 if (wp->w_linep == lp) 107 wp->w_linep = lp->l_fp; 108 if (wp->w_dotp == lp) { 109 wp->w_dotp = lp->l_fp; 110 wp->w_doto = 0; 111 } 112 if (wp->w_markp == lp) { 113 wp->w_markp = lp->l_fp; 114 wp->w_marko = 0; 115 } 116 } 117 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 118 if (bp->b_nwnd == 0) { 119 if (bp->b_dotp == lp) { 120 bp->b_dotp = lp->l_fp; 121 bp->b_doto = 0; 122 } 123 if (bp->b_markp == lp) { 124 bp->b_markp = lp->l_fp; 125 bp->b_marko = 0; 126 } 127 } 128 } 129 lp->l_bp->l_fp = lp->l_fp; 130 lp->l_fp->l_bp = lp->l_bp; 131 free((char *)lp); 132 } 133 134 /* 135 * This routine is called when a character changes in place in the current 136 * buffer. It updates all of the required flags in the buffer and window 137 * system. The flag used is passed as an argument; if the buffer is being 138 * displayed in more than 1 window we change EDIT to HARD. Set MODE if the 139 * mode line needs to be updated (the "*" has to be set). 140 */ 141 void 142 lchange(flag) 143 int flag; 144 { 145 MGWIN *wp; 146 147 /* update mode lines if this is the first change. */ 148 if ((curbp->b_flag & BFCHG) == 0) { 149 flag |= WFMODE; 150 curbp->b_flag |= BFCHG; 151 } 152 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 153 if (wp->w_bufp == curbp) { 154 wp->w_flag |= flag; 155 if (wp != curwp) 156 wp->w_flag |= WFHARD; 157 } 158 } 159 } 160 161 /* 162 * Insert "n" copies of the character "c" at the current location of dot. 163 * In the easy case all that happens is the text is stored in the line. 164 * In the hard case, the line has to be reallocated. When the window list 165 * is updated, take special care; I screwed it up once. You always update 166 * dot in the current window. You update mark and a dot in another window 167 * if it is greater than the place where you did the insert. Return TRUE 168 * if all is well, and FALSE on errors. 169 */ 170 int 171 linsert(n, c) 172 int n, c; 173 { 174 LINE *lp1, *lp2, *lp3; 175 MGWIN *wp; 176 RSIZE i; 177 int doto; 178 char *cp1, *cp2; 179 180 lchange(WFEDIT); 181 182 /* current line */ 183 lp1 = curwp->w_dotp; 184 185 /* special case for the end */ 186 if (lp1 == curbp->b_linep) { 187 /* now should only happen in empty buffer */ 188 if (curwp->w_doto != 0) { 189 ewprintf("bug: linsert"); 190 return FALSE; 191 } 192 /* allocate a new line */ 193 if ((lp2 = lallocx(n)) == NULL) 194 return FALSE; 195 /* previous line */ 196 lp3 = lp1->l_bp; 197 /* link in */ 198 lp3->l_fp = lp2; 199 lp2->l_fp = lp1; 200 lp1->l_bp = lp2; 201 lp2->l_bp = lp3; 202 for (i = 0; i < n; ++i) 203 lp2->l_text[i] = c; 204 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 205 if (wp->w_linep == lp1) 206 wp->w_linep = lp2; 207 if (wp->w_dotp == lp1) 208 wp->w_dotp = lp2; 209 if (wp->w_markp == lp1) 210 wp->w_markp = lp2; 211 } 212 /* NOSTRICT */ 213 curwp->w_doto = n; 214 return TRUE; 215 } 216 /* save for later */ 217 doto = curwp->w_doto; 218 /* NOSTRICT (2) */ 219 /* Hard case: reallocate */ 220 if (lp1->l_used + n > lp1->l_size) { 221 if ((lp2 = lallocx(lp1->l_used + n)) == NULL) 222 return FALSE; 223 cp1 = &lp1->l_text[0]; 224 cp2 = &lp2->l_text[0]; 225 while (cp1 != &lp1->l_text[doto]) 226 *cp2++ = *cp1++; 227 /* NOSTRICT */ 228 cp2 += n; 229 while (cp1 != &lp1->l_text[lp1->l_used]) 230 *cp2++ = *cp1++; 231 lp1->l_bp->l_fp = lp2; 232 lp2->l_fp = lp1->l_fp; 233 lp1->l_fp->l_bp = lp2; 234 lp2->l_bp = lp1->l_bp; 235 free((char *)lp1); 236 /* Easy case: in place */ 237 } else { 238 /* pretend there's a new line */ 239 lp2 = lp1; 240 /* NOSTRICT */ 241 lp2->l_used += n; 242 cp2 = &lp1->l_text[lp1->l_used]; 243 244 cp1 = cp2 - n; 245 while (cp1 != &lp1->l_text[doto]) 246 *--cp2 = *--cp1; 247 } 248 /* Add the characters */ 249 for (i = 0; i < n; ++i) 250 lp2->l_text[doto + i] = c; 251 252 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 253 if (wp->w_linep == lp1) 254 wp->w_linep = lp2; 255 if (wp->w_dotp == lp1) { 256 wp->w_dotp = lp2; 257 if (wp == curwp || wp->w_doto > doto) 258 /* NOSTRICT */ 259 wp->w_doto += n; 260 } 261 if (wp->w_markp == lp1) { 262 wp->w_markp = lp2; 263 if (wp->w_marko > doto) 264 /* NOSTRICT */ 265 wp->w_marko += n; 266 } 267 } 268 return TRUE; 269 } 270 271 /* 272 * Insert a newline into the buffer at the current location of dot in the 273 * current window. The funny ass-backwards way is no longer used. 274 */ 275 int 276 lnewline() 277 { 278 LINE *lp1, *lp2; 279 int doto, nlen; 280 MGWIN *wp; 281 282 lchange(WFHARD); 283 284 /* Get the address and offset of "." */ 285 lp1 = curwp->w_dotp; 286 doto = curwp->w_doto; 287 288 /* avoid unnecessary copying */ 289 if (doto == 0) { 290 /* new first part */ 291 if ((lp2 = lallocx(0)) == NULL) 292 return FALSE; 293 lp2->l_bp = lp1->l_bp; 294 lp1->l_bp->l_fp = lp2; 295 lp2->l_fp = lp1; 296 lp1->l_bp = lp2; 297 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) 298 if (wp->w_linep == lp1) 299 wp->w_linep = lp2; 300 return TRUE; 301 } 302 303 /* length of new part */ 304 nlen = llength(lp1) - doto; 305 306 /* new second half line */ 307 if ((lp2 = lallocx(nlen)) == NULL) 308 return FALSE; 309 if (nlen != 0) 310 bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen); 311 lp1->l_used = doto; 312 lp2->l_bp = lp1; 313 lp2->l_fp = lp1->l_fp; 314 lp1->l_fp = lp2; 315 lp2->l_fp->l_bp = lp2; 316 /* Windows */ 317 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 318 if (wp->w_dotp == lp1 && wp->w_doto >= doto) { 319 wp->w_dotp = lp2; 320 wp->w_doto -= doto; 321 } 322 if (wp->w_markp == lp1 && wp->w_marko >= doto) { 323 wp->w_markp = lp2; 324 wp->w_marko -= doto; 325 } 326 } 327 return TRUE; 328 } 329 330 /* 331 * This function deletes "n" bytes, starting at dot. It understands how to 332 * deal with end of lines, etc. It returns TRUE if all of the characters 333 * were deleted, and FALSE if they were not (because dot ran into the end 334 * of the buffer. The "kflag" indicates either no insertion, or direction 335 * of insertion into the kill buffer. 336 */ 337 int 338 ldelete(n, kflag) 339 RSIZE n; 340 int kflag; 341 { 342 LINE *dotp; 343 RSIZE chunk; 344 MGWIN *wp; 345 int doto; 346 char *cp1, *cp2; 347 348 /* 349 * HACK - doesn't matter, and fixes back-over-nl bug for empty 350 * kill buffers. 351 */ 352 if (kused == kstart) 353 kflag = KFORW; 354 355 while (n != 0) { 356 dotp = curwp->w_dotp; 357 doto = curwp->w_doto; 358 /* Hit the end of the buffer */ 359 if (dotp == curbp->b_linep) 360 return FALSE; 361 /* Size of the chunk */ 362 chunk = dotp->l_used - doto; 363 if (chunk > n) 364 chunk = n; 365 /* End of line, merge */ 366 if (chunk == 0) { 367 if (dotp == lback(curbp->b_linep)) 368 /* End of buffer */ 369 return FALSE; 370 lchange(WFHARD); 371 if (ldelnewline() == FALSE || 372 (kflag != KNONE && kinsert('\n', kflag) == FALSE)) 373 return FALSE; 374 --n; 375 continue; 376 } 377 lchange(WFEDIT); 378 /* Scrunch text */ 379 cp1 = &dotp->l_text[doto]; 380 cp2 = cp1 + chunk; 381 if (kflag == KFORW) { 382 while (ksize - kused < chunk) 383 if (kgrow(FALSE) == FALSE) 384 return FALSE; 385 bcopy(cp1, &(kbufp[kused]), (int)chunk); 386 kused += chunk; 387 } else if (kflag == KBACK) { 388 while (kstart < chunk) 389 if (kgrow(TRUE) == FALSE) 390 return FALSE; 391 bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk); 392 kstart -= chunk; 393 } else if (kflag != KNONE) 394 panic("broken ldelete call"); 395 while (cp2 != &dotp->l_text[dotp->l_used]) 396 *cp1++ = *cp2++; 397 dotp->l_used -= (int)chunk; 398 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 399 if (wp->w_dotp == dotp && wp->w_doto >= doto) { 400 /* NOSTRICT */ 401 wp->w_doto -= chunk; 402 if (wp->w_doto < doto) 403 wp->w_doto = doto; 404 } 405 if (wp->w_markp == dotp && wp->w_marko >= doto) { 406 /* NOSTRICT */ 407 wp->w_marko -= chunk; 408 if (wp->w_marko < doto) 409 wp->w_marko = doto; 410 } 411 } 412 n -= chunk; 413 } 414 return TRUE; 415 } 416 417 /* 418 * Delete a newline and join the current line with the next line. If the next 419 * line is the magic header line always return TRUE; merging the last line 420 * with the header line can be thought of as always being a successful 421 * operation. Even if nothing is done, this makes the kill buffer work 422 * "right". Easy cases can be done by shuffling data around. Hard cases 423 * require that lines be moved about in memory. Return FALSE on error and 424 * TRUE if all looks ok. 425 */ 426 int 427 ldelnewline() 428 { 429 LINE *lp1, *lp2, *lp3; 430 MGWIN *wp; 431 432 lp1 = curwp->w_dotp; 433 lp2 = lp1->l_fp; 434 /* at the end of the buffer */ 435 if (lp2 == curbp->b_linep) 436 return TRUE; 437 if (lp2->l_used <= lp1->l_size - lp1->l_used) { 438 bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used); 439 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 440 if (wp->w_linep == lp2) 441 wp->w_linep = lp1; 442 if (wp->w_dotp == lp2) { 443 wp->w_dotp = lp1; 444 wp->w_doto += lp1->l_used; 445 } 446 if (wp->w_markp == lp2) { 447 wp->w_markp = lp1; 448 wp->w_marko += lp1->l_used; 449 } 450 } 451 lp1->l_used += lp2->l_used; 452 lp1->l_fp = lp2->l_fp; 453 lp2->l_fp->l_bp = lp1; 454 free((char *)lp2); 455 return TRUE; 456 } 457 if ((lp3 = lalloc(lp1->l_used + lp2->l_used)) == NULL) 458 return FALSE; 459 bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used); 460 bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used); 461 lp1->l_bp->l_fp = lp3; 462 lp3->l_fp = lp2->l_fp; 463 lp2->l_fp->l_bp = lp3; 464 lp3->l_bp = lp1->l_bp; 465 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 466 if (wp->w_linep == lp1 || wp->w_linep == lp2) 467 wp->w_linep = lp3; 468 if (wp->w_dotp == lp1) 469 wp->w_dotp = lp3; 470 else if (wp->w_dotp == lp2) { 471 wp->w_dotp = lp3; 472 wp->w_doto += lp1->l_used; 473 } 474 if (wp->w_markp == lp1) 475 wp->w_markp = lp3; 476 else if (wp->w_markp == lp2) { 477 wp->w_markp = lp3; 478 wp->w_marko += lp1->l_used; 479 } 480 } 481 free((char *)lp1); 482 free((char *)lp2); 483 return TRUE; 484 } 485 486 /* 487 * Replace plen characters before dot with argument string. Control-J 488 * characters in st are interpreted as newlines. There is a casehack 489 * disable flag (normally it likes to match case of replacement to what 490 * was there). 491 */ 492 int 493 lreplace(plen, st, f) 494 RSIZE plen; /* length to remove */ 495 char *st; /* replacement string */ 496 int f; /* case hack disable */ 497 { 498 RSIZE rlen; /* replacement length */ 499 int rtype; /* capitalization */ 500 int c; /* used for random characters */ 501 int doto; /* offset into line */ 502 503 /* 504 * Find the capitalization of the word that was found. f says use 505 * exact case of replacement string (same thing that happens with 506 * lowercase found), so bypass check. 507 */ 508 /* NOSTRICT */ 509 (void)backchar(FFARG | FFRAND, (int)plen); 510 rtype = _L; 511 c = lgetc(curwp->w_dotp, curwp->w_doto); 512 if (ISUPPER(c) != FALSE && f == FALSE) { 513 rtype = _U | _L; 514 if (curwp->w_doto + 1 < llength(curwp->w_dotp)) { 515 c = lgetc(curwp->w_dotp, curwp->w_doto + 1); 516 if (ISUPPER(c) != FALSE) { 517 rtype = _U; 518 } 519 } 520 } 521 /* 522 * make the string lengths match (either pad the line 523 * so that it will fit, or scrunch out the excess). 524 * be careful with dot's offset. 525 */ 526 rlen = strlen(st); 527 doto = curwp->w_doto; 528 if (plen > rlen) 529 (void)ldelete((RSIZE) (plen - rlen), KNONE); 530 else if (plen < rlen) { 531 if (linsert((int)(rlen - plen), ' ') == FALSE) 532 return FALSE; 533 } 534 curwp->w_doto = doto; 535 536 /* 537 * do the replacement: If was capital, then place first 538 * char as if upper, and subsequent chars as if lower. 539 * If inserting upper, check replacement for case. 540 */ 541 while ((c = CHARMASK(*st++)) != '\0') { 542 if ((rtype & _U) != 0 && ISLOWER(c) != 0) 543 c = TOUPPER(c); 544 if (rtype == (_U | _L)) 545 rtype = _L; 546 if (c == CCHR('J')) { 547 if (curwp->w_doto == llength(curwp->w_dotp)) 548 (void)forwchar(FFRAND, 1); 549 else { 550 if (ldelete((RSIZE) 1, KNONE) != FALSE) 551 (void)lnewline(); 552 } 553 } else if (curwp->w_dotp == curbp->b_linep) { 554 (void)linsert(1, c); 555 } else if (curwp->w_doto == llength(curwp->w_dotp)) { 556 if (ldelete((RSIZE) 1, KNONE) != FALSE) 557 (void)linsert(1, c); 558 } else 559 lputc(curwp->w_dotp, curwp->w_doto++, c); 560 } 561 lchange(WFHARD); 562 return (TRUE); 563 } 564 565 /* 566 * Delete all of the text saved in the kill buffer. Called by commands when 567 * a new kill context is created. The kill buffer array is released, just in 568 * case the buffer has grown to an immense size. No errors. 569 */ 570 void 571 kdelete() 572 { 573 if (kbufp != NULL) { 574 free((char *)kbufp); 575 kbufp = NULL; 576 kstart = kused = ksize = 0; 577 } 578 } 579 580 /* 581 * Insert a character to the kill buffer, enlarging the buffer if there 582 * isn't any room. Always grow the buffer in chunks, on the assumption 583 * that if you put something in the kill buffer you are going to put more 584 * stuff there too later. Return TRUE if all is well, and FALSE on errors. 585 * Print a message on errors. Dir says whether to put it at back or front. 586 */ 587 int 588 kinsert(c, dir) 589 int c, dir; 590 { 591 if (kused == ksize && dir == KFORW && kgrow(FALSE) == FALSE) 592 return FALSE; 593 if (kstart == 0 && dir == KBACK && kgrow(TRUE) == FALSE) 594 return FALSE; 595 if (dir == KFORW) 596 kbufp[kused++] = c; 597 else if (dir == KBACK) 598 kbufp[--kstart] = c; 599 else 600 panic("broken kinsert call"); /* Oh shit! */ 601 return (TRUE); 602 } 603 604 /* 605 * kgrow - just get more kill buffer for the callee. back is true if 606 * we are trying to get space at the beginning of the kill buffer. 607 */ 608 static int 609 kgrow(back) 610 int back; 611 { 612 int nstart; 613 char *nbufp; 614 615 if ((unsigned)(ksize + KBLOCK) <= (unsigned)ksize) { 616 /* probably 16 bit unsigned */ 617 ewprintf("Kill buffer size at maximum"); 618 return FALSE; 619 } 620 if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) { 621 ewprintf("Can't get %ld bytes", (long)(ksize + KBLOCK)); 622 return FALSE; 623 } 624 nstart = (back == TRUE) ? (kstart + KBLOCK) : (KBLOCK / 4); 625 bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int)(kused - kstart)); 626 if (kbufp != NULL) 627 free((char *)kbufp); 628 kbufp = nbufp; 629 ksize += KBLOCK; 630 kused = kused - kstart + nstart; 631 kstart = nstart; 632 return TRUE; 633 } 634 635 /* 636 * This function gets characters from the kill buffer. If the character 637 * index "n" is off the end, it returns "-1". This lets the caller just 638 * scan along until it gets a "-1" back. 639 */ 640 int 641 kremove(n) 642 int n; 643 { 644 if (n < 0 || n + kstart >= kused) 645 return -1; 646 return CHARMASK(kbufp[n + kstart]); 647 } 648