xref: /freebsd/contrib/nvi/common/delete.c (revision 02e9120893770924227138ba49df1edb3896112a)
1 /*-
2  * Copyright (c) 1992, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5  *	Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9 
10 #include "config.h"
11 
12 #include <sys/types.h>
13 #include <sys/queue.h>
14 #include <sys/time.h>
15 
16 #include <bitstring.h>
17 #include <errno.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include "common.h"
24 
25 /*
26  * del --
27  *	Delete a range of text.
28  *
29  * PUBLIC: int del(SCR *, MARK *, MARK *, int);
30  */
31 int
32 del(SCR *sp, MARK *fm, MARK *tm, int lmode)
33 {
34 	recno_t lno;
35 	size_t blen, len, nlen, tlen;
36 	CHAR_T *bp, *p;
37 	int eof, rval;
38 
39 	bp = NULL;
40 
41 	/* Case 1 -- delete in line mode. */
42 	if (lmode) {
43 		for (lno = tm->lno; lno >= fm->lno; --lno) {
44 			if (db_delete(sp, lno))
45 				return (1);
46 			++sp->rptlines[L_DELETED];
47 			if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
48 				break;
49 		}
50 		goto done;
51 	}
52 
53 	/*
54 	 * Case 2 -- delete to EOF.  This is a special case because it's
55 	 * easier to pick it off than try and find it in the other cases.
56  	 */
57 	if (db_last(sp, &lno))
58 		return (1);
59 	if (tm->lno >= lno) {
60 		if (tm->lno == lno) {
61 			if (db_get(sp, lno, DBG_FATAL, &p, &len))
62 				return (1);
63 			eof = tm->cno != ENTIRE_LINE && tm->cno >= len ? 1 : 0;
64 		} else
65 			eof = 1;
66 		if (eof) {
67 			for (lno = tm->lno; lno > fm->lno; --lno) {
68 				if (db_delete(sp, lno))
69 					return (1);
70 				++sp->rptlines[L_DELETED];
71 				if (lno %
72 				    INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
73 					break;
74 			}
75 			if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
76 				return (1);
77 			GET_SPACE_RETW(sp, bp, blen, fm->cno);
78 			MEMCPY(bp, p, fm->cno);
79 			if (db_set(sp, fm->lno, bp, fm->cno))
80 				return (1);
81 			goto done;
82 		}
83 	}
84 
85 	/* Case 3 -- delete within a single line. */
86 	if (tm->lno == fm->lno) {
87 		if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
88 			return (1);
89 		if (len != 0) {
90 			GET_SPACE_RETW(sp, bp, blen, len);
91 			if (fm->cno != 0)
92 				MEMCPY(bp, p, fm->cno);
93 			MEMCPY(bp + fm->cno, p + (tm->cno + 1),
94 			    len - (tm->cno + 1));
95 			if (db_set(sp, fm->lno,
96 			    bp, len - ((tm->cno - fm->cno) + 1)))
97 				goto err;
98 		}
99 		goto done;
100 	}
101 
102 	/*
103 	 * Case 4 -- delete over multiple lines.
104 	 *
105 	 * Copy the start partial line into place.
106 	 */
107 	if ((tlen = fm->cno) != 0) {
108 		if (db_get(sp, fm->lno, DBG_FATAL, &p, NULL))
109 			return (1);
110 		GET_SPACE_RETW(sp, bp, blen, tlen + 256);
111 		MEMCPY(bp, p, tlen);
112 	}
113 
114 	/* Copy the end partial line into place. */
115 	if (db_get(sp, tm->lno, DBG_FATAL, &p, &len))
116 		goto err;
117 	if (len != 0 && tm->cno != len - 1) {
118 		/*
119 		 * XXX
120 		 * We can overflow memory here, if the total length is greater
121 		 * than SIZE_T_MAX.  The only portable way I've found to test
122 		 * is depending on the overflow being less than the value.
123 		 */
124 		nlen = (len - (tm->cno + 1)) + tlen;
125 		if (tlen > nlen) {
126 			msgq(sp, M_ERR, "002|Line length overflow");
127 			goto err;
128 		}
129 		if (tlen == 0) {
130 			GET_SPACE_RETW(sp, bp, blen, nlen);
131 		} else
132 			ADD_SPACE_RETW(sp, bp, blen, nlen);
133 
134 		MEMCPY(bp + tlen, p + (tm->cno + 1), len - (tm->cno + 1));
135 		tlen += len - (tm->cno + 1);
136 	}
137 
138 	/* Set the current line. */
139 	if (db_set(sp, fm->lno, bp, tlen))
140 		goto err;
141 
142 	/* Delete the last and intermediate lines. */
143 	for (lno = tm->lno; lno > fm->lno; --lno) {
144 		if (db_delete(sp, lno))
145 			goto err;
146 		++sp->rptlines[L_DELETED];
147 		if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
148 			break;
149 	}
150 
151 done:	rval = 0;
152 	if (0)
153 err:		rval = 1;
154 	if (bp != NULL)
155 		FREE_SPACEW(sp, bp, blen);
156 	return (rval);
157 }
158