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