GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: misc.c,v 1.60 2017/10/19 07:54:05 jca Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* Miscellaneous functions |
||
5 |
*/ |
||
6 |
|||
7 |
#include <ctype.h> |
||
8 |
#include <errno.h> |
||
9 |
#include <fcntl.h> |
||
10 |
#include <limits.h> |
||
11 |
#include <stdlib.h> |
||
12 |
#include <string.h> |
||
13 |
#include <unistd.h> |
||
14 |
|||
15 |
#include "sh.h" |
||
16 |
#include "charclass.h" |
||
17 |
|||
18 |
short ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */ |
||
19 |
|||
20 |
static int do_gmatch(const unsigned char *, const unsigned char *, |
||
21 |
const unsigned char *, const unsigned char *); |
||
22 |
static const unsigned char *cclass(const unsigned char *, int); |
||
23 |
|||
24 |
/* |
||
25 |
* Fast character classes |
||
26 |
*/ |
||
27 |
void |
||
28 |
setctypes(const char *s, int t) |
||
29 |
{ |
||
30 |
int i; |
||
31 |
|||
32 |
✓✓ | 1483390 |
if (t & C_IFS) { |
33 |
✓✓ | 58142138 |
for (i = 0; i < UCHAR_MAX+1; i++) |
34 |
28957952 |
ctypes[i] &= ~C_IFS; |
|
35 |
113117 |
ctypes[0] |= C_IFS; /* include \0 in C_IFS */ |
|
36 |
113117 |
} |
|
37 |
✓✓ | 11582402 |
while (*s != 0) |
38 |
5049506 |
ctypes[(unsigned char) *s++] |= t; |
|
39 |
741695 |
} |
|
40 |
|||
41 |
void |
||
42 |
initctypes(void) |
||
43 |
{ |
||
44 |
int c; |
||
45 |
|||
46 |
✓✓ | 5761965 |
for (c = 'a'; c <= 'z'; c++) |
47 |
2723838 |
ctypes[c] |= C_ALPHA; |
|
48 |
✓✓ | 5657202 |
for (c = 'A'; c <= 'Z'; c++) |
49 |
2723838 |
ctypes[c] |= C_ALPHA; |
|
50 |
104763 |
ctypes['_'] |= C_ALPHA; |
|
51 |
104763 |
setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */ |
|
52 |
104763 |
setctypes("*@#!$-?", C_VAR1); |
|
53 |
104763 |
setctypes(" \t\n", C_IFSWS); |
|
54 |
104763 |
setctypes("=-+?", C_SUBOP1); |
|
55 |
104763 |
setctypes("#%", C_SUBOP2); |
|
56 |
104763 |
setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE); |
|
57 |
104763 |
} |
|
58 |
|||
59 |
/* convert unsigned long to base N string */ |
||
60 |
|||
61 |
char * |
||
62 |
ulton(long unsigned int n, int base) |
||
63 |
{ |
||
64 |
char *p; |
||
65 |
static char buf [20]; |
||
66 |
|||
67 |
p = &buf[sizeof(buf)]; |
||
68 |
192 |
*--p = '\0'; |
|
69 |
96 |
do { |
|
70 |
96 |
*--p = "0123456789ABCDEF"[n%base]; |
|
71 |
96 |
n /= base; |
|
72 |
✗✓ | 96 |
} while (n != 0); |
73 |
96 |
return p; |
|
74 |
} |
||
75 |
|||
76 |
char * |
||
77 |
str_save(const char *s, Area *ap) |
||
78 |
{ |
||
79 |
size_t len; |
||
80 |
char *p; |
||
81 |
|||
82 |
✓✓ | 16334590 |
if (!s) |
83 |
1070075 |
return NULL; |
|
84 |
7097220 |
len = strlen(s)+1; |
|
85 |
7097220 |
p = alloc(len, ap); |
|
86 |
7097220 |
strlcpy(p, s, len); |
|
87 |
7097220 |
return (p); |
|
88 |
8167295 |
} |
|
89 |
|||
90 |
/* Allocate a string of size n+1 and copy upto n characters from the possibly |
||
91 |
* null terminated string s into it. Always returns a null terminated string |
||
92 |
* (unless n < 0). |
||
93 |
*/ |
||
94 |
char * |
||
95 |
str_nsave(const char *s, int n, Area *ap) |
||
96 |
{ |
||
97 |
char *ns; |
||
98 |
|||
99 |
✗✓ | 24754472 |
if (n < 0) |
100 |
return 0; |
||
101 |
12377236 |
ns = alloc(n + 1, ap); |
|
102 |
12377236 |
ns[0] = '\0'; |
|
103 |
12377236 |
return strncat(ns, s, n); |
|
104 |
12377236 |
} |
|
105 |
|||
106 |
/* called from expand.h:XcheckN() to grow buffer */ |
||
107 |
char * |
||
108 |
Xcheck_grow_(XString *xsp, char *xp, int more) |
||
109 |
{ |
||
110 |
514244 |
char *old_beg = xsp->beg; |
|
111 |
|||
112 |
✓✓ | 771366 |
xsp->len += more > xsp->len ? more : xsp->len; |
113 |
257122 |
xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap); |
|
114 |
257122 |
xsp->end = xsp->beg + xsp->len; |
|
115 |
257122 |
return xsp->beg + (xp - old_beg); |
|
116 |
} |
||
117 |
|||
118 |
const struct option options[] = { |
||
119 |
/* Special cases (see parse_args()): -A, -o, -s. |
||
120 |
* Options are sorted by their longnames - the order of these |
||
121 |
* entries MUST match the order of sh_flag F* enumerations in sh.h. |
||
122 |
*/ |
||
123 |
{ "allexport", 'a', OF_ANY }, |
||
124 |
#ifdef BRACE_EXPAND |
||
125 |
{ "braceexpand", 0, OF_ANY }, /* non-standard */ |
||
126 |
#endif |
||
127 |
{ "bgnice", 0, OF_ANY }, |
||
128 |
{ NULL, 'c', OF_CMDLINE }, |
||
129 |
{ "csh-history", 0, OF_ANY }, /* non-standard */ |
||
130 |
#ifdef EMACS |
||
131 |
{ "emacs", 0, OF_ANY }, |
||
132 |
#endif |
||
133 |
{ "errexit", 'e', OF_ANY }, |
||
134 |
#ifdef EMACS |
||
135 |
{ "gmacs", 0, OF_ANY }, |
||
136 |
#endif |
||
137 |
{ "ignoreeof", 0, OF_ANY }, |
||
138 |
{ "interactive",'i', OF_CMDLINE }, |
||
139 |
{ "keyword", 'k', OF_ANY }, |
||
140 |
{ "login", 'l', OF_CMDLINE }, |
||
141 |
{ "markdirs", 'X', OF_ANY }, |
||
142 |
#ifdef JOBS |
||
143 |
{ "monitor", 'm', OF_ANY }, |
||
144 |
#else /* JOBS */ |
||
145 |
{ NULL, 'm', 0 }, /* so FMONITOR not ifdef'd */ |
||
146 |
#endif /* JOBS */ |
||
147 |
{ "noclobber", 'C', OF_ANY }, |
||
148 |
{ "noexec", 'n', OF_ANY }, |
||
149 |
{ "noglob", 'f', OF_ANY }, |
||
150 |
{ "nohup", 0, OF_ANY }, |
||
151 |
{ "nolog", 0, OF_ANY }, /* no effect */ |
||
152 |
#ifdef JOBS |
||
153 |
{ "notify", 'b', OF_ANY }, |
||
154 |
#endif /* JOBS */ |
||
155 |
{ "nounset", 'u', OF_ANY }, |
||
156 |
{ "physical", 0, OF_ANY }, /* non-standard */ |
||
157 |
{ "posix", 0, OF_ANY }, /* non-standard */ |
||
158 |
{ "privileged", 'p', OF_ANY }, |
||
159 |
{ "restricted", 'r', OF_CMDLINE }, |
||
160 |
{ "sh", 0, OF_ANY }, /* non-standard */ |
||
161 |
{ "stdin", 's', OF_CMDLINE }, /* pseudo non-standard */ |
||
162 |
{ "trackall", 'h', OF_ANY }, |
||
163 |
{ "verbose", 'v', OF_ANY }, |
||
164 |
#ifdef VI |
||
165 |
{ "vi", 0, OF_ANY }, |
||
166 |
{ "viraw", 0, OF_ANY }, /* no effect */ |
||
167 |
{ "vi-show8", 0, OF_ANY }, /* non-standard */ |
||
168 |
{ "vi-tabcomplete", 0, OF_ANY }, /* non-standard */ |
||
169 |
{ "vi-esccomplete", 0, OF_ANY }, /* non-standard */ |
||
170 |
#endif |
||
171 |
{ "xtrace", 'x', OF_ANY }, |
||
172 |
/* Anonymous flags: used internally by shell only |
||
173 |
* (not visible to user) |
||
174 |
*/ |
||
175 |
{ NULL, 0, OF_INTERNAL }, /* FTALKING_I */ |
||
176 |
}; |
||
177 |
|||
178 |
/* |
||
179 |
* translate -o option into F* constant (also used for test -o option) |
||
180 |
*/ |
||
181 |
int |
||
182 |
option(const char *n) |
||
183 |
{ |
||
184 |
int i; |
||
185 |
|||
186 |
✓✗ | 228 |
for (i = 0; i < NELEM(options); i++) |
187 |
✓✓✓✓ |
210 |
if (options[i].name && strcmp(options[i].name, n) == 0) |
188 |
12 |
return i; |
|
189 |
|||
190 |
return -1; |
||
191 |
12 |
} |
|
192 |
|||
193 |
struct options_info { |
||
194 |
int opt_width; |
||
195 |
struct { |
||
196 |
const char *name; |
||
197 |
int flag; |
||
198 |
} opts[NELEM(options)]; |
||
199 |
}; |
||
200 |
|||
201 |
static char *options_fmt_entry(void *arg, int i, char *buf, int buflen); |
||
202 |
static void printoptions(int verbose); |
||
203 |
|||
204 |
/* format a single select menu item */ |
||
205 |
static char * |
||
206 |
options_fmt_entry(void *arg, int i, char *buf, int buflen) |
||
207 |
{ |
||
208 |
struct options_info *oi = (struct options_info *) arg; |
||
209 |
|||
210 |
shf_snprintf(buf, buflen, "%-*s %s", |
||
211 |
oi->opt_width, oi->opts[i].name, |
||
212 |
Flag(oi->opts[i].flag) ? "on" : "off"); |
||
213 |
return buf; |
||
214 |
} |
||
215 |
|||
216 |
static void |
||
217 |
printoptions(int verbose) |
||
218 |
{ |
||
219 |
int i; |
||
220 |
|||
221 |
if (verbose) { |
||
222 |
struct options_info oi; |
||
223 |
int n, len; |
||
224 |
|||
225 |
/* verbose version */ |
||
226 |
shprintf("Current option settings\n"); |
||
227 |
|||
228 |
for (i = n = oi.opt_width = 0; i < NELEM(options); i++) { |
||
229 |
if (options[i].name) { |
||
230 |
len = strlen(options[i].name); |
||
231 |
oi.opts[n].name = options[i].name; |
||
232 |
oi.opts[n++].flag = i; |
||
233 |
if (len > oi.opt_width) |
||
234 |
oi.opt_width = len; |
||
235 |
} |
||
236 |
} |
||
237 |
print_columns(shl_stdout, n, options_fmt_entry, &oi, |
||
238 |
oi.opt_width + 5, 1); |
||
239 |
} else { |
||
240 |
/* short version ala ksh93 */ |
||
241 |
shprintf("set"); |
||
242 |
for (i = 0; i < NELEM(options); i++) { |
||
243 |
if (options[i].name) |
||
244 |
shprintf(" %co %s", |
||
245 |
Flag(i) ? '-' : '+', |
||
246 |
options[i].name); |
||
247 |
} |
||
248 |
shprintf("\n"); |
||
249 |
} |
||
250 |
} |
||
251 |
|||
252 |
char * |
||
253 |
getoptions(void) |
||
254 |
{ |
||
255 |
int i; |
||
256 |
28 |
char m[(int) FNFLAGS + 1]; |
|
257 |
14 |
char *cp = m; |
|
258 |
|||
259 |
✓✓ | 1036 |
for (i = 0; i < NELEM(options); i++) |
260 |
✓✓✓✓ |
770 |
if (options[i].c && Flag(i)) |
261 |
36 |
*cp++ = options[i].c; |
|
262 |
14 |
*cp = 0; |
|
263 |
28 |
return str_save(m, ATEMP); |
|
264 |
14 |
} |
|
265 |
|||
266 |
/* change a Flag(*) value; takes care of special actions */ |
||
267 |
void |
||
268 |
change_flag(enum sh_flag f, |
||
269 |
int what, /* flag to change */ |
||
270 |
int newval) /* what is changing the flag (command line vs set) */ |
||
271 |
{ |
||
272 |
int oldval; |
||
273 |
|||
274 |
524154 |
oldval = Flag(f); |
|
275 |
262077 |
Flag(f) = newval; |
|
276 |
#ifdef JOBS |
||
277 |
✗✓ | 262077 |
if (f == FMONITOR) { |
278 |
if (what != OF_CMDLINE && newval != oldval) |
||
279 |
j_change(); |
||
280 |
} else |
||
281 |
#endif /* JOBS */ |
||
282 |
#ifdef EDIT |
||
283 |
if (0 |
||
284 |
# ifdef VI |
||
285 |
262077 |
|| f == FVI |
|
286 |
# endif /* VI */ |
||
287 |
# ifdef EMACS |
||
288 |
✓✓ | 262077 |
|| f == FEMACS || f == FGMACS |
289 |
# endif /* EMACS */ |
||
290 |
) |
||
291 |
{ |
||
292 |
✓✗ | 106737 |
if (newval) { |
293 |
# ifdef VI |
||
294 |
106737 |
Flag(FVI) = 0; |
|
295 |
# endif /* VI */ |
||
296 |
# ifdef EMACS |
||
297 |
106737 |
Flag(FEMACS) = Flag(FGMACS) = 0; |
|
298 |
# endif /* EMACS */ |
||
299 |
106737 |
Flag(f) = newval; |
|
300 |
106737 |
} |
|
301 |
} else |
||
302 |
#endif /* EDIT */ |
||
303 |
/* Turning off -p? */ |
||
304 |
✗✓ | 155340 |
if (f == FPRIVILEGED && oldval && !newval) { |
305 |
gid_t gid = getgid(); |
||
306 |
|||
307 |
setresgid(gid, gid, gid); |
||
308 |
setgroups(1, &gid); |
||
309 |
setresuid(ksheuid, ksheuid, ksheuid); |
||
310 |
✓✓ | 155340 |
} else if (f == FPOSIX && newval) { |
311 |
#ifdef BRACE_EXPAND |
||
312 |
12 |
Flag(FBRACEEXPAND) = 0 |
|
313 |
#endif /* BRACE_EXPAND */ |
||
314 |
; |
||
315 |
12 |
} |
|
316 |
/* Changing interactive flag? */ |
||
317 |
✓✓ | 262077 |
if (f == FTALKING) { |
318 |
✓✗✓✗ |
444 |
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) |
319 |
222 |
Flag(FTALKING_I) = newval; |
|
320 |
} |
||
321 |
262077 |
} |
|
322 |
|||
323 |
/* parse command line & set command arguments. returns the index of |
||
324 |
* non-option arguments, -1 if there is an error. |
||
325 |
*/ |
||
326 |
int |
||
327 |
parse_args(char **argv, |
||
328 |
int what, /* OF_CMDLINE or OF_SET */ |
||
329 |
int *setargsp) |
||
330 |
{ |
||
331 |
static char cmd_opts[NELEM(options) + 3]; /* o:\0 */ |
||
332 |
static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */ |
||
333 |
char *opts; |
||
334 |
char *array = NULL; |
||
335 |
225606 |
Getopt go; |
|
336 |
int i, optc, set, sortargs = 0, arrayset = 0; |
||
337 |
|||
338 |
/* First call? Build option strings... */ |
||
339 |
✓✓ | 112803 |
if (cmd_opts[0] == '\0') { |
340 |
char *p, *q; |
||
341 |
|||
342 |
/* see cmd_opts[] declaration */ |
||
343 |
104763 |
strlcpy(cmd_opts, "o:", sizeof cmd_opts); |
|
344 |
104763 |
p = cmd_opts + strlen(cmd_opts); |
|
345 |
/* see set_opts[] declaration */ |
||
346 |
104763 |
strlcpy(set_opts, "A:o;s", sizeof set_opts); |
|
347 |
104763 |
q = set_opts + strlen(set_opts); |
|
348 |
✓✓ | 7752462 |
for (i = 0; i < NELEM(options); i++) { |
349 |
✓✓ | 3771468 |
if (options[i].c) { |
350 |
✓✗ | 1990497 |
if (options[i].flags & OF_CMDLINE) |
351 |
1990497 |
*p++ = options[i].c; |
|
352 |
✓✓ | 1990497 |
if (options[i].flags & OF_SET) |
353 |
1466682 |
*q++ = options[i].c; |
|
354 |
} |
||
355 |
} |
||
356 |
104763 |
*p = '\0'; |
|
357 |
104763 |
*q = '\0'; |
|
358 |
104763 |
} |
|
359 |
|||
360 |
✓✓ | 112803 |
if (what == OF_CMDLINE) { |
361 |
char *p; |
||
362 |
/* Set FLOGIN before parsing options so user can clear |
||
363 |
* flag using +l. |
||
364 |
*/ |
||
365 |
✓✓ | 209526 |
Flag(FLOGIN) = (argv[0][0] == '-' || |
366 |
✓✓ | 187970 |
((p = strrchr(argv[0], '/')) && *++p == '-')); |
367 |
opts = cmd_opts; |
||
368 |
104763 |
} else |
|
369 |
opts = set_opts; |
||
370 |
112803 |
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); |
|
371 |
✓✓ | 536602 |
while ((optc = ksh_getopt(argv, &go, opts)) != -1) { |
372 |
155498 |
set = (go.info & GI_PLUS) ? 0 : 1; |
|
373 |
✓✓✗✓ |
155498 |
switch (optc) { |
374 |
case 'A': |
||
375 |
158 |
arrayset = set ? 1 : -1; |
|
376 |
158 |
array = go.optarg; |
|
377 |
158 |
break; |
|
378 |
|||
379 |
case 'o': |
||
380 |
✗✓ | 12 |
if (go.optarg == NULL) { |
381 |
/* lone -o: print options |
||
382 |
* |
||
383 |
* Note that on the command line, -o requires |
||
384 |
* an option (ie, can't get here if what is |
||
385 |
* OF_CMDLINE). |
||
386 |
*/ |
||
387 |
printoptions(set); |
||
388 |
break; |
||
389 |
} |
||
390 |
12 |
i = option(go.optarg); |
|
391 |
✓✗✓✓ |
24 |
if (i >= 0 && set == Flag(i)) |
392 |
/* Don't check the context if the flag |
||
393 |
* isn't changing - makes "set -o interactive" |
||
394 |
* work if you're already interactive. Needed |
||
395 |
* if the output of "set +o" is to be used. |
||
396 |
*/ |
||
397 |
; |
||
398 |
✓✗✓✗ |
12 |
else if (i >= 0 && (options[i].flags & what)) |
399 |
6 |
change_flag((enum sh_flag) i, what, set); |
|
400 |
else { |
||
401 |
bi_errorf("%s: bad option", go.optarg); |
||
402 |
return -1; |
||
403 |
} |
||
404 |
break; |
||
405 |
|||
406 |
case '?': |
||
407 |
return -1; |
||
408 |
|||
409 |
default: |
||
410 |
/* -s: sort positional params (at&t ksh stupidity) */ |
||
411 |
✓✓ | 155328 |
if (what == OF_SET && optc == 's') { |
412 |
sortargs = 1; |
||
413 |
6 |
break; |
|
414 |
} |
||
415 |
✓✗ | 1744166 |
for (i = 0; i < NELEM(options); i++) |
416 |
✓✓✓✗ |
1027405 |
if (optc == options[i].c && |
417 |
155322 |
(what & options[i].flags)) { |
|
418 |
155322 |
change_flag((enum sh_flag) i, what, |
|
419 |
set); |
||
420 |
155322 |
break; |
|
421 |
} |
||
422 |
✗✓ | 155322 |
if (i == NELEM(options)) { |
423 |
internal_errorf(1, "parse_args: `%c'", optc); |
||
424 |
return -1; /* not reached */ |
||
425 |
} |
||
426 |
} |
||
427 |
} |
||
428 |
✓✓✓✓ ✓✗ |
225599 |
if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && |
429 |
✓✓✗✓ |
204136 |
(argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && |
430 |
238 |
argv[go.optind][1] == '\0') { |
|
431 |
/* lone - clears -v and -x flags */ |
||
432 |
✓✗ | 238 |
if (argv[go.optind][0] == '-' && !Flag(FPOSIX)) |
433 |
238 |
Flag(FVERBOSE) = Flag(FXTRACE) = 0; |
|
434 |
/* set skips lone - or + option */ |
||
435 |
238 |
go.optind++; |
|
436 |
238 |
} |
|
437 |
✓✓ | 112803 |
if (setargsp) |
438 |
/* -- means set $#/$* even if there are no arguments */ |
||
439 |
✓✓✓✓ |
31706 |
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || |
440 |
7744 |
argv[go.optind]); |
|
441 |
|||
442 |
✓✓✓✗ ✗✓ |
113119 |
if (arrayset && (!*array || *skip_varname(array, false))) { |
443 |
bi_errorf("%s: is not an identifier", array); |
||
444 |
return -1; |
||
445 |
} |
||
446 |
✓✓ | 112803 |
if (sortargs) { |
447 |
✓✓ | 60 |
for (i = go.optind; argv[i]; i++) |
448 |
; |
||
449 |
6 |
qsortp((void **) &argv[go.optind], (size_t) (i - go.optind), |
|
450 |
xstrcmp); |
||
451 |
6 |
} |
|
452 |
✓✓ | 112803 |
if (arrayset) { |
453 |
158 |
set_array(array, arrayset, argv + go.optind); |
|
454 |
✓✓ | 1870 |
for (; argv[go.optind]; go.optind++) |
455 |
; |
||
456 |
} |
||
457 |
|||
458 |
112803 |
return go.optind; |
|
459 |
112803 |
} |
|
460 |
|||
461 |
/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */ |
||
462 |
int |
||
463 |
getn(const char *as, int *ai) |
||
464 |
{ |
||
465 |
94164 |
char *p; |
|
466 |
long n; |
||
467 |
|||
468 |
47082 |
n = strtol(as, &p, 10); |
|
469 |
|||
470 |
✓✗✓✓ |
94164 |
if (!*as || *p || INT_MIN >= n || n >= INT_MAX) |
471 |
36 |
return 0; |
|
472 |
|||
473 |
47046 |
*ai = (int)n; |
|
474 |
47046 |
return 1; |
|
475 |
47082 |
} |
|
476 |
|||
477 |
/* getn() that prints error */ |
||
478 |
int |
||
479 |
bi_getn(const char *as, int *ai) |
||
480 |
{ |
||
481 |
300 |
int rv = getn(as, ai); |
|
482 |
|||
483 |
✓✓ | 150 |
if (!rv) |
484 |
12 |
bi_errorf("%s: bad number", as); |
|
485 |
138 |
return rv; |
|
486 |
} |
||
487 |
|||
488 |
/* -------- gmatch.c -------- */ |
||
489 |
|||
490 |
/* |
||
491 |
* int gmatch(string, pattern) |
||
492 |
* char *string, *pattern; |
||
493 |
* |
||
494 |
* Match a pattern as in sh(1). |
||
495 |
* pattern character are prefixed with MAGIC by expand. |
||
496 |
*/ |
||
497 |
|||
498 |
int |
||
499 |
gmatch(const char *s, const char *p, int isfile) |
||
500 |
{ |
||
501 |
const char *se, *pe; |
||
502 |
|||
503 |
✗✓ | 6936478 |
if (s == NULL || p == NULL) |
504 |
return 0; |
||
505 |
3468239 |
se = s + strlen(s); |
|
506 |
3468239 |
pe = p + strlen(p); |
|
507 |
/* isfile is false iff no syntax check has been done on |
||
508 |
* the pattern. If check fails, just to a strcmp(). |
||
509 |
*/ |
||
510 |
✓✓✓✓ |
3700739 |
if (!isfile && !has_globbing(p, pe)) { |
511 |
21374 |
int len = pe - p + 1; |
|
512 |
21374 |
char tbuf[64]; |
|
513 |
✓✗ | 64122 |
char *t = len <= sizeof(tbuf) ? tbuf : |
514 |
alloc(len, ATEMP); |
||
515 |
21374 |
debunk(t, p, len); |
|
516 |
21374 |
return !strcmp(t, s); |
|
517 |
21374 |
} |
|
518 |
3446865 |
return do_gmatch((const unsigned char *) s, (const unsigned char *) se, |
|
519 |
(const unsigned char *) p, (const unsigned char *) pe); |
||
520 |
3468239 |
} |
|
521 |
|||
522 |
/* Returns if p is a syntacticly correct globbing pattern, false |
||
523 |
* if it contains no pattern characters or if there is a syntax error. |
||
524 |
* Syntax errors are: |
||
525 |
* - [ with no closing ] |
||
526 |
* - imbalanced $(...) expression |
||
527 |
* - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d)) |
||
528 |
*/ |
||
529 |
/*XXX |
||
530 |
- if no magic, |
||
531 |
if dest given, copy to dst |
||
532 |
return ? |
||
533 |
- if magic && (no globbing || syntax error) |
||
534 |
debunk to dst |
||
535 |
return ? |
||
536 |
- return ? |
||
537 |
*/ |
||
538 |
int |
||
539 |
has_globbing(const char *xp, const char *xpe) |
||
540 |
{ |
||
541 |
const unsigned char *p = (const unsigned char *) xp; |
||
542 |
const unsigned char *pe = (const unsigned char *) xpe; |
||
543 |
int c; |
||
544 |
int nest = 0, bnest = 0; |
||
545 |
int saw_glob = 0; |
||
546 |
int in_bracket = 0; /* inside [...] */ |
||
547 |
|||
548 |
✓✓ | 13266232 |
for (; p < pe; p++) { |
549 |
✓✓ | 4246988 |
if (!ISMAGIC(*p)) |
550 |
continue; |
||
551 |
✓✓ | 2547708 |
if ((c = *++p) == '*' || c == '?') |
552 |
84294 |
saw_glob = 1; |
|
553 |
✓✓ | 2463414 |
else if (c == '[') { |
554 |
✓✓ | 1554577 |
if (!in_bracket) { |
555 |
saw_glob = 1; |
||
556 |
in_bracket = 1; |
||
557 |
✓✓✓✓ |
1601937 |
if (ISMAGIC(p[1]) && p[2] == '!') |
558 |
47384 |
p += 2; |
|
559 |
✓✓✓✓ |
1554559 |
if (ISMAGIC(p[1]) && p[2] == ']') |
560 |
6 |
p += 2; |
|
561 |
} |
||
562 |
/* XXX Do we need to check ranges here? POSIX Q */ |
||
563 |
✓✓ | 908837 |
} else if (c == ']') { |
564 |
✓✓ | 225107 |
if (in_bracket) { |
565 |
✓✓ | 136675 |
if (bnest) /* [a*(b]) */ |
566 |
6 |
return 0; |
|
567 |
in_bracket = 0; |
||
568 |
136669 |
} |
|
569 |
✓✓✓✗ |
1015580 |
} else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) { |
570 |
saw_glob = 1; |
||
571 |
✓✓ | 331850 |
if (in_bracket) |
572 |
12 |
bnest++; |
|
573 |
else |
||
574 |
331838 |
nest++; |
|
575 |
✓✓ | 351880 |
} else if (c == '|') { |
576 |
✗✓ | 11880 |
if (in_bracket && !bnest) /* *(a[foo|bar]) */ |
577 |
return 0; |
||
578 |
✓✓ | 340000 |
} else if (c == /*(*/ ')') { |
579 |
✓✓ | 331844 |
if (in_bracket) { |
580 |
✓✓ | 36 |
if (!bnest--) /* *(a[b)c] */ |
581 |
30 |
return 0; |
|
582 |
✓✗ | 331808 |
} else if (nest) |
583 |
331808 |
nest--; |
|
584 |
} |
||
585 |
/* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-] |
||
586 |
MAGIC-{, MAGIC-,, MAGIC-} */ |
||
587 |
} |
||
588 |
✓✓ | 3402661 |
return saw_glob && !in_bracket && !nest; |
589 |
1590776 |
} |
|
590 |
|||
591 |
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ |
||
592 |
static int |
||
593 |
do_gmatch(const unsigned char *s, const unsigned char *se, |
||
594 |
const unsigned char *p, const unsigned char *pe) |
||
595 |
{ |
||
596 |
int sc, pc; |
||
597 |
const unsigned char *prest, *psub, *pnext; |
||
598 |
const unsigned char *srest; |
||
599 |
|||
600 |
✗✓ | 107805302 |
if (s == NULL || p == NULL) |
601 |
return 0; |
||
602 |
✓✓ | 148892565 |
while (p < pe) { |
603 |
63597108 |
pc = *p++; |
|
604 |
✓✓ | 189835673 |
sc = s < se ? *s : '\0'; |
605 |
63597108 |
s++; |
|
606 |
✓✓ | 63597108 |
if (!ISMAGIC(pc)) { |
607 |
✓✓ | 41279543 |
if (sc != pc) |
608 |
28701900 |
return 0; |
|
609 |
12577643 |
continue; |
|
610 |
} |
||
611 |
✓✓✓✗ ✓✗✗✓ ✓✓ |
22317565 |
switch (*p++) { |
612 |
case '[': |
||
613 |
✓✓✓✓ |
35517788 |
if (sc == 0 || (p = cclass(p, sc)) == NULL) |
614 |
4008363 |
return 0; |
|
615 |
break; |
||
616 |
|||
617 |
case '?': |
||
618 |
✗✓ | 4 |
if (sc == 0) |
619 |
return 0; |
||
620 |
break; |
||
621 |
|||
622 |
case '*': |
||
623 |
✓✓ | 3154108 |
if (p == pe) |
624 |
47151 |
return 1; |
|
625 |
3106957 |
s--; |
|
626 |
3106957 |
do { |
|
627 |
✓✓ | 30401053 |
if (do_gmatch(s, se, p, pe)) |
628 |
2879640 |
return 1; |
|
629 |
✓✓ | 27521413 |
} while (s++ < se); |
630 |
227317 |
return 0; |
|
631 |
|||
632 |
/* |
||
633 |
* [*+?@!](pattern|pattern|..) |
||
634 |
* |
||
635 |
* Not ifdef'd KSH as this is needed for ${..%..}, etc. |
||
636 |
*/ |
||
637 |
case 0x80|'+': /* matches one or more times */ |
||
638 |
case 0x80|'*': /* matches zero or more times */ |
||
639 |
✗✓ | 699932 |
if (!(prest = pat_scan(p, pe, 0))) |
640 |
return 0; |
||
641 |
699932 |
s--; |
|
642 |
/* take care of zero matches */ |
||
643 |
✓✓✓✓ |
1399516 |
if (p[-1] == (0x80 | '*') && |
644 |
699584 |
do_gmatch(s, se, prest, pe)) |
|
645 |
7804 |
return 1; |
|
646 |
692482 |
for (psub = p; ; psub = pnext) { |
|
647 |
692482 |
pnext = pat_scan(psub, pe, 1); |
|
648 |
✓✓ | 37472428 |
for (srest = s; srest <= se; srest++) { |
649 |
✓✓✓✓ |
18606870 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
650 |
✓✓ | 531772 |
(do_gmatch(srest, se, prest, pe) || |
651 |
✓✓ | 1055428 |
(s != srest && do_gmatch(srest, |
652 |
527570 |
se, p - 2, pe)))) |
|
653 |
35568 |
return 1; |
|
654 |
} |
||
655 |
✓✓ | 656914 |
if (pnext == prest) |
656 |
break; |
||
657 |
} |
||
658 |
656560 |
return 0; |
|
659 |
|||
660 |
case 0x80|'?': /* matches zero or once */ |
||
661 |
case 0x80|'@': /* matches one of the patterns */ |
||
662 |
case 0x80|' ': /* simile for @ */ |
||
663 |
✗✓ | 199810 |
if (!(prest = pat_scan(p, pe, 0))) |
664 |
return 0; |
||
665 |
199810 |
s--; |
|
666 |
/* Take care of zero matches */ |
||
667 |
✗✓✗✗ |
199810 |
if (p[-1] == (0x80 | '?') && |
668 |
do_gmatch(s, se, prest, pe)) |
||
669 |
return 1; |
||
670 |
203556 |
for (psub = p; ; psub = pnext) { |
|
671 |
203556 |
pnext = pat_scan(psub, pe, 1); |
|
672 |
203556 |
srest = prest == pe ? se : s; |
|
673 |
✓✓ | 788766 |
for (; srest <= se; srest++) { |
674 |
✓✓✓✗ |
216285 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
675 |
12729 |
do_gmatch(srest, se, prest, pe)) |
|
676 |
12729 |
return 1; |
|
677 |
} |
||
678 |
✓✓ | 190827 |
if (pnext == prest) |
679 |
break; |
||
680 |
} |
||
681 |
187081 |
return 0; |
|
682 |
|||
683 |
case 0x80|'!': /* matches none of the patterns */ |
||
684 |
✗✓ | 30 |
if (!(prest = pat_scan(p, pe, 0))) |
685 |
return 0; |
||
686 |
30 |
s--; |
|
687 |
✓✓ | 252 |
for (srest = s; srest <= se; srest++) { |
688 |
int matched = 0; |
||
689 |
|||
690 |
144 |
for (psub = p; ; psub = pnext) { |
|
691 |
144 |
pnext = pat_scan(psub, pe, 1); |
|
692 |
✓✓ | 144 |
if (do_gmatch(s, srest, psub, |
693 |
144 |
pnext - 2)) { |
|
694 |
matched = 1; |
||
695 |
24 |
break; |
|
696 |
} |
||
697 |
✓✓ | 120 |
if (pnext == prest) |
698 |
break; |
||
699 |
} |
||
700 |
✓✓✓✓ |
180 |
if (!matched && |
701 |
78 |
do_gmatch(srest, se, prest, pe)) |
|
702 |
6 |
return 1; |
|
703 |
✓✓ | 96 |
} |
704 |
24 |
return 0; |
|
705 |
|||
706 |
default: |
||
707 |
✓✓ | 158735 |
if (sc != p[-1]) |
708 |
512 |
return 0; |
|
709 |
break; |
||
710 |
} |
||
711 |
} |
||
712 |
17137996 |
return s == se; |
|
713 |
53902651 |
} |
|
714 |
|||
715 |
static int |
||
716 |
posix_cclass(const unsigned char *pattern, int test, const unsigned char **ep) |
||
717 |
{ |
||
718 |
struct cclass *cc; |
||
719 |
const unsigned char *colon; |
||
720 |
size_t len; |
||
721 |
int rval = 0; |
||
722 |
|||
723 |
✓✗✗✓ |
9209424 |
if ((colon = strchr(pattern, ':')) == NULL || colon[1] != MAGIC) { |
724 |
*ep = pattern - 2; |
||
725 |
return -1; |
||
726 |
} |
||
727 |
3069808 |
*ep = colon + 3; /* skip MAGIC */ |
|
728 |
3069808 |
len = (size_t)(colon - pattern); |
|
729 |
|||
730 |
✓✗ | 18417912 |
for (cc = cclasses; cc->name != NULL; cc++) { |
731 |
✓✓✓✗ |
12278764 |
if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') { |
732 |
✓✓ | 3069808 |
if (cc->isctype(test)) |
733 |
665352 |
rval = 1; |
|
734 |
break; |
||
735 |
} |
||
736 |
} |
||
737 |
✗✓ | 3069808 |
if (cc->name == NULL) { |
738 |
rval = -2; /* invalid character class */ |
||
739 |
} |
||
740 |
3069808 |
return rval; |
|
741 |
3069808 |
} |
|
742 |
|||
743 |
static const unsigned char * |
||
744 |
cclass(const unsigned char *p, int sub) |
||
745 |
{ |
||
746 |
int c, d, rv, not, found = 0; |
||
747 |
const unsigned char *orig_p = p; |
||
748 |
|||
749 |
✓✓✓✓ |
52214800 |
if ((not = (ISMAGIC(*p) && *++p == '!'))) |
750 |
14319512 |
p++; |
|
751 |
17412842 |
do { |
|
752 |
/* check for POSIX character class (e.g. [[:alpha:]]) */ |
||
753 |
✓✓✓✓ ✗✓✓✗ |
20510816 |
if ((p[0] == MAGIC && p[1] == '[' && p[2] == ':') || |
754 |
✓✓ | 20509922 |
(p[0] == '[' && p[1] == ':')) { |
755 |
3069808 |
do { |
|
756 |
3735160 |
const char *pp = p + (*p == MAGIC) + 2; |
|
757 |
3735160 |
rv = posix_cclass(pp, sub, &p); |
|
758 |
✓✗✓ | 3735160 |
switch (rv) { |
759 |
case 1: |
||
760 |
found = 1; |
||
761 |
665352 |
break; |
|
762 |
case -2: |
||
763 |
return NULL; |
||
764 |
} |
||
765 |
✓✗✓✗ ✓✗✗✓ ✗✗ |
12279232 |
} while (rv != -1 && p[0] == MAGIC && p[1] == '[' && p[2] == ':'); |
766 |
✓✗✗✓ |
6139616 |
if (p[0] == MAGIC && p[1] == ']') |
767 |
break; |
||
768 |
} |
||
769 |
|||
770 |
14370810 |
c = *p++; |
|
771 |
✓✓ | 14370810 |
if (ISMAGIC(c)) { |
772 |
138 |
c = *p++; |
|
773 |
✓✓✓✗ |
156 |
if ((c & 0x80) && !ISMAGIC(c)) { |
774 |
18 |
c &= 0x7f;/* extended pattern matching: *+?@! */ |
|
775 |
/* XXX the ( char isn't handled as part of [] */ |
||
776 |
18 |
if (c == ' ') /* simile for @: plain (..) */ |
|
777 |
c = '(' /*)*/; |
||
778 |
18 |
} |
|
779 |
} |
||
780 |
✗✓ | 14370810 |
if (c == '\0') |
781 |
/* No closing ] - act as if the opening [ was quoted */ |
||
782 |
return sub == '[' ? orig_p : NULL; |
||
783 |
✓✓✓✓ ✓✓ |
28714240 |
if (ISMAGIC(p[0]) && p[1] == '-' && |
784 |
✓✓ | 180 |
(!ISMAGIC(p[2]) || p[3] != ']')) { |
785 |
126 |
p += 2; /* MAGIC- */ |
|
786 |
126 |
d = *p++; |
|
787 |
✓✓ | 126 |
if (ISMAGIC(d)) { |
788 |
6 |
d = *p++; |
|
789 |
✗✓✗✗ |
6 |
if ((d & 0x80) && !ISMAGIC(d)) |
790 |
d &= 0x7f; |
||
791 |
} |
||
792 |
/* POSIX says this is an invalid expression */ |
||
793 |
✓✓ | 126 |
if (c > d) |
794 |
6 |
return NULL; |
|
795 |
} else |
||
796 |
d = c; |
||
797 |
✓✓✓✓ ✓✓ |
41267121 |
if (c == sub || (c <= sub && sub <= d)) |
798 |
891359 |
found = 1; |
|
799 |
✓✓✓✓ |
43085002 |
} while (!(ISMAGIC(p[0]) && p[1] == ']')); |
800 |
|||
801 |
17412836 |
return (found != not) ? p+2 : NULL; |
|
802 |
17412842 |
} |
|
803 |
|||
804 |
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ |
||
805 |
const unsigned char * |
||
806 |
pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep) |
||
807 |
{ |
||
808 |
int nest = 0; |
||
809 |
|||
810 |
✓✗ | 29908686 |
for (; p < pe; p++) { |
811 |
✓✓ | 14056366 |
if (!ISMAGIC(*p)) |
812 |
continue; |
||
813 |
✓✓✓✓ |
9954446 |
if ((*++p == /*(*/ ')' && nest-- == 0) || |
814 |
✓✓ | 6112290 |
(*p == '|' && match_sep && nest == 0)) |
815 |
1795954 |
return ++p; |
|
816 |
✓✓✓✗ |
6369276 |
if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f)) |
817 |
263724 |
nest++; |
|
818 |
} |
||
819 |
return NULL; |
||
820 |
1795954 |
} |
|
821 |
|||
822 |
/* |
||
823 |
* quick sort of array of generic pointers to objects. |
||
824 |
*/ |
||
825 |
void |
||
826 |
qsortp(void **base, /* base address */ |
||
827 |
size_t n, /* elements */ |
||
828 |
int (*f) (const void *, const void *)) /* compare function */ |
||
829 |
{ |
||
830 |
2669890 |
qsort(base, n, sizeof(char *), f); |
|
831 |
1334945 |
} |
|
832 |
|||
833 |
int |
||
834 |
xstrcmp(const void *p1, const void *p2) |
||
835 |
{ |
||
836 |
46925280 |
return (strcmp(*(char **)p1, *(char **)p2)); |
|
837 |
} |
||
838 |
|||
839 |
/* Initialize a Getopt structure */ |
||
840 |
void |
||
841 |
ksh_getopt_reset(Getopt *go, int flags) |
||
842 |
{ |
||
843 |
7250000 |
go->optind = 1; |
|
844 |
3625000 |
go->optarg = NULL; |
|
845 |
3625000 |
go->p = 0; |
|
846 |
3625000 |
go->flags = flags; |
|
847 |
3625000 |
go->info = 0; |
|
848 |
3625000 |
go->buf[1] = '\0'; |
|
849 |
3625000 |
} |
|
850 |
|||
851 |
|||
852 |
/* getopt() used for shell built-in commands, the getopts command, and |
||
853 |
* command line options. |
||
854 |
* A leading ':' in options means don't print errors, instead return '?' |
||
855 |
* or ':' and set go->optarg to the offending option character. |
||
856 |
* If GF_ERROR is set (and option doesn't start with :), errors result in |
||
857 |
* a call to bi_errorf(). |
||
858 |
* |
||
859 |
* Non-standard features: |
||
860 |
* - ';' is like ':' in options, except the argument is optional |
||
861 |
* (if it isn't present, optarg is set to 0). |
||
862 |
* Used for 'set -o'. |
||
863 |
* - ',' is like ':' in options, except the argument always immediately |
||
864 |
* follows the option character (optarg is set to the null string if |
||
865 |
* the option is missing). |
||
866 |
* Used for 'read -u2', 'print -u2' and fc -40. |
||
867 |
* - '#' is like ':' in options, expect that the argument is optional |
||
868 |
* and must start with a digit or be the string "unlimited". If the |
||
869 |
* argument doesn't match, it is assumed to be missing and normal option |
||
870 |
* processing continues (optarg is set to 0 if the option is missing). |
||
871 |
* Used for 'typeset -LZ4' and 'ulimit -adunlimited'. |
||
872 |
* - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an |
||
873 |
* option starting with + is accepted, the GI_PLUS flag will be set |
||
874 |
* in go->info. |
||
875 |
*/ |
||
876 |
int |
||
877 |
ksh_getopt(char **argv, Getopt *go, const char *options) |
||
878 |
{ |
||
879 |
char c; |
||
880 |
char *o; |
||
881 |
|||
882 |
✓✓✓✓ |
5731741 |
if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { |
883 |
✓✓ | 6097467 |
char *arg = argv[go->optind], flag = arg ? *arg : '\0'; |
884 |
|||
885 |
2099787 |
go->p = 1; |
|
886 |
✓✓✓✓ ✓✗ |
2834746 |
if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { |
887 |
257 |
go->optind++; |
|
888 |
257 |
go->p = 0; |
|
889 |
257 |
go->info |= GI_MINUSMINUS; |
|
890 |
257 |
return -1; |
|
891 |
} |
||
892 |
✓✓✓✓ |
2834724 |
if (arg == NULL || |
893 |
✓✓ | 1897636 |
((flag != '-' ) && /* neither a - nor a + (if + allowed) */ |
894 |
✓✓✓✓ |
2065368 |
(!(go->flags & GF_PLUSOPT) || flag != '+')) || |
895 |
735194 |
(c = arg[1]) == '\0') { |
|
896 |
1364592 |
go->p = 0; |
|
897 |
1364592 |
return -1; |
|
898 |
} |
||
899 |
734938 |
go->optind++; |
|
900 |
734938 |
go->info &= ~(GI_MINUS|GI_PLUS); |
|
901 |
734938 |
go->info |= flag == '-' ? GI_MINUS : GI_PLUS; |
|
902 |
✓✓ | 734938 |
} |
903 |
1001479 |
go->p++; |
|
904 |
✓✗✓✗ ✓✗✓✗ ✓✗✓✓ |
6008874 |
if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || |
905 |
1001479 |
!(o = strchr(options, c))) { |
|
906 |
✓✗ | 150 |
if (options[0] == ':') { |
907 |
150 |
go->buf[0] = c; |
|
908 |
150 |
go->optarg = go->buf; |
|
909 |
150 |
} else { |
|
910 |
warningf(true, "%s%s-%c: unknown option", |
||
911 |
(go->flags & GF_NONAME) ? "" : argv[0], |
||
912 |
(go->flags & GF_NONAME) ? "" : ": ", c); |
||
913 |
if (go->flags & GF_ERROR) |
||
914 |
bi_errorf(NULL); |
||
915 |
} |
||
916 |
150 |
return '?'; |
|
917 |
} |
||
918 |
/* : means argument must be present, may be part of option argument |
||
919 |
* or the next argument |
||
920 |
* ; same as : but argument may be missing |
||
921 |
* , means argument is part of option argument, and may be null. |
||
922 |
*/ |
||
923 |
✓✓✓✓ |
2002420 |
if (*++o == ':' || *o == ';') { |
924 |
✗✓ | 250 |
if (argv[go->optind - 1][go->p]) |
925 |
go->optarg = argv[go->optind - 1] + go->p; |
||
926 |
✓✗ | 250 |
else if (argv[go->optind]) |
927 |
250 |
go->optarg = argv[go->optind++]; |
|
928 |
else if (*o == ';') |
||
929 |
go->optarg = NULL; |
||
930 |
else { |
||
931 |
if (options[0] == ':') { |
||
932 |
go->buf[0] = c; |
||
933 |
go->optarg = go->buf; |
||
934 |
return ':'; |
||
935 |
} |
||
936 |
warningf(true, "%s%s-`%c' requires argument", |
||
937 |
(go->flags & GF_NONAME) ? "" : argv[0], |
||
938 |
(go->flags & GF_NONAME) ? "" : ": ", c); |
||
939 |
if (go->flags & GF_ERROR) |
||
940 |
bi_errorf(NULL); |
||
941 |
return '?'; |
||
942 |
} |
||
943 |
250 |
go->p = 0; |
|
944 |
✓✓ | 1001329 |
} else if (*o == ',') { |
945 |
/* argument is attached to option character, even if null */ |
||
946 |
48 |
go->optarg = argv[go->optind - 1] + go->p; |
|
947 |
48 |
go->p = 0; |
|
948 |
✓✓ | 1001079 |
} else if (*o == '#') { |
949 |
/* argument is optional and may be attached or unattached |
||
950 |
* but must start with a digit. optarg is set to 0 if the |
||
951 |
* argument is missing. |
||
952 |
*/ |
||
953 |
✓✓ | 316445 |
if (argv[go->optind - 1][go->p]) { |
954 |
✓✓✗✓ |
209592 |
if (digit(argv[go->optind - 1][go->p]) || |
955 |
104763 |
!strcmp(&argv[go->optind - 1][go->p], "unlimited")) { |
|
956 |
66 |
go->optarg = argv[go->optind - 1] + go->p; |
|
957 |
66 |
go->p = 0; |
|
958 |
66 |
} else |
|
959 |
104763 |
go->optarg = NULL; |
|
960 |
} else { |
||
961 |
✓✓✓✓ ✗✓ |
632932 |
if (argv[go->optind] && (digit(argv[go->optind][0]) || |
962 |
209718 |
!strcmp(argv[go->optind], "unlimited"))) { |
|
963 |
1880 |
go->optarg = argv[go->optind++]; |
|
964 |
1880 |
go->p = 0; |
|
965 |
1880 |
} else |
|
966 |
209736 |
go->optarg = NULL; |
|
967 |
} |
||
968 |
} |
||
969 |
1001329 |
return c; |
|
970 |
2366328 |
} |
|
971 |
|||
972 |
/* print variable/alias value using necessary quotes |
||
973 |
* (POSIX says they should be suitable for re-entry...) |
||
974 |
* No trailing newline is printed. |
||
975 |
*/ |
||
976 |
void |
||
977 |
print_value_quoted(const char *s) |
||
978 |
{ |
||
979 |
const char *p; |
||
980 |
int inquote = 0; |
||
981 |
|||
982 |
/* Test if any quotes are needed */ |
||
983 |
✓✓ | 5202 |
for (p = s; *p; p++) |
984 |
✓✓ | 2340 |
if (ctype(*p, C_QUOTE)) |
985 |
break; |
||
986 |
✓✓ | 270 |
if (!*p) { |
987 |
126 |
shprintf("%s", s); |
|
988 |
126 |
return; |
|
989 |
} |
||
990 |
✓✓ | 2268 |
for (p = s; *p; p++) { |
991 |
✗✓ | 990 |
if (*p == '\'') { |
992 |
shprintf(inquote ? "'\\'" : "\\'"); |
||
993 |
inquote = 0; |
||
994 |
} else { |
||
995 |
✓✓ | 990 |
if (!inquote) { |
996 |
144 |
shprintf("'"); |
|
997 |
inquote = 1; |
||
998 |
144 |
} |
|
999 |
✗✓ | 1980 |
shf_putc(*p, shl_stdout); |
1000 |
} |
||
1001 |
} |
||
1002 |
✓✗ | 144 |
if (inquote) |
1003 |
144 |
shprintf("'"); |
|
1004 |
414 |
} |
|
1005 |
|||
1006 |
/* Print things in columns and rows - func() is called to format the ith |
||
1007 |
* element |
||
1008 |
*/ |
||
1009 |
void |
||
1010 |
print_columns(struct shf *shf, int n, char *(*func) (void *, int, char *, int), |
||
1011 |
void *arg, int max_width, int prefcol) |
||
1012 |
{ |
||
1013 |
char *str = alloc(max_width + 1, ATEMP); |
||
1014 |
int i; |
||
1015 |
int r, c; |
||
1016 |
int rows, cols; |
||
1017 |
int nspace; |
||
1018 |
int col_width; |
||
1019 |
|||
1020 |
/* max_width + 1 for the space. Note that no space |
||
1021 |
* is printed after the last column to avoid problems |
||
1022 |
* with terminals that have auto-wrap. |
||
1023 |
*/ |
||
1024 |
cols = x_cols / (max_width + 1); |
||
1025 |
if (!cols) |
||
1026 |
cols = 1; |
||
1027 |
rows = (n + cols - 1) / cols; |
||
1028 |
if (prefcol && n && cols > rows) { |
||
1029 |
int tmp = rows; |
||
1030 |
|||
1031 |
rows = cols; |
||
1032 |
cols = tmp; |
||
1033 |
if (rows > n) |
||
1034 |
rows = n; |
||
1035 |
} |
||
1036 |
|||
1037 |
col_width = max_width; |
||
1038 |
if (cols == 1) |
||
1039 |
col_width = 0; /* Don't pad entries in single column output. */ |
||
1040 |
nspace = (x_cols - max_width * cols) / cols; |
||
1041 |
if (nspace <= 0) |
||
1042 |
nspace = 1; |
||
1043 |
for (r = 0; r < rows; r++) { |
||
1044 |
for (c = 0; c < cols; c++) { |
||
1045 |
i = c * rows + r; |
||
1046 |
if (i < n) { |
||
1047 |
shf_fprintf(shf, "%-*s", |
||
1048 |
col_width, |
||
1049 |
(*func)(arg, i, str, max_width + 1)); |
||
1050 |
if (c + 1 < cols) |
||
1051 |
shf_fprintf(shf, "%*s", nspace, ""); |
||
1052 |
} |
||
1053 |
} |
||
1054 |
shf_putchar('\n', shf); |
||
1055 |
} |
||
1056 |
afree(str, ATEMP); |
||
1057 |
} |
||
1058 |
|||
1059 |
/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ |
||
1060 |
int |
||
1061 |
strip_nuls(char *buf, int nbytes) |
||
1062 |
{ |
||
1063 |
char *dst; |
||
1064 |
|||
1065 |
✓✓ | 3041496 |
if ((dst = memchr(buf, '\0', nbytes))) { |
1066 |
5 |
char *end = buf + nbytes; |
|
1067 |
char *p, *q; |
||
1068 |
|||
1069 |
✓✓ | 20 |
for (p = dst; p < end; p = q) { |
1070 |
/* skip a block of nulls */ |
||
1071 |
✓✗✗✓ |
15 |
while (++p < end && *p == '\0') |
1072 |
; |
||
1073 |
/* find end of non-null block */ |
||
1074 |
✓✗ | 5 |
if (!(q = memchr(p, '\0', end - p))) |
1075 |
5 |
q = end; |
|
1076 |
5 |
memmove(dst, p, q - p); |
|
1077 |
5 |
dst += q - p; |
|
1078 |
} |
||
1079 |
5 |
*dst = '\0'; |
|
1080 |
5 |
return dst - buf; |
|
1081 |
} |
||
1082 |
1520743 |
return nbytes; |
|
1083 |
1520748 |
} |
|
1084 |
|||
1085 |
/* Like read(2), but if read fails due to non-blocking flag, resets flag |
||
1086 |
* and restarts read. |
||
1087 |
*/ |
||
1088 |
int |
||
1089 |
blocking_read(int fd, char *buf, int nbytes) |
||
1090 |
{ |
||
1091 |
int ret; |
||
1092 |
int tried_reset = 0; |
||
1093 |
|||
1094 |
✓✓ | 23690476 |
while ((ret = read(fd, buf, nbytes)) < 0) { |
1095 |
✗✓ | 8511113 |
if (!tried_reset && errno == EAGAIN) { |
1096 |
int oerrno = errno; |
||
1097 |
if (reset_nonblock(fd) > 0) { |
||
1098 |
tried_reset = 1; |
||
1099 |
continue; |
||
1100 |
} |
||
1101 |
errno = oerrno; |
||
1102 |
} |
||
1103 |
break; |
||
1104 |
} |
||
1105 |
5922619 |
return ret; |
|
1106 |
} |
||
1107 |
|||
1108 |
/* Reset the non-blocking flag on the specified file descriptor. |
||
1109 |
* Returns -1 if there was an error, 0 if non-blocking wasn't set, |
||
1110 |
* 1 if it was. |
||
1111 |
*/ |
||
1112 |
int |
||
1113 |
reset_nonblock(int fd) |
||
1114 |
{ |
||
1115 |
int flags; |
||
1116 |
|||
1117 |
✗✓ | 1316 |
if ((flags = fcntl(fd, F_GETFL)) < 0) |
1118 |
return -1; |
||
1119 |
✓✗ | 658 |
if (!(flags & O_NONBLOCK)) |
1120 |
658 |
return 0; |
|
1121 |
flags &= ~O_NONBLOCK; |
||
1122 |
if (fcntl(fd, F_SETFL, flags) < 0) |
||
1123 |
return -1; |
||
1124 |
return 1; |
||
1125 |
658 |
} |
|
1126 |
|||
1127 |
|||
1128 |
/* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */ |
||
1129 |
char * |
||
1130 |
ksh_get_wd(char *buf, int bsize) |
||
1131 |
{ |
||
1132 |
char *b; |
||
1133 |
char *ret; |
||
1134 |
|||
1135 |
/* Note: we could just use plain getcwd(), but then we'd had to |
||
1136 |
* inject possibly allocated space into the ATEMP area. */ |
||
1137 |
/* Assume getcwd() available */ |
||
1138 |
✓✗ | 4012 |
if (!buf) { |
1139 |
bsize = PATH_MAX; |
||
1140 |
2006 |
b = alloc(PATH_MAX + 1, ATEMP); |
|
1141 |
2006 |
} else |
|
1142 |
b = buf; |
||
1143 |
|||
1144 |
2006 |
ret = getcwd(b, bsize); |
|
1145 |
|||
1146 |
✓✗ | 2006 |
if (!buf) { |
1147 |
✓✗ | 2006 |
if (ret) |
1148 |
2006 |
ret = aresize(b, strlen(b) + 1, ATEMP); |
|
1149 |
else |
||
1150 |
afree(b, ATEMP); |
||
1151 |
} |
||
1152 |
|||
1153 |
2006 |
return ret; |
|
1154 |
} |
Generated by: GCOVR (Version 3.3) |