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