GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: varmodifiers.c,v 1.47 2017/07/10 07:10:29 bluhm Exp $ */ |
||
2 |
/* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ |
||
3 |
|||
4 |
/* |
||
5 |
* Copyright (c) 1999-2010 Marc Espie. |
||
6 |
* |
||
7 |
* Extensive code changes for the OpenBSD project. |
||
8 |
* |
||
9 |
* Redistribution and use in source and binary forms, with or without |
||
10 |
* modification, are permitted provided that the following conditions |
||
11 |
* are met: |
||
12 |
* 1. Redistributions of source code must retain the above copyright |
||
13 |
* notice, this list of conditions and the following disclaimer. |
||
14 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
15 |
* notice, this list of conditions and the following disclaimer in the |
||
16 |
* documentation and/or other materials provided with the distribution. |
||
17 |
* |
||
18 |
* THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS |
||
19 |
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
20 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
21 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD |
||
22 |
* PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
23 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
24 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
25 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
26 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
27 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
28 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
29 |
*/ |
||
30 |
/* |
||
31 |
* Copyright (c) 1988, 1989, 1990, 1993 |
||
32 |
* The Regents of the University of California. All rights reserved. |
||
33 |
* Copyright (c) 1989 by Berkeley Softworks |
||
34 |
* All rights reserved. |
||
35 |
* |
||
36 |
* This code is derived from software contributed to Berkeley by |
||
37 |
* Adam de Boor. |
||
38 |
* |
||
39 |
* Redistribution and use in source and binary forms, with or without |
||
40 |
* modification, are permitted provided that the following conditions |
||
41 |
* are met: |
||
42 |
* 1. Redistributions of source code must retain the above copyright |
||
43 |
* notice, this list of conditions and the following disclaimer. |
||
44 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
45 |
* notice, this list of conditions and the following disclaimer in the |
||
46 |
* documentation and/or other materials provided with the distribution. |
||
47 |
* 3. Neither the name of the University nor the names of its contributors |
||
48 |
* may be used to endorse or promote products derived from this software |
||
49 |
* without specific prior written permission. |
||
50 |
* |
||
51 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
52 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
53 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
54 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
55 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
56 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
57 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
58 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
59 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
60 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
61 |
* SUCH DAMAGE. |
||
62 |
*/ |
||
63 |
|||
64 |
/* VarModifiers_Apply is mostly a constituent function of Var_Parse, it |
||
65 |
* is also called directly by Var_SubstVar. */ |
||
66 |
|||
67 |
|||
68 |
#include <ctype.h> |
||
69 |
#include <sys/types.h> |
||
70 |
#include <regex.h> |
||
71 |
#include <stddef.h> |
||
72 |
#include <stdio.h> |
||
73 |
#include <stdlib.h> |
||
74 |
#include <string.h> |
||
75 |
#include "config.h" |
||
76 |
#include "defines.h" |
||
77 |
#include "buf.h" |
||
78 |
#include "var.h" |
||
79 |
#include "varmodifiers.h" |
||
80 |
#include "varname.h" |
||
81 |
#include "targ.h" |
||
82 |
#include "error.h" |
||
83 |
#include "str.h" |
||
84 |
#include "cmd_exec.h" |
||
85 |
#include "memory.h" |
||
86 |
#include "gnode.h" |
||
87 |
|||
88 |
|||
89 |
/* Var*Pattern flags */ |
||
90 |
#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ |
||
91 |
#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ |
||
92 |
#define VAR_SUB_MATCHED 0x04 /* There was a match */ |
||
93 |
#define VAR_MATCH_START 0x08 /* Match at start of word */ |
||
94 |
#define VAR_MATCH_END 0x10 /* Match at end of word */ |
||
95 |
|||
96 |
/* Modifiers flags */ |
||
97 |
#define VAR_EQUAL 0x20 |
||
98 |
#define VAR_MAY_EQUAL 0x40 |
||
99 |
#define VAR_ADD_EQUAL 0x80 |
||
100 |
#define VAR_BANG_EQUAL 0x100 |
||
101 |
|||
102 |
typedef struct { |
||
103 |
char *lbuffer; /* left string to free */ |
||
104 |
char *lhs; /* String to match */ |
||
105 |
size_t leftLen; /* Length of string */ |
||
106 |
char *rhs; /* Replacement string (w/ &'s removed) */ |
||
107 |
size_t rightLen; /* Length of replacement */ |
||
108 |
int flags; |
||
109 |
} VarPattern; |
||
110 |
|||
111 |
struct LoopStuff { |
||
112 |
struct LoopVar *var; |
||
113 |
char *expand; |
||
114 |
bool err; |
||
115 |
}; |
||
116 |
|||
117 |
static bool VarHead(struct Name *, bool, Buffer, void *); |
||
118 |
static bool VarTail(struct Name *, bool, Buffer, void *); |
||
119 |
static bool VarSuffix(struct Name *, bool, Buffer, void *); |
||
120 |
static bool VarRoot(struct Name *, bool, Buffer, void *); |
||
121 |
static bool VarMatch(struct Name *, bool, Buffer, void *); |
||
122 |
static bool VarSYSVMatch(struct Name *, bool, Buffer, void *); |
||
123 |
static bool VarNoMatch(struct Name *, bool, Buffer, void *); |
||
124 |
static bool VarUniq(struct Name *, bool, Buffer, void *); |
||
125 |
static bool VarLoop(struct Name *, bool, Buffer, void *); |
||
126 |
|||
127 |
|||
128 |
static void VarREError(int, regex_t *, const char *); |
||
129 |
static bool VarRESubstitute(struct Name *, bool, Buffer, void *); |
||
130 |
static char *do_regex(const char *, const struct Name *, void *); |
||
131 |
|||
132 |
typedef struct { |
||
133 |
regex_t re; |
||
134 |
int nsub; |
||
135 |
regmatch_t *matches; |
||
136 |
char *replace; |
||
137 |
int flags; |
||
138 |
} VarREPattern; |
||
139 |
|||
140 |
static bool VarSubstitute(struct Name *, bool, Buffer, void *); |
||
141 |
static char *VarGetPattern(SymTable *, int, const char **, int, int, |
||
142 |
size_t *, VarPattern *); |
||
143 |
static char *VarQuote(const char *, const struct Name *, void *); |
||
144 |
static char *VarModify(char *, bool (*)(struct Name *, bool, Buffer, void *), void *); |
||
145 |
|||
146 |
static void *check_empty(const char **, SymTable *, bool, int); |
||
147 |
static void *check_quote(const char **, SymTable *, bool, int); |
||
148 |
static char *do_upper(const char *, const struct Name *, void *); |
||
149 |
static char *do_lower(const char *, const struct Name *, void *); |
||
150 |
static void *check_shcmd(const char **, SymTable *, bool, int); |
||
151 |
static char *do_shcmd(const char *, const struct Name *, void *); |
||
152 |
static char *do_sort(const char *, const struct Name *, void *); |
||
153 |
static char *finish_loop(const char *, const struct Name *, void *); |
||
154 |
static int NameCompare(const void *, const void *); |
||
155 |
static char *do_label(const char *, const struct Name *, void *); |
||
156 |
static char *do_path(const char *, const struct Name *, void *); |
||
157 |
static char *do_def(const char *, const struct Name *, void *); |
||
158 |
static char *do_undef(const char *, const struct Name *, void *); |
||
159 |
static char *do_assign(const char *, const struct Name *, void *); |
||
160 |
static char *do_exec(const char *, const struct Name *, void *); |
||
161 |
|||
162 |
static void *assign_get_value(const char **, SymTable *, bool, int); |
||
163 |
static void *get_cmd(const char **, SymTable *, bool, int); |
||
164 |
static void *get_value(const char **, SymTable *, bool, int); |
||
165 |
static void *get_stringarg(const char **, SymTable *, bool, int); |
||
166 |
static void free_stringarg(void *); |
||
167 |
static void *get_patternarg(const char **, SymTable *, bool, int); |
||
168 |
static void *get_spatternarg(const char **, SymTable *, bool, int); |
||
169 |
static void *common_get_patternarg(const char **, SymTable *, bool, int, bool); |
||
170 |
static void free_patternarg(void *); |
||
171 |
static void free_looparg(void *); |
||
172 |
static void *get_sysvpattern(const char **, SymTable *, bool, int); |
||
173 |
static void *get_loop(const char **, SymTable *, bool, int); |
||
174 |
static char *LoopGrab(const char **); |
||
175 |
|||
176 |
static struct Name dummy; |
||
177 |
static struct Name *dummy_arg = &dummy; |
||
178 |
|||
179 |
static struct modifier { |
||
180 |
bool atstart; |
||
181 |
void * (*getarg)(const char **, SymTable *, bool, int); |
||
182 |
char * (*apply)(const char *, const struct Name *, void *); |
||
183 |
bool (*word_apply)(struct Name *, bool, Buffer, void *); |
||
184 |
void (*freearg)(void *); |
||
185 |
} *choose_mod[256], |
||
186 |
match_mod = {false, get_stringarg, NULL, VarMatch, free_stringarg}, |
||
187 |
nomatch_mod = {false, get_stringarg, NULL, VarNoMatch, free_stringarg}, |
||
188 |
subst_mod = {false, get_spatternarg, NULL, VarSubstitute, free_patternarg}, |
||
189 |
resubst_mod = {false, get_patternarg, do_regex, NULL, free_patternarg}, |
||
190 |
quote_mod = {false, check_quote, VarQuote, NULL , free}, |
||
191 |
tail_mod = {false, check_empty, NULL, VarTail, NULL}, |
||
192 |
head_mod = {false, check_empty, NULL, VarHead, NULL}, |
||
193 |
suffix_mod = {false, check_empty, NULL, VarSuffix, NULL}, |
||
194 |
root_mod = {false, check_empty, NULL, VarRoot, NULL}, |
||
195 |
upper_mod = {false, check_empty, do_upper, NULL, NULL}, |
||
196 |
lower_mod = {false, check_empty, do_lower, NULL, NULL}, |
||
197 |
shcmd_mod = {false, check_shcmd, do_shcmd, NULL, NULL}, |
||
198 |
sysv_mod = {false, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg}, |
||
199 |
uniq_mod = {false, check_empty, NULL, VarUniq, NULL}, |
||
200 |
sort_mod = {false, check_empty, do_sort, NULL, NULL}, |
||
201 |
loop_mod = {false, get_loop, finish_loop, VarLoop, free_looparg}, |
||
202 |
undef_mod = {true, get_value, do_undef, NULL, NULL}, |
||
203 |
def_mod = {true, get_value, do_def, NULL, NULL}, |
||
204 |
label_mod = {true, check_empty, do_label, NULL, NULL}, |
||
205 |
path_mod = {true, check_empty, do_path, NULL, NULL}, |
||
206 |
assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg}, |
||
207 |
exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg} |
||
208 |
; |
||
209 |
|||
210 |
void |
||
211 |
VarModifiers_Init() |
||
212 |
{ |
||
213 |
68248 |
choose_mod['M'] = &match_mod; |
|
214 |
34124 |
choose_mod['N'] = &nomatch_mod; |
|
215 |
34124 |
choose_mod['S'] = &subst_mod; |
|
216 |
34124 |
choose_mod['C'] = &resubst_mod; |
|
217 |
34124 |
choose_mod['Q'] = "e_mod; |
|
218 |
34124 |
choose_mod['T'] = &tail_mod; |
|
219 |
34124 |
choose_mod['H'] = &head_mod; |
|
220 |
34124 |
choose_mod['E'] = &suffix_mod; |
|
221 |
34124 |
choose_mod['R'] = &root_mod; |
|
222 |
if (FEATURES(FEATURE_UPPERLOWER)) { |
||
223 |
34124 |
choose_mod['U'] = &upper_mod; |
|
224 |
34124 |
choose_mod['L'] = &lower_mod; |
|
225 |
} |
||
226 |
if (FEATURES(FEATURE_SUNSHCMD)) |
||
227 |
34124 |
choose_mod['s'] = &shcmd_mod; |
|
228 |
if (FEATURES(FEATURE_UNIQ)) |
||
229 |
choose_mod['u'] = &uniq_mod; |
||
230 |
if (FEATURES(FEATURE_SORT)) |
||
231 |
choose_mod['O'] = &sort_mod; |
||
232 |
if (FEATURES(FEATURE_ODE)) { |
||
233 |
choose_mod['@'] = &loop_mod; |
||
234 |
choose_mod['D'] = &def_mod; |
||
235 |
choose_mod['U'] = &undef_mod; |
||
236 |
choose_mod['L'] = &label_mod; |
||
237 |
choose_mod['P'] = &path_mod; |
||
238 |
} |
||
239 |
if (FEATURES(FEATURE_ASSIGN)) |
||
240 |
choose_mod[':'] = &assign_mod; |
||
241 |
if (FEATURES(FEATURE_EXECMOD)) |
||
242 |
choose_mod['!'] = &exec_mod; |
||
243 |
34124 |
} |
|
244 |
|||
245 |
/* All modifiers handle addSpace (need to add a space before placing the |
||
246 |
* next word into the buffer) and propagate it when necessary. |
||
247 |
*/ |
||
248 |
|||
249 |
/*- |
||
250 |
*----------------------------------------------------------------------- |
||
251 |
* VarHead -- |
||
252 |
* Remove the tail of the given word and add the result to the given |
||
253 |
* buffer. |
||
254 |
*----------------------------------------------------------------------- |
||
255 |
*/ |
||
256 |
static bool |
||
257 |
VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
||
258 |
{ |
||
259 |
const char *slash; |
||
260 |
|||
261 |
15 |
slash = Str_rchri(word->s, word->e, '/'); |
|
262 |
✓✗ | 15 |
if (slash != NULL) { |
263 |
✗✓ | 15 |
if (addSpace) |
264 |
Buf_AddSpace(buf); |
||
265 |
15 |
Buf_Addi(buf, word->s, slash); |
|
266 |
15 |
} else { |
|
267 |
/* If no directory part, give . (q.v. the POSIX standard). */ |
||
268 |
if (addSpace) |
||
269 |
Buf_AddString(buf, " ."); |
||
270 |
else |
||
271 |
Buf_AddChar(buf, '.'); |
||
272 |
} |
||
273 |
15 |
return true; |
|
274 |
} |
||
275 |
|||
276 |
/*- |
||
277 |
*----------------------------------------------------------------------- |
||
278 |
* VarTail -- |
||
279 |
* Remove the head of the given word add the result to the given |
||
280 |
* buffer. |
||
281 |
*----------------------------------------------------------------------- |
||
282 |
*/ |
||
283 |
static bool |
||
284 |
VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
||
285 |
{ |
||
286 |
const char *slash; |
||
287 |
|||
288 |
✗✓ | 32635 |
if (addSpace) |
289 |
Buf_AddSpace(buf); |
||
290 |
32635 |
slash = Str_rchri(word->s, word->e, '/'); |
|
291 |
✓✓ | 32635 |
if (slash != NULL) |
292 |
2020 |
Buf_Addi(buf, slash+1, word->e); |
|
293 |
else |
||
294 |
30615 |
Buf_Addi(buf, word->s, word->e); |
|
295 |
32635 |
return true; |
|
296 |
} |
||
297 |
|||
298 |
/*- |
||
299 |
*----------------------------------------------------------------------- |
||
300 |
* VarSuffix -- |
||
301 |
* Add the suffix of the given word to the given buffer. |
||
302 |
*----------------------------------------------------------------------- |
||
303 |
*/ |
||
304 |
static bool |
||
305 |
VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
||
306 |
{ |
||
307 |
const char *dot; |
||
308 |
|||
309 |
30636 |
dot = Str_rchri(word->s, word->e, '.'); |
|
310 |
✓✗ | 30636 |
if (dot != NULL) { |
311 |
✗✓ | 30636 |
if (addSpace) |
312 |
Buf_AddSpace(buf); |
||
313 |
30636 |
Buf_Addi(buf, dot+1, word->e); |
|
314 |
addSpace = true; |
||
315 |
30636 |
} |
|
316 |
30636 |
return addSpace; |
|
317 |
} |
||
318 |
|||
319 |
/*- |
||
320 |
*----------------------------------------------------------------------- |
||
321 |
* VarRoot -- |
||
322 |
* Remove the suffix of the given word and add the result to the |
||
323 |
* buffer. |
||
324 |
*----------------------------------------------------------------------- |
||
325 |
*/ |
||
326 |
static bool |
||
327 |
VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED) |
||
328 |
{ |
||
329 |
const char *dot; |
||
330 |
|||
331 |
✓✓ | 1671521 |
if (addSpace) |
332 |
✓✓ | 2532944 |
Buf_AddSpace(buf); |
333 |
1671521 |
dot = Str_rchri(word->s, word->e, '.'); |
|
334 |
✓✗ | 1671521 |
if (dot != NULL) |
335 |
1671521 |
Buf_Addi(buf, word->s, dot); |
|
336 |
else |
||
337 |
Buf_Addi(buf, word->s, word->e); |
||
338 |
1671521 |
return true; |
|
339 |
} |
||
340 |
|||
341 |
/*- |
||
342 |
*----------------------------------------------------------------------- |
||
343 |
* VarMatch -- |
||
344 |
* Add the word to the buffer if it matches the given pattern. |
||
345 |
*----------------------------------------------------------------------- |
||
346 |
*/ |
||
347 |
static bool |
||
348 |
VarMatch(struct Name *word, bool addSpace, Buffer buf, |
||
349 |
void *pattern) /* Pattern the word must match */ |
||
350 |
{ |
||
351 |
const char *pat = pattern; |
||
352 |
|||
353 |
✓✓ | 7858874 |
if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { |
354 |
✓✓ | 700434 |
if (addSpace) |
355 |
✓✓ | 360882 |
Buf_AddSpace(buf); |
356 |
700434 |
Buf_Addi(buf, word->s, word->e); |
|
357 |
700434 |
return true; |
|
358 |
} else |
||
359 |
7158440 |
return addSpace; |
|
360 |
7858874 |
} |
|
361 |
|||
362 |
/*- |
||
363 |
*----------------------------------------------------------------------- |
||
364 |
* VarNoMatch -- |
||
365 |
* Add the word to the buffer if it doesn't match the given pattern. |
||
366 |
*----------------------------------------------------------------------- |
||
367 |
*/ |
||
368 |
static bool |
||
369 |
VarNoMatch(struct Name *word, bool addSpace, Buffer buf, |
||
370 |
void *pattern) /* Pattern the word must not match */ |
||
371 |
{ |
||
372 |
const char *pat = pattern; |
||
373 |
|||
374 |
✓✓ | 4982891 |
if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) { |
375 |
✓✓ | 4870319 |
if (addSpace) |
376 |
✓✓ | 9385246 |
Buf_AddSpace(buf); |
377 |
4870319 |
Buf_Addi(buf, word->s, word->e); |
|
378 |
4870319 |
return true; |
|
379 |
} else |
||
380 |
112572 |
return addSpace; |
|
381 |
4982891 |
} |
|
382 |
|||
383 |
static bool |
||
384 |
VarUniq(struct Name *word, bool addSpace, Buffer buf, void *lastp) |
||
385 |
{ |
||
386 |
struct Name *last = lastp; |
||
387 |
|||
388 |
/* does not match */ |
||
389 |
if (last->s == NULL || last->e - last->s != word->e - word->s || |
||
390 |
strncmp(word->s, last->s, word->e - word->s) != 0) { |
||
391 |
if (addSpace) |
||
392 |
Buf_AddSpace(buf); |
||
393 |
Buf_Addi(buf, word->s, word->e); |
||
394 |
addSpace = true; |
||
395 |
} |
||
396 |
last->s = word->s; |
||
397 |
last->e = word->e; |
||
398 |
return addSpace; |
||
399 |
} |
||
400 |
|||
401 |
static bool |
||
402 |
VarLoop(struct Name *word, bool addSpace, Buffer buf, void *vp) |
||
403 |
{ |
||
404 |
struct LoopStuff *v = vp; |
||
405 |
|||
406 |
if (addSpace) |
||
407 |
Buf_AddSpace(buf); |
||
408 |
Var_SubstVar(buf, v->expand, v->var, word->s); |
||
409 |
return true; |
||
410 |
} |
||
411 |
|||
412 |
static char * |
||
413 |
finish_loop(const char *s, const struct Name *n UNUSED , void *p) |
||
414 |
{ |
||
415 |
struct LoopStuff *l = p; |
||
416 |
|||
417 |
return Var_Subst(s, NULL, l->err); |
||
418 |
} |
||
419 |
|||
420 |
static int |
||
421 |
NameCompare(const void *ap, const void *bp) |
||
422 |
{ |
||
423 |
const struct Name *a, *b; |
||
424 |
size_t n, m; |
||
425 |
int c; |
||
426 |
|||
427 |
a = ap; |
||
428 |
b = bp; |
||
429 |
n = a->e - a->s; |
||
430 |
m = b->e - b->s; |
||
431 |
if (n < m) { |
||
432 |
c = strncmp(a->s, b->s, n); |
||
433 |
if (c != 0) |
||
434 |
return c; |
||
435 |
else |
||
436 |
return -1; |
||
437 |
} else if (m < n) { |
||
438 |
c = strncmp(a->s, b->s, m); |
||
439 |
if (c != 0) |
||
440 |
return c; |
||
441 |
else |
||
442 |
return 1; |
||
443 |
} else |
||
444 |
return strncmp(a->s, b->s, n); |
||
445 |
} |
||
446 |
|||
447 |
static char * |
||
448 |
do_sort(const char *s, const struct Name *dummy UNUSED, void *arg UNUSED) |
||
449 |
{ |
||
450 |
struct Name *t; |
||
451 |
unsigned long n, i, j; |
||
452 |
const char *start, *end; |
||
453 |
|||
454 |
n = 1024; /* start at 1024 words */ |
||
455 |
t = ereallocarray(NULL, n, sizeof(struct Name)); |
||
456 |
start = s; |
||
457 |
end = start; |
||
458 |
|||
459 |
for (i = 0;; i++) { |
||
460 |
if (i == n) { |
||
461 |
n *= 2; |
||
462 |
t = ereallocarray(t, n, sizeof(struct Name)); |
||
463 |
} |
||
464 |
start = iterate_words(&end); |
||
465 |
if (start == NULL) |
||
466 |
break; |
||
467 |
t[i].s = start; |
||
468 |
t[i].e = end; |
||
469 |
} |
||
470 |
if (i > 0) { |
||
471 |
BUFFER buf; |
||
472 |
|||
473 |
Buf_Init(&buf, end - s); |
||
474 |
qsort(t, i, sizeof(struct Name), NameCompare); |
||
475 |
Buf_Addi(&buf, t[0].s, t[0].e); |
||
476 |
for (j = 1; j < i; j++) { |
||
477 |
Buf_AddSpace(&buf); |
||
478 |
Buf_Addi(&buf, t[j].s, t[j].e); |
||
479 |
} |
||
480 |
free(t); |
||
481 |
return Buf_Retrieve(&buf); |
||
482 |
} else { |
||
483 |
free(t); |
||
484 |
return ""; |
||
485 |
} |
||
486 |
} |
||
487 |
|||
488 |
static char * |
||
489 |
do_label(const char *s UNUSED, const struct Name *n, void *arg UNUSED) |
||
490 |
{ |
||
491 |
return Str_dupi(n->s, n->e); |
||
492 |
} |
||
493 |
|||
494 |
static char * |
||
495 |
do_path(const char *s UNUSED, const struct Name *n, void *arg UNUSED) |
||
496 |
{ |
||
497 |
GNode *gn; |
||
498 |
|||
499 |
gn = Targ_FindNodei(n->s, n->e, TARG_NOCREATE); |
||
500 |
if (gn == NULL) |
||
501 |
return Str_dupi(n->s, n->e); |
||
502 |
else |
||
503 |
return strdup(gn->path); |
||
504 |
} |
||
505 |
|||
506 |
static char * |
||
507 |
do_def(const char *s, const struct Name *n UNUSED, void *arg) |
||
508 |
{ |
||
509 |
VarPattern *v = arg; |
||
510 |
if (s == NULL) { |
||
511 |
free_patternarg(v); |
||
512 |
return NULL; |
||
513 |
} else |
||
514 |
return v->lbuffer; |
||
515 |
} |
||
516 |
|||
517 |
static char * |
||
518 |
do_undef(const char *s, const struct Name *n UNUSED, void *arg) |
||
519 |
{ |
||
520 |
VarPattern *v = arg; |
||
521 |
if (s != NULL) { |
||
522 |
free_patternarg(v); |
||
523 |
return NULL; |
||
524 |
} else |
||
525 |
return v->lbuffer; |
||
526 |
} |
||
527 |
|||
528 |
static char * |
||
529 |
do_assign(const char *s, const struct Name *n, void *arg) |
||
530 |
{ |
||
531 |
VarPattern *v = arg; |
||
532 |
char *msg; |
||
533 |
char *result; |
||
534 |
|||
535 |
switch (v->flags) { |
||
536 |
case VAR_EQUAL: |
||
537 |
Var_Seti(n->s, n->e, v->lbuffer); |
||
538 |
break; |
||
539 |
case VAR_MAY_EQUAL: |
||
540 |
if (s == NULL) |
||
541 |
Var_Seti(n->s, n->e, v->lbuffer); |
||
542 |
break; |
||
543 |
case VAR_ADD_EQUAL: |
||
544 |
if (s == NULL) |
||
545 |
Var_Seti(n->s, n->e, v->lbuffer); |
||
546 |
else |
||
547 |
Var_Appendi(n->s, n->e, v->lbuffer); |
||
548 |
break; |
||
549 |
case VAR_BANG_EQUAL: |
||
550 |
result = Cmd_Exec(v->lbuffer, &msg); |
||
551 |
if (result != NULL) { |
||
552 |
Var_Seti(n->s, n->e, result); |
||
553 |
free(result); |
||
554 |
} else |
||
555 |
Error(msg, v->lbuffer); |
||
556 |
break; |
||
557 |
|||
558 |
} |
||
559 |
return NULL; |
||
560 |
} |
||
561 |
|||
562 |
static char * |
||
563 |
do_exec(const char *s UNUSED, const struct Name *n UNUSED, void *arg) |
||
564 |
{ |
||
565 |
VarPattern *v = arg; |
||
566 |
char *msg; |
||
567 |
char *result; |
||
568 |
|||
569 |
result = Cmd_Exec(v->lbuffer, &msg); |
||
570 |
if (result == NULL) |
||
571 |
Error(msg, v->lbuffer); |
||
572 |
return result; |
||
573 |
} |
||
574 |
|||
575 |
/*- |
||
576 |
*----------------------------------------------------------------------- |
||
577 |
* VarSYSVMatch -- |
||
578 |
* Add the word to the buffer if it matches the given pattern. |
||
579 |
* Used to implement the System V % modifiers. |
||
580 |
*----------------------------------------------------------------------- |
||
581 |
*/ |
||
582 |
static bool |
||
583 |
VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, void *patp) |
||
584 |
{ |
||
585 |
815364 |
size_t len; |
|
586 |
const char *ptr; |
||
587 |
815364 |
VarPattern *pat = patp; |
|
588 |
|||
589 |
✓✗ | 815364 |
if (*word->s != '\0') { |
590 |
✓✓ | 815364 |
if (addSpace) |
591 |
✓✓ | 1567877 |
Buf_AddSpace(buf); |
592 |
✓✗ | 815364 |
if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL) |
593 |
815364 |
Str_SYSVSubst(buf, pat->rhs, ptr, len); |
|
594 |
else |
||
595 |
Buf_Addi(buf, word->s, word->e); |
||
596 |
815364 |
return true; |
|
597 |
} else |
||
598 |
return addSpace; |
||
599 |
815364 |
} |
|
600 |
|||
601 |
void * |
||
602 |
get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err, int endc) |
||
603 |
{ |
||
604 |
VarPattern *pattern; |
||
605 |
const char *cp, *cp2; |
||
606 |
80895 |
BUFFER buf, buf2; |
|
607 |
int cnt = 0; |
||
608 |
80895 |
char startc = endc == ')' ? '(' : '{'; |
|
609 |
|||
610 |
80895 |
Buf_Init(&buf, 0); |
|
611 |
241650 |
for (cp = *p;; cp++) { |
|
612 |
✓✓ | 241650 |
if (*cp == '=' && cnt == 0) |
613 |
break; |
||
614 |
✗✓ | 160755 |
if (*cp == '\0') { |
615 |
Buf_Destroy(&buf); |
||
616 |
return NULL; |
||
617 |
} |
||
618 |
✗✓ | 160755 |
if (*cp == startc) |
619 |
cnt++; |
||
620 |
✗✓ | 160755 |
else if (*cp == endc) { |
621 |
cnt--; |
||
622 |
if (cnt < 0) { |
||
623 |
Buf_Destroy(&buf); |
||
624 |
return NULL; |
||
625 |
} |
||
626 |
✗✓ | 160755 |
} else if (*cp == '$') { |
627 |
if (cp[1] == '$') |
||
628 |
cp++; |
||
629 |
else { |
||
630 |
size_t len; |
||
631 |
(void)Var_ParseBuffer(&buf, cp, ctxt, err, |
||
632 |
&len); |
||
633 |
cp += len - 1; |
||
634 |
continue; |
||
635 |
} |
||
636 |
} |
||
637 |
✗✓ | 321510 |
Buf_AddChar(&buf, *cp); |
638 |
160755 |
} |
|
639 |
|||
640 |
80895 |
Buf_Init(&buf2, 0); |
|
641 |
252579 |
for (cp2 = cp+1;; cp2++) { |
|
642 |
✗✓✗✗ ✓✓✗✗ |
757737 |
if (((*cp2 == ':' && cp2[1] != endc) || *cp2 == endc) && |
643 |
252579 |
cnt == 0) |
|
644 |
break; |
||
645 |
✗✓ | 171684 |
if (*cp2 == '\0') { |
646 |
Buf_Destroy(&buf); |
||
647 |
Buf_Destroy(&buf2); |
||
648 |
return NULL; |
||
649 |
} |
||
650 |
✗✓ | 171684 |
if (*cp2 == startc) |
651 |
cnt++; |
||
652 |
✗✓ | 171684 |
else if (*cp2 == endc) { |
653 |
cnt--; |
||
654 |
if (cnt < 0) { |
||
655 |
Buf_Destroy(&buf); |
||
656 |
Buf_Destroy(&buf2); |
||
657 |
return NULL; |
||
658 |
} |
||
659 |
✗✓ | 171684 |
} else if (*cp2 == '$') { |
660 |
if (cp2[1] == '$') |
||
661 |
cp2++; |
||
662 |
else { |
||
663 |
size_t len; |
||
664 |
(void)Var_ParseBuffer(&buf2, cp2, ctxt, err, |
||
665 |
&len); |
||
666 |
cp2 += len - 1; |
||
667 |
continue; |
||
668 |
} |
||
669 |
} |
||
670 |
✗✓ | 343368 |
Buf_AddChar(&buf2, *cp2); |
671 |
171684 |
} |
|
672 |
|||
673 |
80895 |
pattern = emalloc(sizeof(VarPattern)); |
|
674 |
80895 |
pattern->lbuffer = pattern->lhs = Buf_Retrieve(&buf); |
|
675 |
80895 |
pattern->leftLen = Buf_Size(&buf); |
|
676 |
80895 |
pattern->rhs = Buf_Retrieve(&buf2); |
|
677 |
80895 |
pattern->rightLen = Buf_Size(&buf2); |
|
678 |
80895 |
pattern->flags = 0; |
|
679 |
80895 |
*p = cp2; |
|
680 |
80895 |
return pattern; |
|
681 |
80895 |
} |
|
682 |
|||
683 |
|||
684 |
/*- |
||
685 |
*----------------------------------------------------------------------- |
||
686 |
* VarSubstitute -- |
||
687 |
* Perform a string-substitution on the given word, Adding the |
||
688 |
* result to the given buffer. |
||
689 |
*----------------------------------------------------------------------- |
||
690 |
*/ |
||
691 |
static bool |
||
692 |
VarSubstitute(struct Name *word, bool addSpace, Buffer buf, |
||
693 |
void *patternp) /* Pattern for substitution */ |
||
694 |
{ |
||
695 |
size_t wordLen; /* Length of word */ |
||
696 |
const char *cp; /* General pointer */ |
||
697 |
10742036 |
VarPattern *pattern = patternp; |
|
698 |
|||
699 |
10742036 |
wordLen = word->e - word->s; |
|
700 |
✓✗ | 10742036 |
if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != |
701 |
(VAR_SUB_ONE|VAR_SUB_MATCHED)) { |
||
702 |
/* Still substituting -- break it down into simple anchored cases |
||
703 |
* and if none of them fits, perform the general substitution case. */ |
||
704 |
✓✓✓✓ |
19612691 |
if ((pattern->flags & VAR_MATCH_START) && |
705 |
8870655 |
(strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) { |
|
706 |
/* Anchored at start and beginning of word matches pattern. */ |
||
707 |
✗✓✗✗ |
3736166 |
if ((pattern->flags & VAR_MATCH_END) && |
708 |
(wordLen == pattern->leftLen)) { |
||
709 |
/* Also anchored at end and matches to the end (word |
||
710 |
* is same length as pattern) add space and rhs only |
||
711 |
* if rhs is non-null. */ |
||
712 |
if (pattern->rightLen != 0) { |
||
713 |
if (addSpace) |
||
714 |
Buf_AddSpace(buf); |
||
715 |
addSpace = true; |
||
716 |
Buf_AddChars(buf, pattern->rightLen, |
||
717 |
pattern->rhs); |
||
718 |
} |
||
719 |
pattern->flags |= VAR_SUB_MATCHED; |
||
720 |
✓✗ | 3736166 |
} else if (pattern->flags & VAR_MATCH_END) { |
721 |
/* Doesn't match to end -- copy word wholesale. */ |
||
722 |
goto nosub; |
||
723 |
} else { |
||
724 |
/* Matches at start but need to copy in |
||
725 |
* trailing characters. */ |
||
726 |
✓✗ | 3736166 |
if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ |
727 |
✓✓ | 3736166 |
if (addSpace) |
728 |
✓✓ | 6908365 |
Buf_AddSpace(buf); |
729 |
addSpace = true; |
||
730 |
3736166 |
} |
|
731 |
3736166 |
Buf_AddChars(buf, pattern->rightLen, pattern->rhs); |
|
732 |
7472332 |
Buf_AddChars(buf, wordLen - pattern->leftLen, |
|
733 |
3736166 |
word->s + pattern->leftLen); |
|
734 |
pattern->flags |= VAR_SUB_MATCHED; |
||
735 |
} |
||
736 |
✓✓ | 7005870 |
} else if (pattern->flags & VAR_MATCH_START) { |
737 |
/* Had to match at start of word and didn't -- copy whole word. */ |
||
738 |
goto nosub; |
||
739 |
✓✓ | 1871381 |
} else if (pattern->flags & VAR_MATCH_END) { |
740 |
/* Anchored at end, Find only place match could occur (leftLen |
||
741 |
* characters from the end of the word) and see if it does. Note |
||
742 |
* that because the $ will be left at the end of the lhs, we have |
||
743 |
* to use strncmp. */ |
||
744 |
1678926 |
cp = word->s + (wordLen - pattern->leftLen); |
|
745 |
✓✗✓✗ |
3357852 |
if (cp >= word->s && |
746 |
1678926 |
strncmp(cp, pattern->lhs, pattern->leftLen) == 0) { |
|
747 |
/* Match found. If we will place characters in the buffer, |
||
748 |
* add a space before hand as indicated by addSpace, then |
||
749 |
* stuff in the initial, unmatched part of the word followed |
||
750 |
* by the right-hand-side. */ |
||
751 |
✓✗ | 1678926 |
if (((cp - word->s) + pattern->rightLen) != 0) { |
752 |
✓✓ | 1678926 |
if (addSpace) |
753 |
✓✓ | 2534499 |
Buf_AddSpace(buf); |
754 |
addSpace = true; |
||
755 |
1678926 |
} |
|
756 |
1678926 |
Buf_Addi(buf, word->s, cp); |
|
757 |
1678926 |
Buf_AddChars(buf, pattern->rightLen, pattern->rhs); |
|
758 |
pattern->flags |= VAR_SUB_MATCHED; |
||
759 |
} else { |
||
760 |
/* Had to match at end and didn't. Copy entire word. */ |
||
761 |
goto nosub; |
||
762 |
} |
||
763 |
} else { |
||
764 |
/* Pattern is unanchored: search for the pattern in the word using |
||
765 |
* strstr, copying unmatched portions and the |
||
766 |
* right-hand-side for each match found, handling non-global |
||
767 |
* substitutions correctly, etc. When the loop is done, any |
||
768 |
* remaining part of the word (word and wordLen are adjusted |
||
769 |
* accordingly through the loop) is copied straight into the |
||
770 |
* buffer. |
||
771 |
* addSpace is set to false as soon as a space is added to the |
||
772 |
* buffer. */ |
||
773 |
bool done; |
||
774 |
size_t origSize; |
||
775 |
|||
776 |
done = false; |
||
777 |
192455 |
origSize = Buf_Size(buf); |
|
778 |
✓✓ | 578019 |
while (!done) { |
779 |
193109 |
cp = strstr(word->s, pattern->lhs); |
|
780 |
✓✓ | 193109 |
if (cp != NULL) { |
781 |
✓✓✓✗ |
24504 |
if (addSpace && (cp - word->s) + pattern->rightLen != 0){ |
782 |
✗✓ | 1362 |
Buf_AddSpace(buf); |
783 |
addSpace = false; |
||
784 |
681 |
} |
|
785 |
23823 |
Buf_Addi(buf, word->s, cp); |
|
786 |
23823 |
Buf_AddChars(buf, pattern->rightLen, pattern->rhs); |
|
787 |
23823 |
wordLen -= (cp - word->s) + pattern->leftLen; |
|
788 |
23823 |
word->s = cp + pattern->leftLen; |
|
789 |
✓✓✓✓ |
45851 |
if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0) |
790 |
23169 |
done = true; |
|
791 |
23823 |
pattern->flags |= VAR_SUB_MATCHED; |
|
792 |
23823 |
} else |
|
793 |
done = true; |
||
794 |
} |
||
795 |
✓✓ | 192455 |
if (wordLen != 0) { |
796 |
✓✓ | 190660 |
if (addSpace) |
797 |
✓✓ | 328305 |
Buf_AddSpace(buf); |
798 |
190660 |
Buf_AddChars(buf, wordLen, word->s); |
|
799 |
190660 |
} |
|
800 |
/* If added characters to the buffer, need to add a space |
||
801 |
* before we add any more. If we didn't add any, just return |
||
802 |
* the previous value of addSpace. */ |
||
803 |
192455 |
return Buf_Size(buf) != origSize || addSpace; |
|
804 |
} |
||
805 |
5415092 |
return addSpace; |
|
806 |
} |
||
807 |
nosub: |
||
808 |
✗✓ | 5134489 |
if (addSpace) |
809 |
Buf_AddSpace(buf); |
||
810 |
5134489 |
Buf_AddChars(buf, wordLen, word->s); |
|
811 |
5134489 |
return true; |
|
812 |
10742036 |
} |
|
813 |
|||
814 |
/*- |
||
815 |
*----------------------------------------------------------------------- |
||
816 |
* VarREError -- |
||
817 |
* Print the error caused by a regcomp or regexec call. |
||
818 |
*----------------------------------------------------------------------- |
||
819 |
*/ |
||
820 |
static void |
||
821 |
VarREError(int err, regex_t *pat, const char *str) |
||
822 |
{ |
||
823 |
char *errbuf; |
||
824 |
int errlen; |
||
825 |
|||
826 |
errlen = regerror(err, pat, 0, 0); |
||
827 |
errbuf = emalloc(errlen); |
||
828 |
regerror(err, pat, errbuf, errlen); |
||
829 |
Error("%s: %s", str, errbuf); |
||
830 |
free(errbuf); |
||
831 |
} |
||
832 |
|||
833 |
/*- |
||
834 |
*----------------------------------------------------------------------- |
||
835 |
* VarRESubstitute -- |
||
836 |
* Perform a regex substitution on the given word, placing the |
||
837 |
* result in the passed buffer. |
||
838 |
*----------------------------------------------------------------------- |
||
839 |
*/ |
||
840 |
static bool |
||
841 |
VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp) |
||
842 |
{ |
||
843 |
VarREPattern *pat; |
||
844 |
int xrv; |
||
845 |
const char *wp; |
||
846 |
char *rp; |
||
847 |
int added; |
||
848 |
|||
849 |
#define MAYBE_ADD_SPACE() \ |
||
850 |
if (addSpace && !added) \ |
||
851 |
Buf_AddSpace(buf); \ |
||
852 |
added = 1 |
||
853 |
|||
854 |
added = 0; |
||
855 |
19800352 |
wp = word->s; |
|
856 |
19800352 |
pat = patternp; |
|
857 |
|||
858 |
✗✓ | 19800352 |
if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == |
859 |
(VAR_SUB_ONE|VAR_SUB_MATCHED)) |
||
860 |
xrv = REG_NOMATCH; |
||
861 |
else { |
||
862 |
tryagain: |
||
863 |
19800352 |
xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); |
|
864 |
} |
||
865 |
|||
866 |
✓✗✓ | 19800352 |
switch (xrv) { |
867 |
case 0: |
||
868 |
19799434 |
pat->flags |= VAR_SUB_MATCHED; |
|
869 |
✓✓ | 19799434 |
if (pat->matches[0].rm_so > 0) { |
870 |
✓✓✗✓ |
5352057 |
MAYBE_ADD_SPACE(); |
871 |
5139253 |
Buf_AddChars(buf, pat->matches[0].rm_so, wp); |
|
872 |
5139253 |
} |
|
873 |
|||
874 |
✓✓ | 42584238 |
for (rp = pat->replace; *rp; rp++) { |
875 |
✗✓✗✗ ✗✗ |
1492685 |
if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { |
876 |
MAYBE_ADD_SPACE(); |
||
877 |
Buf_AddChar(buf,rp[1]); |
||
878 |
rp++; |
||
879 |
} |
||
880 |
✓✗✗✗ |
1492685 |
else if (*rp == '&' || |
881 |
✗✓ | 1492685 |
(*rp == '\\' && ISDIGIT(rp[1]))) { |
882 |
int n; |
||
883 |
const char *subbuf; |
||
884 |
int sublen; |
||
885 |
char errstr[3]; |
||
886 |
|||
887 |
if (*rp == '&') { |
||
888 |
n = 0; |
||
889 |
errstr[0] = '&'; |
||
890 |
errstr[1] = '\0'; |
||
891 |
} else { |
||
892 |
n = rp[1] - '0'; |
||
893 |
errstr[0] = '\\'; |
||
894 |
errstr[1] = rp[1]; |
||
895 |
errstr[2] = '\0'; |
||
896 |
rp++; |
||
897 |
} |
||
898 |
|||
899 |
if (n >= pat->nsub) { |
||
900 |
Error("No subexpression %s", |
||
901 |
&errstr[0]); |
||
902 |
subbuf = ""; |
||
903 |
sublen = 0; |
||
904 |
} else if (pat->matches[n].rm_so == -1 && |
||
905 |
pat->matches[n].rm_eo == -1) { |
||
906 |
Error("No match for subexpression %s", |
||
907 |
&errstr[0]); |
||
908 |
subbuf = ""; |
||
909 |
sublen = 0; |
||
910 |
} else { |
||
911 |
subbuf = wp + pat->matches[n].rm_so; |
||
912 |
sublen = pat->matches[n].rm_eo - |
||
913 |
pat->matches[n].rm_so; |
||
914 |
} |
||
915 |
|||
916 |
if (sublen > 0) { |
||
917 |
MAYBE_ADD_SPACE(); |
||
918 |
Buf_AddChars(buf, sublen, subbuf); |
||
919 |
} |
||
920 |
} else { |
||
921 |
✓✓✗✓ |
1498543 |
MAYBE_ADD_SPACE(); |
922 |
✓✓ | 2987344 |
Buf_AddChar(buf, *rp); |
923 |
} |
||
924 |
} |
||
925 |
19799434 |
wp += pat->matches[0].rm_eo; |
|
926 |
✗✓ | 19799434 |
if (pat->flags & VAR_SUB_GLOBAL) { |
927 |
/* like most modern tools, empty string matches |
||
928 |
* should advance one char at a time... |
||
929 |
*/ |
||
930 |
if (pat->matches[0].rm_eo == 0) { |
||
931 |
if (*wp) { |
||
932 |
MAYBE_ADD_SPACE(); |
||
933 |
Buf_AddChar(buf, *wp++); |
||
934 |
} else |
||
935 |
break; |
||
936 |
} |
||
937 |
goto tryagain; |
||
938 |
} |
||
939 |
✓✓ | 19799434 |
if (*wp) { |
940 |
✗✓✗✗ |
14660217 |
MAYBE_ADD_SPACE(); |
941 |
14660217 |
Buf_AddString(buf, wp); |
|
942 |
14660217 |
} |
|
943 |
break; |
||
944 |
default: |
||
945 |
VarREError(xrv, &pat->re, "Unexpected regex error"); |
||
946 |
/* FALLTHROUGH */ |
||
947 |
case REG_NOMATCH: |
||
948 |
✓✗ | 918 |
if (*wp) { |
949 |
✓✓✗✓ |
2682 |
MAYBE_ADD_SPACE(); |
950 |
918 |
Buf_AddString(buf, wp); |
|
951 |
918 |
} |
|
952 |
break; |
||
953 |
} |
||
954 |
19800352 |
return addSpace||added; |
|
955 |
} |
||
956 |
|||
957 |
/*- |
||
958 |
*----------------------------------------------------------------------- |
||
959 |
* VarModify -- |
||
960 |
* Modify each of the words of the passed string using the given |
||
961 |
* function. Used to implement all modifiers. |
||
962 |
* |
||
963 |
* Results: |
||
964 |
* A string of all the words modified appropriately. |
||
965 |
*----------------------------------------------------------------------- |
||
966 |
*/ |
||
967 |
static char * |
||
968 |
VarModify(char *str, /* String whose words should be trimmed */ |
||
969 |
/* Function to use to modify them */ |
||
970 |
bool (*modProc)(struct Name *, bool, Buffer, void *), |
||
971 |
void *datum) /* Datum to pass it */ |
||
972 |
{ |
||
973 |
66865592 |
BUFFER buf; /* Buffer for the new string */ |
|
974 |
bool addSpace; /* true if need to add a space to the |
||
975 |
* buffer before adding the trimmed |
||
976 |
* word */ |
||
977 |
33432796 |
struct Name word; |
|
978 |
|||
979 |
33432796 |
Buf_Init(&buf, 0); |
|
980 |
addSpace = false; |
||
981 |
|||
982 |
33432796 |
word.e = str; |
|
983 |
|||
984 |
✓✓ | 158734240 |
while ((word.s = iterate_words(&word.e)) != NULL) { |
985 |
char termc; |
||
986 |
|||
987 |
45934324 |
termc = *word.e; |
|
988 |
45934324 |
*((char *)(word.e)) = '\0'; |
|
989 |
45934324 |
addSpace = (*modProc)(&word, addSpace, &buf, datum); |
|
990 |
45934324 |
*((char *)(word.e)) = termc; |
|
991 |
} |
||
992 |
66865592 |
return Buf_Retrieve(&buf); |
|
993 |
33432796 |
} |
|
994 |
|||
995 |
/*- |
||
996 |
*----------------------------------------------------------------------- |
||
997 |
* VarGetPattern -- |
||
998 |
* Pass through the tstr looking for 1) escaped delimiters, |
||
999 |
* '$'s and backslashes (place the escaped character in |
||
1000 |
* uninterpreted) and 2) unescaped $'s that aren't before |
||
1001 |
* the delimiter (expand the variable substitution). |
||
1002 |
* Return the expanded string or NULL if the delimiter was missing |
||
1003 |
* If pattern is specified, handle escaped ampersands, and replace |
||
1004 |
* unescaped ampersands with the lhs of the pattern. |
||
1005 |
* |
||
1006 |
* Results: |
||
1007 |
* A string of all the words modified appropriately. |
||
1008 |
* If length is specified, return the string length of the buffer |
||
1009 |
*----------------------------------------------------------------------- |
||
1010 |
*/ |
||
1011 |
static char * |
||
1012 |
VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1, |
||
1013 |
int delim2, size_t *length, VarPattern *pattern) |
||
1014 |
{ |
||
1015 |
const char *cp; |
||
1016 |
char *result; |
||
1017 |
102333200 |
BUFFER buf; |
|
1018 |
51166600 |
size_t junk; |
|
1019 |
|||
1020 |
51166600 |
Buf_Init(&buf, 0); |
|
1021 |
✗✓ | 51166600 |
if (length == NULL) |
1022 |
length = &junk; |
||
1023 |
|||
1024 |
#define IS_A_MATCH(cp, delim1, delim2) \ |
||
1025 |
(cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \ |
||
1026 |
cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&'))) |
||
1027 |
|||
1028 |
/* |
||
1029 |
* Skim through until the matching delimiter is found; |
||
1030 |
* pick up variable substitutions on the way. Also allow |
||
1031 |
* backslashes to quote the delimiter, $, and \, but don't |
||
1032 |
* touch other backslashes. |
||
1033 |
*/ |
||
1034 |
✓✗✓✓ ✓✗ |
702270424 |
for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) { |
1035 |
✓✓✓✓ ✓✗✓✗ ✓✗✗✓ ✗✗ |
163282369 |
if (IS_A_MATCH(cp, delim1, delim2)) { |
1036 |
✗✓ | 85496 |
Buf_AddChar(&buf, cp[1]); |
1037 |
42748 |
cp++; |
|
1038 |
✓✓ | 137192656 |
} else if (*cp == '$') { |
1039 |
/* Allowed at end of pattern */ |
||
1040 |
✓✓✗✓ |
507554 |
if (cp[1] == delim1 || cp[1] == delim2) |
1041 |
✗✓ | 929332 |
Buf_AddChar(&buf, *cp); |
1042 |
else { |
||
1043 |
21444 |
size_t len; |
|
1044 |
|||
1045 |
/* If unescaped dollar sign not before the |
||
1046 |
* delimiter, assume it's a variable |
||
1047 |
* substitution and recurse. */ |
||
1048 |
21444 |
(void)Var_ParseBuffer(&buf, cp, ctxt, err, |
|
1049 |
&len); |
||
1050 |
21444 |
cp += len - 1; |
|
1051 |
21444 |
} |
|
1052 |
✓✓✗✓ |
138095437 |
} else if (pattern && *cp == '&') |
1053 |
Buf_AddChars(&buf, pattern->leftLen, pattern->lhs); |
||
1054 |
else |
||
1055 |
✗✓ | 273327596 |
Buf_AddChar(&buf, *cp); |
1056 |
} |
||
1057 |
|||
1058 |
51166600 |
*length = Buf_Size(&buf); |
|
1059 |
51166600 |
result = Buf_Retrieve(&buf); |
|
1060 |
|||
1061 |
✗✓✗✗ |
51166600 |
if (*cp != delim1 && *cp != delim2) { |
1062 |
*tstr = cp; |
||
1063 |
*length = 0; |
||
1064 |
free(result); |
||
1065 |
return NULL; |
||
1066 |
} |
||
1067 |
else { |
||
1068 |
51166600 |
*tstr = ++cp; |
|
1069 |
51166600 |
return result; |
|
1070 |
} |
||
1071 |
51166600 |
} |
|
1072 |
|||
1073 |
/*- |
||
1074 |
*----------------------------------------------------------------------- |
||
1075 |
* VarQuote -- |
||
1076 |
* Quote shell meta-characters in the string |
||
1077 |
* |
||
1078 |
* Results: |
||
1079 |
* The quoted string |
||
1080 |
*----------------------------------------------------------------------- |
||
1081 |
*/ |
||
1082 |
static char * |
||
1083 |
VarQuote(const char *str, const struct Name *n UNUSED, void *islistp) |
||
1084 |
{ |
||
1085 |
int *p = islistp; |
||
1086 |
int islist = *p; |
||
1087 |
|||
1088 |
BUFFER buf; |
||
1089 |
/* This should cover most shells :-( */ |
||
1090 |
static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; |
||
1091 |
char *rep = meta; |
||
1092 |
if (islist) |
||
1093 |
rep += 3; |
||
1094 |
|||
1095 |
Buf_Init(&buf, MAKE_BSIZE); |
||
1096 |
for (; *str; str++) { |
||
1097 |
if (strchr(rep, *str) != NULL) |
||
1098 |
Buf_AddChar(&buf, '\\'); |
||
1099 |
Buf_AddChar(&buf, *str); |
||
1100 |
} |
||
1101 |
return Buf_Retrieve(&buf); |
||
1102 |
} |
||
1103 |
|||
1104 |
static void * |
||
1105 |
check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
||
1106 |
{ |
||
1107 |
1017860 |
dummy_arg->s = NULL; |
|
1108 |
✓✓✓✗ |
911378 |
if ((*p)[1] == endc || (*p)[1] == ':') { |
1109 |
508930 |
(*p)++; |
|
1110 |
508930 |
return dummy_arg; |
|
1111 |
} else |
||
1112 |
return NULL; |
||
1113 |
508930 |
} |
|
1114 |
|||
1115 |
static void * |
||
1116 |
check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
||
1117 |
{ |
||
1118 |
int *qargs = emalloc(sizeof(int)); |
||
1119 |
*qargs = 0; |
||
1120 |
if ((*p)[1] == 'L') { |
||
1121 |
*qargs = 1; |
||
1122 |
(*p)++; |
||
1123 |
} |
||
1124 |
if ((*p)[1] == endc || (*p)[1] == ':') { |
||
1125 |
(*p)++; |
||
1126 |
return qargs; |
||
1127 |
} else { |
||
1128 |
free(qargs); |
||
1129 |
return NULL; |
||
1130 |
} |
||
1131 |
} |
||
1132 |
|||
1133 |
static void * |
||
1134 |
check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
||
1135 |
{ |
||
1136 |
if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) { |
||
1137 |
(*p)+=2; |
||
1138 |
return dummy_arg; |
||
1139 |
} else |
||
1140 |
return NULL; |
||
1141 |
} |
||
1142 |
|||
1143 |
|||
1144 |
static char * |
||
1145 |
do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
||
1146 |
{ |
||
1147 |
char *err; |
||
1148 |
char *t; |
||
1149 |
|||
1150 |
t = Cmd_Exec(s, &err); |
||
1151 |
if (err) |
||
1152 |
Error(err, s); |
||
1153 |
return t; |
||
1154 |
} |
||
1155 |
|||
1156 |
static void * |
||
1157 |
get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc) |
||
1158 |
{ |
||
1159 |
const char *cp; |
||
1160 |
char *s; |
||
1161 |
|||
1162 |
✓✓✓✓ |
268560295 |
for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) { |
1163 |
✗✓ | 79726486 |
if (*cp == '\\') { |
1164 |
if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\') |
||
1165 |
cp++; |
||
1166 |
✗✓ | 79726486 |
} else if (*cp == '\0') |
1167 |
return NULL; |
||
1168 |
} |
||
1169 |
7399847 |
s = escape_dupi(*p+1, cp, ":)}"); |
|
1170 |
7399847 |
*p = cp; |
|
1171 |
7399847 |
return s; |
|
1172 |
7399847 |
} |
|
1173 |
|||
1174 |
static void |
||
1175 |
free_stringarg(void *arg) |
||
1176 |
{ |
||
1177 |
14799694 |
free(arg); |
|
1178 |
7399847 |
} |
|
1179 |
|||
1180 |
static char * |
||
1181 |
do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
||
1182 |
{ |
||
1183 |
size_t len, i; |
||
1184 |
char *t; |
||
1185 |
|||
1186 |
len = strlen(s); |
||
1187 |
t = emalloc(len+1); |
||
1188 |
for (i = 0; i < len; i++) |
||
1189 |
t[i] = TOUPPER(s[i]); |
||
1190 |
t[len] = '\0'; |
||
1191 |
return t; |
||
1192 |
} |
||
1193 |
|||
1194 |
static char * |
||
1195 |
do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED) |
||
1196 |
{ |
||
1197 |
size_t len, i; |
||
1198 |
char *t; |
||
1199 |
|||
1200 |
67956 |
len = strlen(s); |
|
1201 |
33978 |
t = emalloc(len+1); |
|
1202 |
✓✓ | 209522 |
for (i = 0; i < len; i++) |
1203 |
70783 |
t[i] = TOLOWER(s[i]); |
|
1204 |
33978 |
t[len] = '\0'; |
|
1205 |
33978 |
return t; |
|
1206 |
} |
||
1207 |
|||
1208 |
static void * |
||
1209 |
get_patternarg(const char **p, SymTable *ctxt, bool err, int endc) |
||
1210 |
{ |
||
1211 |
19724843 |
return common_get_patternarg(p, ctxt, err, endc, false); |
|
1212 |
} |
||
1213 |
|||
1214 |
/* Extract anchors */ |
||
1215 |
static void * |
||
1216 |
get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc) |
||
1217 |
{ |
||
1218 |
VarPattern *pattern; |
||
1219 |
|||
1220 |
5858457 |
pattern = common_get_patternarg(p, ctxt, err, endc, true); |
|
1221 |
✓✗✓✗ |
11716914 |
if (pattern != NULL && pattern->leftLen > 0) { |
1222 |
✓✓ | 5858457 |
if (pattern->lhs[pattern->leftLen-1] == '$') { |
1223 |
412271 |
pattern->leftLen--; |
|
1224 |
412271 |
pattern->flags |= VAR_MATCH_END; |
|
1225 |
412271 |
} |
|
1226 |
✓✓ | 5858457 |
if (pattern->lhs[0] == '^') { |
1227 |
5418242 |
pattern->lhs++; |
|
1228 |
5418242 |
pattern->leftLen--; |
|
1229 |
5418242 |
pattern->flags |= VAR_MATCH_START; |
|
1230 |
5418242 |
} |
|
1231 |
} |
||
1232 |
5858457 |
return pattern; |
|
1233 |
} |
||
1234 |
|||
1235 |
static void |
||
1236 |
free_looparg(void *arg) |
||
1237 |
{ |
||
1238 |
struct LoopStuff *l = arg; |
||
1239 |
|||
1240 |
Var_DeleteLoopVar(l->var); |
||
1241 |
free(l->expand); |
||
1242 |
} |
||
1243 |
|||
1244 |
static char * |
||
1245 |
LoopGrab(const char **s) |
||
1246 |
{ |
||
1247 |
const char *p, *start; |
||
1248 |
|||
1249 |
start = *s; |
||
1250 |
for (p = start; *p != '@'; p++) { |
||
1251 |
if (*p == '\\') |
||
1252 |
p++; |
||
1253 |
if (*p == 0) |
||
1254 |
return NULL; |
||
1255 |
} |
||
1256 |
*s = p+1; |
||
1257 |
return escape_dupi(start, p, "@\\"); |
||
1258 |
} |
||
1259 |
|||
1260 |
static void * |
||
1261 |
get_loop(const char **p, SymTable *ctxt UNUSED, bool err, int endc) |
||
1262 |
{ |
||
1263 |
static struct LoopStuff loop; |
||
1264 |
const char *s; |
||
1265 |
const char *var; |
||
1266 |
|||
1267 |
s = *p +1; |
||
1268 |
|||
1269 |
loop.var = NULL; |
||
1270 |
loop.expand = NULL; |
||
1271 |
loop.err = err; |
||
1272 |
var = LoopGrab(&s); |
||
1273 |
if (var != NULL) { |
||
1274 |
loop.expand = LoopGrab(&s); |
||
1275 |
if (*s == endc || *s == ':') { |
||
1276 |
*p = s; |
||
1277 |
loop.var = Var_NewLoopVar(var, NULL); |
||
1278 |
return &loop; |
||
1279 |
} |
||
1280 |
} |
||
1281 |
free_looparg(&loop); |
||
1282 |
return NULL; |
||
1283 |
} |
||
1284 |
|||
1285 |
static void * |
||
1286 |
common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc, |
||
1287 |
bool dosubst) |
||
1288 |
{ |
||
1289 |
VarPattern *pattern; |
||
1290 |
char delim; |
||
1291 |
25583300 |
const char *s; |
|
1292 |
|||
1293 |
25583300 |
pattern = emalloc(sizeof(VarPattern)); |
|
1294 |
25583300 |
pattern->flags = 0; |
|
1295 |
25583300 |
s = *p; |
|
1296 |
|||
1297 |
25583300 |
delim = s[1]; |
|
1298 |
✗✓ | 25583300 |
if (delim == '\0') |
1299 |
return NULL; |
||
1300 |
25583300 |
s += 2; |
|
1301 |
|||
1302 |
25583300 |
pattern->rhs = NULL; |
|
1303 |
51166600 |
pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim, |
|
1304 |
25583300 |
&pattern->leftLen, NULL); |
|
1305 |
25583300 |
pattern->lbuffer = pattern->lhs; |
|
1306 |
✓✗ | 25583300 |
if (pattern->lhs != NULL) { |
1307 |
25583300 |
pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim, |
|
1308 |
25583300 |
&pattern->rightLen, dosubst ? pattern: NULL); |
|
1309 |
✓✗ | 25583300 |
if (pattern->rhs != NULL) { |
1310 |
/* Check for global substitution. If 'g' after the |
||
1311 |
* final delimiter, substitution is global and is |
||
1312 |
* marked that way. */ |
||
1313 |
5965 |
for (;; s++) { |
|
1314 |
✓✗✓ | 25589265 |
switch (*s) { |
1315 |
case 'g': |
||
1316 |
pattern->flags |= VAR_SUB_GLOBAL; |
||
1317 |
5965 |
continue; |
|
1318 |
case '1': |
||
1319 |
pattern->flags |= VAR_SUB_ONE; |
||
1320 |
continue; |
||
1321 |
} |
||
1322 |
break; |
||
1323 |
} |
||
1324 |
✓✓✓✗ |
30407273 |
if (*s == endc || *s == ':') { |
1325 |
25583300 |
*p = s; |
|
1326 |
25583300 |
return pattern; |
|
1327 |
} |
||
1328 |
} |
||
1329 |
} |
||
1330 |
free_patternarg(pattern); |
||
1331 |
return NULL; |
||
1332 |
25583300 |
} |
|
1333 |
|||
1334 |
static void * |
||
1335 |
assign_get_value(const char **p, SymTable *ctxt, bool err, int endc) |
||
1336 |
{ |
||
1337 |
const char *s; |
||
1338 |
int flags; |
||
1339 |
VarPattern *arg; |
||
1340 |
|||
1341 |
s = *p + 1; |
||
1342 |
if (s[0] == '=') |
||
1343 |
flags = VAR_EQUAL; |
||
1344 |
else if (s[0] == '?' && s[1] == '=') |
||
1345 |
flags = VAR_MAY_EQUAL; |
||
1346 |
else if (s[0] == '+' && s[1] == '=') |
||
1347 |
flags = VAR_ADD_EQUAL; |
||
1348 |
else if (s[0] == '!' && s[1] == '=') |
||
1349 |
flags = VAR_BANG_EQUAL; |
||
1350 |
else |
||
1351 |
return NULL; |
||
1352 |
|||
1353 |
arg = get_value(&s, ctxt, err, endc); |
||
1354 |
if (arg != NULL) { |
||
1355 |
*p = s; |
||
1356 |
arg->flags = flags; |
||
1357 |
} |
||
1358 |
return arg; |
||
1359 |
} |
||
1360 |
|||
1361 |
static void * |
||
1362 |
get_value(const char **p, SymTable *ctxt, bool err, int endc) |
||
1363 |
{ |
||
1364 |
VarPattern *pattern; |
||
1365 |
const char *s; |
||
1366 |
|||
1367 |
pattern = emalloc(sizeof(VarPattern)); |
||
1368 |
s = *p + 1; |
||
1369 |
pattern->rhs = NULL; |
||
1370 |
pattern->lbuffer = VarGetPattern(ctxt, err, &s, ':', endc, |
||
1371 |
&pattern->leftLen, NULL); |
||
1372 |
if (s[-1] == endc || s[-1] == ':') { |
||
1373 |
*p = s-1; |
||
1374 |
return pattern; |
||
1375 |
} |
||
1376 |
free_patternarg(pattern); |
||
1377 |
return NULL; |
||
1378 |
} |
||
1379 |
|||
1380 |
static void * |
||
1381 |
get_cmd(const char **p, SymTable *ctxt, bool err, int endc UNUSED) |
||
1382 |
{ |
||
1383 |
VarPattern *pattern; |
||
1384 |
const char *s; |
||
1385 |
|||
1386 |
pattern = emalloc(sizeof(VarPattern)); |
||
1387 |
s = *p + 1; |
||
1388 |
pattern->rhs = NULL; |
||
1389 |
pattern->lbuffer = VarGetPattern(ctxt, err, &s, '!', '!', |
||
1390 |
&pattern->leftLen, NULL); |
||
1391 |
if (s[-1] == '!') { |
||
1392 |
*p = s-1; |
||
1393 |
return pattern; |
||
1394 |
} |
||
1395 |
free_patternarg(pattern); |
||
1396 |
return NULL; |
||
1397 |
} |
||
1398 |
|||
1399 |
static void |
||
1400 |
free_patternarg(void *p) |
||
1401 |
{ |
||
1402 |
51328390 |
VarPattern *vp = p; |
|
1403 |
|||
1404 |
25664195 |
free(vp->lbuffer); |
|
1405 |
25664195 |
free(vp->rhs); |
|
1406 |
25664195 |
free(vp); |
|
1407 |
25664195 |
} |
|
1408 |
|||
1409 |
static char * |
||
1410 |
do_regex(const char *s, const struct Name *n UNUSED, void *arg) |
||
1411 |
{ |
||
1412 |
39380278 |
VarREPattern p2; |
|
1413 |
19690139 |
VarPattern *p = arg; |
|
1414 |
int error; |
||
1415 |
char *result; |
||
1416 |
|||
1417 |
19690139 |
error = regcomp(&p2.re, p->lhs, REG_EXTENDED); |
|
1418 |
✗✓ | 19690139 |
if (error) { |
1419 |
VarREError(error, &p2.re, "RE substitution error"); |
||
1420 |
return var_Error; |
||
1421 |
} |
||
1422 |
19690139 |
p2.nsub = p2.re.re_nsub + 1; |
|
1423 |
19690139 |
p2.replace = p->rhs; |
|
1424 |
19690139 |
p2.flags = p->flags; |
|
1425 |
✗✓ | 19690139 |
if (p2.nsub < 1) |
1426 |
p2.nsub = 1; |
||
1427 |
✗✓ | 19690139 |
if (p2.nsub > 10) |
1428 |
p2.nsub = 10; |
||
1429 |
19690139 |
p2.matches = ereallocarray(NULL, p2.nsub, sizeof(regmatch_t)); |
|
1430 |
19690139 |
result = VarModify((char *)s, VarRESubstitute, &p2); |
|
1431 |
19690139 |
regfree(&p2.re); |
|
1432 |
19690139 |
free(p2.matches); |
|
1433 |
19690139 |
return result; |
|
1434 |
19690139 |
} |
|
1435 |
|||
1436 |
char * |
||
1437 |
VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt, |
||
1438 |
bool err, bool *freePtr, const char **pscan, int paren) |
||
1439 |
{ |
||
1440 |
28128000 |
const char *tstr; |
|
1441 |
bool atstart; /* Some ODE modifiers only make sense at start */ |
||
1442 |
28128000 |
char endc = paren == '(' ? ')' : '}'; |
|
1443 |
28128000 |
const char *start = *pscan; |
|
1444 |
|||
1445 |
28128000 |
tstr = start; |
|
1446 |
/* |
||
1447 |
* Now we need to apply any modifiers the user wants applied. |
||
1448 |
* These are: |
||
1449 |
* :M<pattern> words which match the given <pattern>. |
||
1450 |
* <pattern> is of the standard file |
||
1451 |
* wildcarding form. |
||
1452 |
* :S<d><pat1><d><pat2><d>[g] |
||
1453 |
* Substitute <pat2> for <pat1> in the |
||
1454 |
* value |
||
1455 |
* :C<d><pat1><d><pat2><d>[g] |
||
1456 |
* Substitute <pat2> for regex <pat1> in |
||
1457 |
* the value |
||
1458 |
* :H Substitute the head of each word |
||
1459 |
* :T Substitute the tail of each word |
||
1460 |
* :E Substitute the extension (minus '.') of |
||
1461 |
* each word |
||
1462 |
* :R Substitute the root of each word |
||
1463 |
* (pathname minus the suffix). |
||
1464 |
* :lhs=rhs Like :S, but the rhs goes to the end of |
||
1465 |
* the invocation. |
||
1466 |
*/ |
||
1467 |
|||
1468 |
atstart = true; |
||
1469 |
✓✓✓✗ |
123401944 |
while (*tstr != endc && *tstr != '\0') { |
1470 |
struct modifier *mod; |
||
1471 |
void *arg; |
||
1472 |
char *newStr; |
||
1473 |
|||
1474 |
33572972 |
tstr++; |
|
1475 |
✗✓ | 33572972 |
if (DEBUG(VAR)) { |
1476 |
if (str != NULL) |
||
1477 |
printf("Applying :%c to \"%s\"\n", *tstr, str); |
||
1478 |
else |
||
1479 |
printf("Applying :%c\n", *tstr); |
||
1480 |
} |
||
1481 |
|||
1482 |
33572972 |
mod = choose_mod[(unsigned char)*tstr]; |
|
1483 |
arg = NULL; |
||
1484 |
|||
1485 |
✓✓✗✓ ✗✗ |
67065049 |
if (mod != NULL && (!mod->atstart || atstart)) |
1486 |
33492077 |
arg = mod->getarg(&tstr, ctxt, err, endc); |
|
1487 |
✓✓ | 33572972 |
if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) { |
1488 |
mod = &sysv_mod; |
||
1489 |
80895 |
arg = mod->getarg(&tstr, ctxt, err, endc); |
|
1490 |
80895 |
} |
|
1491 |
atstart = false; |
||
1492 |
✓✗ | 33572972 |
if (arg != NULL) { |
1493 |
✓✓✗✓ |
33679170 |
if (str != NULL || (mod->atstart && name != NULL)) { |
1494 |
✓✓ | 33466774 |
if (mod->word_apply != NULL) { |
1495 |
13742657 |
newStr = VarModify(str, |
|
1496 |
mod->word_apply, arg); |
||
1497 |
✗✓ | 13742657 |
if (mod->apply != NULL) { |
1498 |
char *newStr2; |
||
1499 |
|||
1500 |
newStr2 = mod->apply(newStr, |
||
1501 |
name, arg); |
||
1502 |
free(newStr); |
||
1503 |
newStr = newStr2; |
||
1504 |
} |
||
1505 |
} else |
||
1506 |
19724117 |
newStr = mod->apply(str, name, arg); |
|
1507 |
✓✓ | 33466774 |
if (*freePtr) |
1508 |
5704394 |
free(str); |
|
1509 |
str = newStr; |
||
1510 |
33466774 |
if (str != var_Error) |
|
1511 |
*freePtr = true; |
||
1512 |
else |
||
1513 |
*freePtr = false; |
||
1514 |
33466774 |
} |
|
1515 |
✓✓ | 33572972 |
if (mod->freearg != NULL) |
1516 |
33064042 |
mod->freearg(arg); |
|
1517 |
} else { |
||
1518 |
Error("Bad modifier: %s", tstr); |
||
1519 |
/* Try skipping to end of var... */ |
||
1520 |
while (*tstr != endc && *tstr != '\0') |
||
1521 |
tstr++; |
||
1522 |
if (str != NULL && *freePtr) |
||
1523 |
free(str); |
||
1524 |
str = var_Error; |
||
1525 |
*freePtr = false; |
||
1526 |
break; |
||
1527 |
} |
||
1528 |
✗✓ | 33572972 |
if (DEBUG(VAR) && str != NULL) |
1529 |
printf("Result is \"%s\"\n", str); |
||
1530 |
✓✗ | 33572972 |
} |
1531 |
✗✓ | 28128000 |
if (*tstr == '\0') |
1532 |
Parse_Error(PARSE_FATAL, "Unclosed variable specification"); |
||
1533 |
else |
||
1534 |
28128000 |
tstr++; |
|
1535 |
|||
1536 |
28128000 |
*pscan = tstr; |
|
1537 |
28128000 |
return str; |
|
1538 |
28128000 |
} |
|
1539 |
|||
1540 |
char * |
||
1541 |
Var_GetHead(char *s) |
||
1542 |
{ |
||
1543 |
return VarModify(s, VarHead, NULL); |
||
1544 |
} |
||
1545 |
|||
1546 |
char * |
||
1547 |
Var_GetTail(char *s) |
||
1548 |
{ |
||
1549 |
return VarModify(s, VarTail, NULL); |
||
1550 |
} |
Generated by: GCOVR (Version 3.3) |