1 /* $OpenBSD: line.c,v 1.63 2021/03/01 10:51:14 lum Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Text line handling. 7 * 8 * The functions in this file are a general set of line management 9 * utilities. They are the only routines that touch the text. They 10 * also touch the buffer and window structures to make sure that the 11 * necessary updating gets done. 12 * 13 * Note that this code only updates the dot and mark values in the window 14 * list. Since all the code acts on the current window, the buffer that 15 * we are editing must be displayed, which means that "b_nwnd" is non-zero, 16 * which means that the dot and mark values in the buffer headers are 17 * nonsense. 18 */ 19 20 #include <sys/queue.h> 21 #include <ctype.h> 22 #include <limits.h> 23 #include <signal.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include "def.h" 29 30 int casereplace = TRUE; 31 32 /* 33 * Preserve the case of the replaced string. 34 */ 35 int 36 setcasereplace(int f, int n) 37 { 38 if (f & FFARG) 39 casereplace = n > 0; 40 else 41 casereplace = !casereplace; 42 ewprintf("Case-replace is %sabled", casereplace ? "en" : "dis"); 43 return (TRUE); 44 } 45 46 /* 47 * Allocate a new line of size `used'. lrealloc() can be called if the line 48 * ever needs to grow beyond that. 49 */ 50 struct line * 51 lalloc(int used) 52 { 53 struct line *lp; 54 55 if ((lp = malloc(sizeof(*lp))) == NULL) 56 return (NULL); 57 lp->l_text = NULL; 58 lp->l_size = 0; 59 lp->l_used = used; /* XXX */ 60 if (lrealloc(lp, used) == FALSE) { 61 free(lp); 62 return (NULL); 63 } 64 return (lp); 65 } 66 67 int 68 lrealloc(struct line *lp, int newsize) 69 { 70 char *tmp; 71 72 if (lp->l_size < newsize) { 73 if ((tmp = realloc(lp->l_text, newsize)) == NULL) 74 return (FALSE); 75 lp->l_text = tmp; 76 lp->l_size = newsize; 77 } 78 return (TRUE); 79 } 80 81 /* 82 * Delete line "lp". Fix all of the links that might point to it (they are 83 * moved to offset 0 of the next line. Unlink the line from whatever buffer 84 * it might be in, and release the memory. The buffers are updated too; the 85 * magic conditions described in the above comments don't hold here. 86 */ 87 void 88 lfree(struct line *lp) 89 { 90 struct buffer *bp; 91 struct mgwin *wp; 92 93 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 94 if (wp->w_linep == lp) 95 wp->w_linep = lp->l_fp; 96 if (wp->w_dotp == lp) { 97 wp->w_dotp = lp->l_fp; 98 wp->w_doto = 0; 99 } 100 if (wp->w_markp == lp) { 101 wp->w_markp = lp->l_fp; 102 wp->w_marko = 0; 103 } 104 } 105 for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { 106 if (bp->b_nwnd == 0) { 107 if (bp->b_dotp == lp) { 108 bp->b_dotp = lp->l_fp; 109 bp->b_doto = 0; 110 } 111 if (bp->b_markp == lp) { 112 bp->b_markp = lp->l_fp; 113 bp->b_marko = 0; 114 } 115 } 116 } 117 lp->l_bp->l_fp = lp->l_fp; 118 lp->l_fp->l_bp = lp->l_bp; 119 free(lp->l_text); 120 free(lp); 121 } 122 123 /* 124 * This routine is called when a character changes in place in the current 125 * buffer. It updates all of the required flags in the buffer and window 126 * system. The flag used is passed as an argument; if the buffer is being 127 * displayed in more than 1 window we change EDIT to HARD. Set MODE if the 128 * mode line needs to be updated (the "*" has to be set). 129 */ 130 void 131 lchange(int flag) 132 { 133 struct mgwin *wp; 134 135 /* update mode lines if this is the first change. */ 136 if ((curbp->b_flag & BFCHG) == 0) { 137 flag |= WFMODE; 138 curbp->b_flag |= BFCHG; 139 } 140 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 141 if (wp->w_bufp == curbp) { 142 wp->w_rflag |= flag; 143 if (wp != curwp) 144 wp->w_rflag |= WFFULL; 145 } 146 } 147 } 148 149 /* 150 * Insert "n" copies of the character "c" at the current location of dot. 151 * In the easy case all that happens is the text is stored in the line. 152 * In the hard case, the line has to be reallocated. When the window list 153 * is updated, take special care; I screwed it up once. You always update 154 * dot in the current window. You update mark and a dot in another window 155 * if it is greater than the place where you did the insert. Return TRUE 156 * if all is well, and FALSE on errors. 157 */ 158 int 159 linsert(int n, int c) 160 { 161 struct line *lp1; 162 struct mgwin *wp; 163 RSIZE i; 164 int doto; 165 int s; 166 167 if (!n) 168 return (TRUE); 169 170 if ((s = checkdirty(curbp)) != TRUE) 171 return (s); 172 173 if (curbp->b_flag & BFREADONLY) { 174 dobeep(); 175 ewprintf("Buffer is read only"); 176 return (FALSE); 177 } 178 179 lchange(WFEDIT); 180 181 /* current line */ 182 lp1 = curwp->w_dotp; 183 184 /* special case for the end */ 185 if (lp1 == curbp->b_headp) { 186 struct line *lp2, *lp3; 187 188 /* now should only happen in empty buffer */ 189 if (curwp->w_doto != 0) { 190 dobeep(); 191 ewprintf("bug: linsert"); 192 return (FALSE); 193 } 194 /* allocate a new line */ 195 if ((lp2 = lalloc(n)) == NULL) 196 return (FALSE); 197 /* previous line */ 198 lp3 = lp1->l_bp; 199 /* link in */ 200 lp3->l_fp = lp2; 201 lp2->l_fp = lp1; 202 lp1->l_bp = lp2; 203 lp2->l_bp = lp3; 204 for (i = 0; i < n; ++i) 205 lp2->l_text[i] = c; 206 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 207 if (wp->w_linep == lp1) 208 wp->w_linep = lp2; 209 if (wp->w_dotp == lp1) 210 wp->w_dotp = lp2; 211 if (wp->w_markp == lp1) 212 wp->w_markp = lp2; 213 } 214 undo_add_insert(lp2, 0, n); 215 curwp->w_doto = n; 216 return (TRUE); 217 } 218 /* save for later */ 219 doto = curwp->w_doto; 220 221 if ((lp1->l_used + n) > lp1->l_size) { 222 if (lrealloc(lp1, lp1->l_used + n) == FALSE) 223 return (FALSE); 224 } 225 lp1->l_used += n; 226 if (lp1->l_used != n) 227 memmove(&lp1->l_text[doto + n], &lp1->l_text[doto], 228 lp1->l_used - n - doto); 229 230 /* Add the characters */ 231 for (i = 0; i < n; ++i) 232 lp1->l_text[doto + i] = c; 233 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 234 if (wp->w_dotp == lp1) { 235 if (wp == curwp || wp->w_doto > doto) 236 wp->w_doto += n; 237 } 238 if (wp->w_markp == lp1) { 239 if (wp->w_marko > doto) 240 wp->w_marko += n; 241 } 242 } 243 undo_add_insert(curwp->w_dotp, doto, n); 244 return (TRUE); 245 } 246 247 /* 248 * Do the work of inserting a newline at the given line/offset. 249 * If mark is on the current line, we may have to move the markline 250 * to keep line numbers in sync. 251 * lnewline_at assumes the current buffer is writable. Checking for 252 * this fact should be done by the caller. 253 */ 254 int 255 lnewline_at(struct line *lp1, int doto) 256 { 257 struct line *lp2; 258 struct mgwin *wp; 259 int nlen, tcurwpdotline; 260 261 lchange(WFFULL); 262 263 curwp->w_bufp->b_lines++; 264 /* Check if mark is past dot (even on current line) */ 265 if (curwp->w_markline > curwp->w_dotline || 266 (curwp->w_dotline == curwp->w_markline && 267 curwp->w_marko >= doto)) 268 curwp->w_markline++; 269 270 tcurwpdotline = curwp->w_dotline; 271 272 /* If start of line, allocate a new line instead of copying */ 273 if (doto == 0) { 274 /* new first part */ 275 if ((lp2 = lalloc(0)) == NULL) 276 return (FALSE); 277 lp2->l_bp = lp1->l_bp; 278 lp1->l_bp->l_fp = lp2; 279 lp2->l_fp = lp1; 280 lp1->l_bp = lp2; 281 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 282 if (wp->w_linep == lp1) 283 wp->w_linep = lp2; 284 if (wp->w_dotline >= tcurwpdotline && 285 wp->w_bufp == curwp->w_bufp) 286 wp->w_dotline++; 287 } 288 undo_add_boundary(FFRAND, 1); 289 undo_add_insert(lp2, 0, 1); 290 undo_add_boundary(FFRAND, 1); 291 return (TRUE); 292 } 293 294 /* length of new part */ 295 nlen = llength(lp1) - doto; 296 297 /* new second half line */ 298 if ((lp2 = lalloc(nlen)) == NULL) 299 return (FALSE); 300 if (nlen != 0) 301 bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen); 302 lp1->l_used = doto; 303 lp2->l_bp = lp1; 304 lp2->l_fp = lp1->l_fp; 305 lp1->l_fp = lp2; 306 lp2->l_fp->l_bp = lp2; 307 /* Windows */ 308 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 309 if (wp->w_dotp == lp1 && wp->w_doto >= doto) { 310 wp->w_dotp = lp2; 311 wp->w_doto -= doto; 312 wp->w_dotline++; 313 } else if (wp->w_dotline > tcurwpdotline && 314 wp->w_bufp == curwp->w_bufp) 315 wp->w_dotline++; 316 if (wp->w_markp == lp1 && wp->w_marko >= doto) { 317 wp->w_markp = lp2; 318 wp->w_marko -= doto; 319 } 320 } 321 undo_add_boundary(FFRAND, 1); 322 undo_add_insert(lp1, llength(lp1), 1); 323 undo_add_boundary(FFRAND, 1); 324 return (TRUE); 325 } 326 327 /* 328 * Insert a newline into the buffer at the current location of dot in the 329 * current window. 330 */ 331 int 332 lnewline(void) 333 { 334 int s; 335 336 if ((s = checkdirty(curbp)) != TRUE) 337 return (s); 338 if (curbp->b_flag & BFREADONLY) { 339 dobeep(); 340 ewprintf("Buffer is read only"); 341 return (FALSE); 342 } 343 return (lnewline_at(curwp->w_dotp, curwp->w_doto)); 344 } 345 346 /* 347 * This function deletes "n" bytes, starting at dot. (actually, n+1, as the 348 * newline is included) It understands how to deal with end of lines, etc. 349 * It returns TRUE if all of the characters were deleted, and FALSE if 350 * they were not (because dot ran into the end of the buffer). 351 * The "kflag" indicates either no insertion, or direction of insertion 352 * into the kill buffer. 353 */ 354 int 355 ldelete(RSIZE n, int kflag) 356 { 357 struct line *dotp; 358 RSIZE chunk; 359 struct mgwin *wp; 360 int doto; 361 char *cp1, *cp2; 362 size_t len; 363 char *sv = NULL; 364 int end; 365 int s; 366 int rval = FALSE; 367 368 if ((s = checkdirty(curbp)) != TRUE) 369 return (s); 370 if (curbp->b_flag & BFREADONLY) { 371 dobeep(); 372 ewprintf("Buffer is read only"); 373 goto out; 374 } 375 len = n; 376 if ((sv = calloc(1, len + 1)) == NULL) 377 goto out; 378 end = 0; 379 380 undo_add_delete(curwp->w_dotp, curwp->w_doto, n, (kflag & KREG)); 381 382 while (n != 0) { 383 dotp = curwp->w_dotp; 384 doto = curwp->w_doto; 385 /* Hit the end of the buffer */ 386 if (dotp == curbp->b_headp) 387 goto out; 388 /* Size of the chunk */ 389 chunk = dotp->l_used - doto; 390 391 if (chunk > n) 392 chunk = n; 393 /* End of line, merge */ 394 if (chunk == 0) { 395 if (dotp == blastlp(curbp)) 396 goto out; 397 lchange(WFFULL); 398 if (ldelnewline() == FALSE) 399 goto out; 400 end = strlcat(sv, curbp->b_nlchr, len + 1); 401 --n; 402 continue; 403 } 404 lchange(WFEDIT); 405 /* Scrunch text */ 406 cp1 = &dotp->l_text[doto]; 407 memcpy(&sv[end], cp1, chunk); 408 end += chunk; 409 sv[end] = '\0'; 410 for (cp2 = cp1 + chunk; cp2 < &dotp->l_text[dotp->l_used]; 411 cp2++) 412 *cp1++ = *cp2; 413 dotp->l_used -= (int)chunk; 414 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 415 if (wp->w_dotp == dotp && wp->w_doto >= doto) { 416 wp->w_doto -= chunk; 417 if (wp->w_doto < doto) 418 wp->w_doto = doto; 419 } 420 if (wp->w_markp == dotp && wp->w_marko >= doto) { 421 wp->w_marko -= chunk; 422 if (wp->w_marko < doto) 423 wp->w_marko = doto; 424 } 425 } 426 n -= chunk; 427 } 428 if (kchunk(sv, (RSIZE)len, kflag) != TRUE) 429 goto out; 430 rval = TRUE; 431 out: 432 free(sv); 433 return (rval); 434 } 435 436 /* 437 * Delete a newline and join the current line with the next line. If the next 438 * line is the magic header line always return TRUE; merging the last line 439 * with the header line can be thought of as always being a successful 440 * operation. Even if nothing is done, this makes the kill buffer work 441 * "right". If the mark is past the dot (actually, markline > dotline), 442 * decrease the markline accordingly to keep line numbers in sync. 443 * Easy cases can be done by shuffling data around. Hard cases 444 * require that lines be moved about in memory. Return FALSE on error and 445 * TRUE if all looks ok. We do not update w_dotline here, as deletes are done 446 * after moves. 447 */ 448 int 449 ldelnewline(void) 450 { 451 struct line *lp1, *lp2, *lp3; 452 struct mgwin *wp; 453 int s; 454 455 if ((s = checkdirty(curbp)) != TRUE) 456 return (s); 457 if (curbp->b_flag & BFREADONLY) { 458 dobeep(); 459 ewprintf("Buffer is read only"); 460 return (FALSE); 461 } 462 463 lp1 = curwp->w_dotp; 464 lp2 = lp1->l_fp; 465 /* at the end of the buffer */ 466 if (lp2 == curbp->b_headp) 467 return (TRUE); 468 /* Keep line counts in sync */ 469 curwp->w_bufp->b_lines--; 470 if (curwp->w_markline > curwp->w_dotline) 471 curwp->w_markline--; 472 if (lp2->l_used <= lp1->l_size - lp1->l_used) { 473 bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used); 474 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 475 if (wp->w_linep == lp2) 476 wp->w_linep = lp1; 477 if (wp->w_dotp == lp2) { 478 wp->w_dotp = lp1; 479 wp->w_doto += lp1->l_used; 480 } 481 if (wp->w_markp == lp2) { 482 wp->w_markp = lp1; 483 wp->w_marko += lp1->l_used; 484 } 485 } 486 lp1->l_used += lp2->l_used; 487 lp1->l_fp = lp2->l_fp; 488 lp2->l_fp->l_bp = lp1; 489 free(lp2); 490 return (TRUE); 491 } 492 if ((lp3 = lalloc(lp1->l_used + lp2->l_used)) == NULL) 493 return (FALSE); 494 bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used); 495 bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used); 496 lp1->l_bp->l_fp = lp3; 497 lp3->l_fp = lp2->l_fp; 498 lp2->l_fp->l_bp = lp3; 499 lp3->l_bp = lp1->l_bp; 500 for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { 501 if (wp->w_linep == lp1 || wp->w_linep == lp2) 502 wp->w_linep = lp3; 503 if (wp->w_dotp == lp1) 504 wp->w_dotp = lp3; 505 else if (wp->w_dotp == lp2) { 506 wp->w_dotp = lp3; 507 wp->w_doto += lp1->l_used; 508 } 509 if (wp->w_markp == lp1) 510 wp->w_markp = lp3; 511 else if (wp->w_markp == lp2) { 512 wp->w_markp = lp3; 513 wp->w_marko += lp1->l_used; 514 } 515 } 516 free(lp1); 517 free(lp2); 518 return (TRUE); 519 } 520 521 /* 522 * Replace plen characters before dot with argument string. Control-J 523 * characters in st are interpreted as newlines. There is a casehack 524 * disable flag (normally it likes to match case of replacement to what 525 * was there). 526 */ 527 int 528 lreplace(RSIZE plen, char *st) 529 { 530 RSIZE rlen; /* replacement length */ 531 struct line *lp; 532 RSIZE n; 533 int s, doto, is_query_capitalised = 0, is_query_allcaps = 0; 534 int is_replace_alllower = 0; 535 char *repl = NULL; 536 537 if ((s = checkdirty(curbp)) != TRUE) 538 return (s); 539 if (curbp->b_flag & BFREADONLY) { 540 dobeep(); 541 ewprintf("Buffer is read only"); 542 return (FALSE); 543 } 544 545 if ((repl = strdup(st)) == NULL) { 546 dobeep(); 547 ewprintf("out of memory"); 548 return (FALSE); 549 } 550 rlen = strlen(repl); 551 552 undo_boundary_enable(FFRAND, 0); 553 (void)backchar(FFARG | FFRAND, (int)plen); 554 555 if (casereplace != TRUE) 556 goto done; 557 558 lp = curwp->w_dotp; 559 if (ltext(lp) == NULL) 560 goto done; 561 doto = curwp->w_doto; 562 n = plen; 563 564 is_query_capitalised = isupper((unsigned char)lgetc(lp, doto)); 565 566 if (is_query_capitalised) { 567 for (n = 0, is_query_allcaps = 1; n < plen && is_query_allcaps; 568 n++) { 569 is_query_allcaps = !isalpha((unsigned char)lgetc(lp, 570 doto)) || isupper((unsigned char)lgetc(lp, doto)); 571 doto++; 572 if (doto == llength(lp)) { 573 doto = 0; 574 lp = lforw(lp); 575 n++; /* \n is implicit in the buffer */ 576 } 577 } 578 } 579 580 for (n = 0, is_replace_alllower = 1; n < rlen && is_replace_alllower; 581 n++) 582 is_replace_alllower = !isupper((unsigned char)repl[n]); 583 584 if (is_replace_alllower) { 585 if (is_query_allcaps) { 586 for (n = 0; n < rlen; n++) 587 repl[n] = toupper((unsigned char)repl[n]); 588 } else if (is_query_capitalised) { 589 repl[0] = toupper((unsigned char)repl[0]); 590 } 591 } 592 593 done: 594 (void)ldelete(plen, KNONE); 595 region_put_data(repl, rlen); 596 lchange(WFFULL); 597 598 undo_boundary_enable(FFRAND, 1); 599 600 free(repl); 601 return (TRUE); 602 } 603 604 /* 605 * Allocate and return the supplied line as a C string 606 */ 607 char * 608 linetostr(const struct line *ln) 609 { 610 int len; 611 char *line; 612 613 len = llength(ln); 614 if (len == INT_MAX) /* (len + 1) overflow */ 615 return (NULL); 616 617 if ((line = malloc(len + 1)) == NULL) 618 return (NULL); 619 620 (void)memcpy(line, ltext(ln), len); 621 line[len] = '\0'; 622 623 return (line); 624 } 625