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
setcasereplace(int f,int n)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 *
lalloc(int used)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
lrealloc(struct line * lp,int newsize)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
lfree(struct line * lp)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
lchange(int flag)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
linsert(int n,int c)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
lnewline_at(struct line * lp1,int doto)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
lnewline(void)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
ldelete(RSIZE n,int kflag)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
ldelnewline(void)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
lreplace(RSIZE plen,char * st)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 *
linetostr(const struct line * ln)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