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