| 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 | } |