xref: /netbsd-src/external/bsd/nvi/dist/common/cut.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: cut.c,v 1.3 2013/11/25 22:43:46 christos Exp $ */
2 /*-
3  * Copyright (c) 1992, 1993, 1994
4  *	The Regents of the University of California.  All rights reserved.
5  * Copyright (c) 1992, 1993, 1994, 1995, 1996
6  *	Keith Bostic.  All rights reserved.
7  *
8  * See the LICENSE file for redistribution information.
9  */
10 
11 #include "config.h"
12 
13 #ifndef lint
14 static const char sccsid[] = "Id: cut.c,v 10.18 2001/06/25 15:19:09 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:09 ";
15 #endif /* not lint */
16 
17 #include <sys/types.h>
18 #include <sys/queue.h>
19 
20 #include <bitstring.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "common.h"
30 
31 static void	cb_rotate __P((SCR *));
32 
33 /*
34  * cut --
35  *	Put a range of lines/columns into a TEXT buffer.
36  *
37  * There are two buffer areas, both found in the global structure.  The first
38  * is the linked list of all the buffers the user has named, the second is the
39  * unnamed buffer storage.  There is a pointer, too, which is the current
40  * default buffer, i.e. it may point to the unnamed buffer or a named buffer
41  * depending on into what buffer the last text was cut.  Logically, in both
42  * delete and yank operations, if the user names a buffer, the text is cut
43  * into it.  If it's a delete of information on more than a single line, the
44  * contents of the numbered buffers are rotated up one, the contents of the
45  * buffer named '9' are discarded, and the text is cut into the buffer named
46  * '1'.  The text is always cut into the unnamed buffer.
47  *
48  * In all cases, upper-case buffer names are the same as lower-case names,
49  * with the exception that they cause the buffer to be appended to instead
50  * of replaced.  Note, however, that if text is appended to a buffer, the
51  * default buffer only contains the appended text, not the entire contents
52  * of the buffer.
53  *
54  * !!!
55  * The contents of the default buffer would disappear after most operations
56  * in historic vi.  It's unclear that this is useful, so we don't bother.
57  *
58  * When users explicitly cut text into the numeric buffers, historic vi became
59  * genuinely strange.  I've never been able to figure out what was supposed to
60  * happen.  It behaved differently if you deleted text than if you yanked text,
61  * and, in the latter case, the text was appended to the buffer instead of
62  * replacing the contents.  Hopefully it's not worth getting right, and here
63  * we just treat the numeric buffers like any other named buffer.
64  *
65  * PUBLIC: int cut __P((SCR *, ARG_CHAR_T *, MARK *, MARK *, int));
66  */
67 int
68 cut(SCR *sp, ARG_CHAR_T *namep, MARK *fm, MARK *tm, int flags)
69 {
70 	CB *cbp;
71 	ARG_CHAR_T name = '\0';
72 	db_recno_t lno;
73 	int append, copy_one, copy_def;
74 
75 	/*
76 	 * If the user specified a buffer, put it there.  (This may require
77 	 * a copy into the numeric buffers.  We do the copy so that we don't
78 	 * have to reference count and so we don't have to deal with things
79 	 * like appends to buffers that are used multiple times.)
80 	 *
81 	 * Otherwise, if it's supposed to be put in a numeric buffer (usually
82 	 * a delete) put it there.  The rules for putting things in numeric
83 	 * buffers were historically a little strange.  There were three cases.
84 	 *
85 	 *	1: Some motions are always line mode motions, which means
86 	 *	   that the cut always goes into the numeric buffers.
87 	 *	2: Some motions aren't line mode motions, e.g. d10w, but
88 	 *	   can cross line boundaries.  For these commands, if the
89 	 *	   cut crosses a line boundary, it goes into the numeric
90 	 *	   buffers.  This includes most of the commands.
91 	 *	3: Some motions aren't line mode motions, e.g. d`<char>,
92 	 *	   but always go into the numeric buffers, regardless.  This
93 	 *	   was the commands: % ` / ? ( ) N n { } -- and nvi adds ^A.
94 	 *
95 	 * Otherwise, put it in the unnamed buffer.
96 	 */
97 	append = copy_one = copy_def = 0;
98 	if (namep != NULL) {
99 		name = *namep;
100 		if (LF_ISSET(CUT_NUMREQ) || (LF_ISSET(CUT_NUMOPT) &&
101 		    (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno))) {
102 			copy_one = 1;
103 			cb_rotate(sp);
104 		}
105 		if ((append = ISUPPER(name))) {
106 			if (!copy_one)
107 				copy_def = 1;
108 			name = TOLOWER(name);
109 		}
110 namecb:		CBNAME(sp, cbp, name);
111 	} else if (LF_ISSET(CUT_NUMREQ) || (LF_ISSET(CUT_NUMOPT) &&
112 	    (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno))) {
113 		name = L('1');
114 		cb_rotate(sp);
115 		goto namecb;
116 	} else
117 		cbp = &sp->wp->dcb_store;
118 
119 copyloop:
120 	/*
121 	 * If this is a new buffer, create it and add it into the list.
122 	 * Otherwise, if it's not an append, free its current contents.
123 	 */
124 	if (cbp == NULL) {
125 		CALLOC_RET(sp, cbp, CB *, 1, sizeof(CB));
126 		cbp->name = name;
127 		TAILQ_INIT(&cbp->textq);
128 		LIST_INSERT_HEAD(&sp->wp->cutq, cbp, q);
129 	} else if (!append) {
130 		text_lfree(&cbp->textq);
131 		cbp->len = 0;
132 		cbp->flags = 0;
133 	}
134 
135 
136 	/* In line mode, it's pretty easy, just cut the lines. */
137 	if (LF_ISSET(CUT_LINEMODE)) {
138 		cbp->flags |= CB_LMODE;
139 		for (lno = fm->lno; lno <= tm->lno; ++lno)
140 			if (cut_line(sp, lno, 0, ENTIRE_LINE, cbp))
141 				goto cut_line_err;
142 	} else {
143 		/*
144 		 * Get the first line.  A length of ENTIRE_LINE causes cut_line
145 		 * to cut from the MARK to the end of the line.
146 		 */
147 		if (cut_line(sp, fm->lno, fm->cno, fm->lno != tm->lno ?
148 		    ENTIRE_LINE : (tm->cno - fm->cno) + 1, cbp))
149 			goto cut_line_err;
150 
151 		/* Get the intermediate lines. */
152 		for (lno = fm->lno; ++lno < tm->lno;)
153 			if (cut_line(sp, lno, 0, ENTIRE_LINE, cbp))
154 				goto cut_line_err;
155 
156 		/* Get the last line. */
157 		if (tm->lno != fm->lno &&
158 		    cut_line(sp, lno, 0, tm->cno + 1, cbp))
159 			goto cut_line_err;
160 	}
161 
162 	append = 0;		/* Only append to the named buffer. */
163 	sp->wp->dcbp = cbp;	/* Repoint the default buffer on each pass. */
164 
165 	if (copy_one) {		/* Copy into numeric buffer 1. */
166 		name = L('1');
167 		CBNAME(sp, cbp, name);
168 		copy_one = 0;
169 		goto copyloop;
170 	}
171 	if (copy_def) {		/* Copy into the default buffer. */
172 		cbp = &sp->wp->dcb_store;
173 		copy_def = 0;
174 		goto copyloop;
175 	}
176 	return (0);
177 
178 cut_line_err:
179 	text_lfree(&cbp->textq);
180 	cbp->len = 0;
181 	cbp->flags = 0;
182 	return (1);
183 }
184 
185 /*
186  * cb_rotate --
187  *	Rotate the numbered buffers up one.
188  */
189 static void
190 cb_rotate(SCR *sp)
191 {
192 	CB *cbp, *del_cbp;
193 
194 	del_cbp = NULL;
195 	LIST_FOREACH(cbp, &sp->wp->cutq, q)
196 		switch(cbp->name) {
197 		case L('1'):
198 			cbp->name = L('2');
199 			break;
200 		case L('2'):
201 			cbp->name = L('3');
202 			break;
203 		case L('3'):
204 			cbp->name = L('4');
205 			break;
206 		case L('4'):
207 			cbp->name = L('5');
208 			break;
209 		case L('5'):
210 			cbp->name = L('6');
211 			break;
212 		case L('6'):
213 			cbp->name = L('7');
214 			break;
215 		case L('7'):
216 			cbp->name = L('8');
217 			break;
218 		case L('8'):
219 			cbp->name = L('9');
220 			break;
221 		case L('9'):
222 			del_cbp = cbp;
223 			break;
224 		}
225 	if (del_cbp != NULL) {
226 		LIST_REMOVE(del_cbp, q);
227 		text_lfree(&del_cbp->textq);
228 		free(del_cbp);
229 	}
230 }
231 
232 /*
233  * cut_line --
234  *	Cut a portion of a single line.
235  *
236  * PUBLIC: int cut_line __P((SCR *, db_recno_t, size_t, size_t, CB *));
237  */
238 int
239 cut_line(SCR *sp, db_recno_t lno, size_t fcno, size_t clen, CB *cbp)
240 {
241 	TEXT *tp;
242 	size_t len;
243 	CHAR_T *p;
244 
245 	/* Get the line. */
246 	if (db_get(sp, lno, DBG_FATAL, &p, &len))
247 		return (1);
248 
249 	/* Create a TEXT structure that can hold the entire line. */
250 	if ((tp = text_init(sp, NULL, 0, len)) == NULL)
251 		return (1);
252 
253 	/*
254 	 * If the line isn't empty and it's not the entire line,
255 	 * copy the portion we want, and reset the TEXT length.
256 	 */
257 	if (len != 0) {
258 		if (clen == ENTIRE_LINE)
259 			clen = len - fcno;
260 		MEMCPYW(tp->lb, p + fcno, clen);
261 		tp->len = clen;
262 	}
263 
264 	/* Append to the end of the cut buffer. */
265 	TAILQ_INSERT_TAIL(&cbp->textq, tp, q);
266 	cbp->len += tp->len;
267 
268 	return (0);
269 }
270 
271 /*
272  * cut_close --
273  *	Discard all cut buffers.
274  *
275  * PUBLIC: void cut_close __P((WIN *));
276  */
277 void
278 cut_close(WIN *wp)
279 {
280 	CB *cbp;
281 
282 	/* Free cut buffer list. */
283 	while ((cbp = LIST_FIRST(&wp->cutq)) != NULL) {
284 		if (!TAILQ_EMPTY(&cbp->textq))
285 			text_lfree(&cbp->textq);
286 		LIST_REMOVE(cbp, q);
287 		free(cbp);
288 	}
289 
290 	/* Free default cut storage. */
291 	cbp = &wp->dcb_store;
292 	if (!TAILQ_EMPTY(&cbp->textq))
293 		text_lfree(&cbp->textq);
294 }
295 
296 /*
297  * text_init --
298  *	Allocate a new TEXT structure.
299  *
300  * PUBLIC: TEXT *text_init __P((SCR *, const CHAR_T *, size_t, size_t));
301  */
302 TEXT *
303 text_init(SCR *sp, const CHAR_T *p, size_t len, size_t total_len)
304 {
305 	TEXT *tp;
306 
307 	CALLOC(sp, tp, TEXT *, 1, sizeof(TEXT));
308 	if (tp == NULL)
309 		return (NULL);
310 	/* ANSI C doesn't define a call to malloc(3) for 0 bytes. */
311 	if ((tp->lb_len = total_len * sizeof(CHAR_T)) != 0) {
312 		MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len * sizeof(CHAR_T));
313 		if (tp->lb == NULL) {
314 			free(tp);
315 			return (NULL);
316 		}
317 		if (p != NULL && len != 0)
318 			MEMCPYW(tp->lb, p, len);
319 	}
320 	tp->len = len;
321 	return (tp);
322 }
323 
324 /*
325  * text_lfree --
326  *	Free a chain of text structures.
327  *
328  * PUBLIC: void text_lfree __P((TEXTH *));
329  */
330 void
331 text_lfree(TEXTH *headp)
332 {
333 	TEXT *tp;
334 
335 	while ((tp = TAILQ_FIRST(headp)) != NULL) {
336 		TAILQ_REMOVE(headp, tp, q);
337 		text_free(tp);
338 	}
339 }
340 
341 /*
342  * text_free --
343  *	Free a text structure.
344  *
345  * PUBLIC: void text_free __P((TEXT *));
346  */
347 void
348 text_free(TEXT *tp)
349 {
350 	if (tp->lb != NULL)
351 		free(tp->lb);
352 	free(tp);
353 }
354