| File: | src/usr.bin/vi/build/../ex/ex_global.c |
| Warning: | line 305, column 5 Value stored to 'rp' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: ex_global.c,v 1.17 2016/05/27 09:18:12 martijn 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 <limits.h> |
| 21 | #include <stdio.h> |
| 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
| 24 | #include <unistd.h> |
| 25 | |
| 26 | #include "../common/common.h" |
| 27 | |
| 28 | enum which {GLOBAL, V}; |
| 29 | |
| 30 | static int ex_g_setup(SCR *, EXCMD *, enum which); |
| 31 | |
| 32 | /* |
| 33 | * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands] |
| 34 | * Exec on lines matching a pattern. |
| 35 | * |
| 36 | * PUBLIC: int ex_global(SCR *, EXCMD *); |
| 37 | */ |
| 38 | int |
| 39 | ex_global(SCR *sp, EXCMD *cmdp) |
| 40 | { |
| 41 | return (ex_g_setup(sp, |
| 42 | cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE)((cmdp->iflags) & (0x00100)) ? V : GLOBAL)); |
| 43 | } |
| 44 | |
| 45 | /* |
| 46 | * ex_v -- [line [,line]] v /pattern/ [commands] |
| 47 | * Exec on lines not matching a pattern. |
| 48 | * |
| 49 | * PUBLIC: int ex_v(SCR *, EXCMD *); |
| 50 | */ |
| 51 | int |
| 52 | ex_v(SCR *sp, EXCMD *cmdp) |
| 53 | { |
| 54 | return (ex_g_setup(sp, cmdp, V)); |
| 55 | } |
| 56 | |
| 57 | /* |
| 58 | * ex_g_setup -- |
| 59 | * Ex global and v commands. |
| 60 | */ |
| 61 | static int |
| 62 | ex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd) |
| 63 | { |
| 64 | CHAR_T *ptrn, *p, *t; |
| 65 | EXCMD *ecp; |
| 66 | MARK abs_mark; |
| 67 | RANGE *rp; |
| 68 | busy_t btype; |
| 69 | recno_t start, end; |
| 70 | regex_t *re; |
| 71 | regmatch_t match[1]; |
| 72 | size_t len; |
| 73 | int cnt, delim, eval; |
| 74 | char *dbp; |
| 75 | |
| 76 | NEEDFILE(sp, cmdp){ if ((sp)->ep == ((void *)0)) { ex_emsg((sp), (cmdp)-> cmd->name, EXM_NOFILEYET); return (1); } }; |
| 77 | |
| 78 | if (F_ISSET(sp, SC_EX_GLOBAL)(((sp)->flags) & ((0x00020000)))) { |
| 79 | msgq(sp, M_ERR, |
| 80 | "The %s command can't be used as part of a global or v command", |
| 81 | cmdp->cmd->name); |
| 82 | return (1); |
| 83 | } |
| 84 | |
| 85 | /* |
| 86 | * Skip leading white space. Historic vi allowed any non-alphanumeric |
| 87 | * to serve as the global command delimiter. |
| 88 | */ |
| 89 | if (cmdp->argc == 0) |
| 90 | goto usage; |
| 91 | for (p = cmdp->argv[0]->bp; isblank(*p); ++p); |
| 92 | if (*p == '\0' || isalnum(*p) || |
| 93 | *p == '\\' || *p == '|' || *p == '\n') { |
| 94 | usage: ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); |
| 95 | return (1); |
| 96 | } |
| 97 | delim = *p++; |
| 98 | |
| 99 | /* |
| 100 | * Get the pattern string, toss escaped characters. |
| 101 | * |
| 102 | * QUOTING NOTE: |
| 103 | * Only toss an escaped character if it escapes a delimiter. |
| 104 | */ |
| 105 | for (ptrn = t = p;;) { |
| 106 | if (p[0] == '\0' || p[0] == delim) { |
| 107 | if (p[0] == delim) |
| 108 | ++p; |
| 109 | /* |
| 110 | * !!! |
| 111 | * Nul terminate the pattern string -- it's passed |
| 112 | * to regcomp which doesn't understand anything else. |
| 113 | */ |
| 114 | *t = '\0'; |
| 115 | break; |
| 116 | } |
| 117 | if (p[0] == '\\') { |
| 118 | if (p[1] == delim) |
| 119 | ++p; |
| 120 | else if (p[1] == '\\') |
| 121 | *t++ = *p++; |
| 122 | } |
| 123 | *t++ = *p++; |
| 124 | } |
| 125 | |
| 126 | /* If the pattern string is empty, use the last one. */ |
| 127 | if (*ptrn == '\0') { |
| 128 | if (sp->re == NULL((void *)0)) { |
| 129 | ex_emsg(sp, NULL((void *)0), EXM_NOPREVRE); |
| 130 | return (1); |
| 131 | } |
| 132 | |
| 133 | /* Re-compile the RE if necessary. */ |
| 134 | if (!F_ISSET(sp, SC_RE_SEARCH)(((sp)->flags) & ((0x00400000))) && re_compile(sp, |
| 135 | sp->re, sp->re_len, NULL((void *)0), NULL((void *)0), &sp->re_c, RE_C_SEARCH0x0002)) |
| 136 | return (1); |
| 137 | } else { |
| 138 | /* Compile the RE. */ |
| 139 | if (re_compile(sp, ptrn, t - ptrn, |
| 140 | &sp->re, &sp->re_len, &sp->re_c, RE_C_SEARCH0x0002)) |
| 141 | return (1); |
| 142 | |
| 143 | /* |
| 144 | * Set saved RE. Historic practice is that globals set |
| 145 | * direction as well as the RE. |
| 146 | */ |
| 147 | sp->searchdir = FORWARD; |
| 148 | } |
| 149 | re = &sp->re_c; |
| 150 | |
| 151 | /* The global commands always set the previous context mark. */ |
| 152 | abs_mark.lno = sp->lno; |
| 153 | abs_mark.cno = sp->cno; |
| 154 | if (mark_set(sp, ABSMARK1'\'', &abs_mark, 1)) |
| 155 | return (1); |
| 156 | |
| 157 | /* Get an EXCMD structure. */ |
| 158 | CALLOC_RET(sp, ecp, 1, sizeof(EXCMD)){ if (((ecp) = calloc((1), (sizeof(EXCMD)))) == ((void *)0)) { msgq((sp), M_SYSERR, ((void *)0)); return (1); } }; |
| 159 | TAILQ_INIT(&ecp->rq)do { (&ecp->rq)->tqh_first = ((void *)0); (&ecp ->rq)->tqh_last = &(&ecp->rq)->tqh_first; } while (0); |
| 160 | |
| 161 | /* |
| 162 | * Get a copy of the command string; the default command is print. |
| 163 | * Don't worry about a set of <blank>s with no command, that will |
| 164 | * default to print in the ex parser. We need to have two copies |
| 165 | * because the ex parser may step on the command string when it's |
| 166 | * parsing it. |
| 167 | */ |
| 168 | if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) { |
| 169 | p = "pp"; |
| 170 | len = 1; |
| 171 | } |
| 172 | |
| 173 | MALLOC_RET(sp, ecp->cp, len * 2){ if (((ecp->cp) = malloc(len * 2)) == ((void *)0)) { msgq ((sp), M_SYSERR, ((void *)0)); return (1); } }; |
| 174 | ecp->o_cp = ecp->cp; |
| 175 | ecp->o_clen = len; |
| 176 | memcpy(ecp->cp + len, p, len); |
| 177 | ecp->range_lno = OOBLNO0; |
| 178 | FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V)((ecp->agv_flags) |= (cmd == GLOBAL ? 0x04 : 0x08)); |
| 179 | LIST_INSERT_HEAD(&sp->gp->ecq, ecp, q)do { if (((ecp)->q.le_next = (&sp->gp->ecq)-> lh_first) != ((void *)0)) (&sp->gp->ecq)->lh_first ->q.le_prev = &(ecp)->q.le_next; (&sp->gp-> ecq)->lh_first = (ecp); (ecp)->q.le_prev = &(&sp ->gp->ecq)->lh_first; } while (0); |
| 180 | |
| 181 | /* |
| 182 | * For each line... The semantics of global matching are that we first |
| 183 | * have to decide which lines are going to get passed to the command, |
| 184 | * and then pass them to the command, ignoring other changes. There's |
| 185 | * really no way to do this in a single pass, since arbitrary line |
| 186 | * creation, deletion and movement can be done in the ex command. For |
| 187 | * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d". |
| 188 | * What we do is create linked list of lines that are tracked through |
| 189 | * each ex command. There's a callback routine which the DB interface |
| 190 | * routines call when a line is created or deleted. This doesn't help |
| 191 | * the layering much. |
| 192 | */ |
| 193 | btype = BUSY_ON; |
| 194 | cnt = INTERRUPT_CHECK100; |
| 195 | for (start = cmdp->addr1.lno, |
| 196 | end = cmdp->addr2.lno; start <= end; ++start) { |
| 197 | if (cnt-- == 0) { |
| 198 | if (INTERRUPTED(sp)(((((sp)->gp)->flags) & ((0x0004))) || (!v_event_get ((sp), ((void *)0), 0, 0x001) && ((((sp)->gp)-> flags) & ((0x0004)))))) { |
| 199 | LIST_REMOVE(ecp, q)do { if ((ecp)->q.le_next != ((void *)0)) (ecp)->q.le_next ->q.le_prev = (ecp)->q.le_prev; *(ecp)->q.le_prev = ( ecp)->q.le_next; ; ; } while (0); |
| 200 | free(ecp->cp); |
| 201 | free(ecp); |
| 202 | break; |
| 203 | } |
| 204 | search_busy(sp, btype); |
| 205 | btype = BUSY_UPDATE; |
| 206 | cnt = INTERRUPT_CHECK100; |
| 207 | } |
| 208 | if (db_get(sp, start, DBG_FATAL0x001, &dbp, &len)) |
| 209 | return (1); |
| 210 | match[0].rm_so = 0; |
| 211 | match[0].rm_eo = len; |
| 212 | switch (eval = |
| 213 | regexec(&sp->re_c, dbp, 0, match, REG_STARTEND00004)) { |
| 214 | case 0: |
| 215 | if (cmd == V) |
| 216 | continue; |
| 217 | break; |
| 218 | case REG_NOMATCH1: |
| 219 | if (cmd == GLOBAL) |
| 220 | continue; |
| 221 | break; |
| 222 | default: |
| 223 | re_error(sp, eval, &sp->re_c); |
| 224 | break; |
| 225 | } |
| 226 | |
| 227 | /* If follows the last entry, extend the last entry's range. */ |
| 228 | if ((rp = TAILQ_LAST(&ecp->rq, _rh)(*(((struct _rh *)((&ecp->rq)->tqh_last))->tqh_last ))) && rp->stop == start - 1) { |
| 229 | ++rp->stop; |
| 230 | continue; |
| 231 | } |
| 232 | |
| 233 | /* Allocate a new range, and append it to the list. */ |
| 234 | CALLOC(sp, rp, 1, sizeof(RANGE)){ if (((rp) = calloc((1), (sizeof(RANGE)))) == ((void *)0)) msgq ((sp), M_SYSERR, ((void *)0)); }; |
| 235 | if (rp == NULL((void *)0)) |
| 236 | return (1); |
| 237 | rp->start = rp->stop = start; |
| 238 | TAILQ_INSERT_TAIL(&ecp->rq, rp, q)do { (rp)->q.tqe_next = ((void *)0); (rp)->q.tqe_prev = (&ecp->rq)->tqh_last; *(&ecp->rq)->tqh_last = (rp); (&ecp->rq)->tqh_last = &(rp)->q.tqe_next ; } while (0); |
| 239 | } |
| 240 | search_busy(sp, BUSY_OFF); |
| 241 | return (0); |
| 242 | } |
| 243 | |
| 244 | /* |
| 245 | * ex_g_insdel -- |
| 246 | * Update the ranges based on an insertion or deletion. |
| 247 | * |
| 248 | * PUBLIC: int ex_g_insdel(SCR *, lnop_t, recno_t); |
| 249 | */ |
| 250 | int |
| 251 | ex_g_insdel(SCR *sp, lnop_t op, recno_t lno) |
| 252 | { |
| 253 | EXCMD *ecp; |
| 254 | RANGE *nrp, *rp; |
| 255 | |
| 256 | /* All insert/append operations are done as inserts. */ |
| 257 | if (op == LINE_APPEND) |
| 258 | abort(); |
| 259 | |
| 260 | if (op == LINE_RESET) |
| 261 | return (0); |
| 262 | |
| 263 | LIST_FOREACH(ecp, &sp->gp->ecq, q)for((ecp) = ((&sp->gp->ecq)->lh_first); (ecp)!= ( (void *)0); (ecp) = ((ecp)->q.le_next)) { |
| 264 | if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V)((ecp->agv_flags) & (0x01 | 0x04 | 0x08))) |
| 265 | continue; |
| 266 | for (rp = TAILQ_FIRST(&ecp->rq)((&ecp->rq)->tqh_first); rp != NULL((void *)0); rp = nrp) { |
| 267 | nrp = TAILQ_NEXT(rp, q)((rp)->q.tqe_next); |
| 268 | |
| 269 | /* If range less than the line, ignore it. */ |
| 270 | if (rp->stop < lno) |
| 271 | continue; |
| 272 | |
| 273 | /* |
| 274 | * If range greater than the line, decrement or |
| 275 | * increment the range. |
| 276 | */ |
| 277 | if (rp->start > lno) { |
| 278 | if (op == LINE_DELETE) { |
| 279 | --rp->start; |
| 280 | --rp->stop; |
| 281 | } else { |
| 282 | ++rp->start; |
| 283 | ++rp->stop; |
| 284 | } |
| 285 | continue; |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | * Lno is inside the range, decrement the end point |
| 290 | * for deletion, and split the range for insertion. |
| 291 | * In the latter case, since we're inserting a new |
| 292 | * element, neither range can be exhausted. |
| 293 | */ |
| 294 | if (op == LINE_DELETE) { |
| 295 | if (rp->start > --rp->stop) { |
| 296 | TAILQ_REMOVE(&ecp->rq, rp, q)do { if (((rp)->q.tqe_next) != ((void *)0)) (rp)->q.tqe_next ->q.tqe_prev = (rp)->q.tqe_prev; else (&ecp->rq) ->tqh_last = (rp)->q.tqe_prev; *(rp)->q.tqe_prev = ( rp)->q.tqe_next; ; ; } while (0); |
| 297 | free(rp); |
| 298 | } |
| 299 | } else { |
| 300 | CALLOC_RET(sp, nrp, 1, sizeof(RANGE)){ if (((nrp) = calloc((1), (sizeof(RANGE)))) == ((void *)0)) { msgq((sp), M_SYSERR, ((void *)0)); return (1); } }; |
| 301 | nrp->start = lno + 1; |
| 302 | nrp->stop = rp->stop + 1; |
| 303 | rp->stop = lno - 1; |
| 304 | TAILQ_INSERT_AFTER(&ecp->rq, rp, nrp, q)do { if (((nrp)->q.tqe_next = (rp)->q.tqe_next) != ((void *)0)) (nrp)->q.tqe_next->q.tqe_prev = &(nrp)->q .tqe_next; else (&ecp->rq)->tqh_last = &(nrp)-> q.tqe_next; (rp)->q.tqe_next = (nrp); (nrp)->q.tqe_prev = &(rp)->q.tqe_next; } while (0); |
| 305 | rp = nrp; |
Value stored to 'rp' is never read | |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | /* |
| 310 | * If the command deleted/inserted lines, the cursor moves to |
| 311 | * the line after the deleted/inserted line. |
| 312 | */ |
| 313 | ecp->range_lno = lno; |
| 314 | } |
| 315 | return (0); |
| 316 | } |