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