GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: misc.c,v 1.59 2017/08/30 17:15:36 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 |
✓✓ | 3474060 |
if (t & C_IFS) { |
33 |
✓✓ | 221230740 |
for (i = 0; i < UCHAR_MAX+1; i++) |
34 |
110184960 |
ctypes[i] &= ~C_IFS; |
|
35 |
430410 |
ctypes[0] |= C_IFS; /* include \0 in C_IFS */ |
|
36 |
430410 |
} |
|
37 |
✓✓ | 23709910 |
while (*s != 0) |
38 |
10986440 |
ctypes[(unsigned char) *s++] |= t; |
|
39 |
1737030 |
} |
|
40 |
|||
41 |
void |
||
42 |
initctypes(void) |
||
43 |
{ |
||
44 |
int c; |
||
45 |
|||
46 |
✓✓ | 11977350 |
for (c = 'a'; c <= 'z'; c++) |
47 |
5662020 |
ctypes[c] |= C_ALPHA; |
|
48 |
✓✓ | 11759580 |
for (c = 'A'; c <= 'Z'; c++) |
49 |
5662020 |
ctypes[c] |= C_ALPHA; |
|
50 |
217770 |
ctypes['_'] |= C_ALPHA; |
|
51 |
217770 |
setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */ |
|
52 |
217770 |
setctypes("*@#!$-?", C_VAR1); |
|
53 |
217770 |
setctypes(" \t\n", C_IFSWS); |
|
54 |
217770 |
setctypes("=-+?", C_SUBOP1); |
|
55 |
217770 |
setctypes("#%", C_SUBOP2); |
|
56 |
217770 |
setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE); |
|
57 |
217770 |
} |
|
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 |
290 |
*--p = '\0'; |
|
69 |
145 |
do { |
|
70 |
145 |
*--p = "0123456789ABCDEF"[n%base]; |
|
71 |
145 |
n /= base; |
|
72 |
✗✓ | 145 |
} while (n != 0); |
73 |
145 |
return p; |
|
74 |
} |
||
75 |
|||
76 |
char * |
||
77 |
str_save(const char *s, Area *ap) |
||
78 |
{ |
||
79 |
size_t len; |
||
80 |
char *p; |
||
81 |
|||
82 |
✓✓ | 43583030 |
if (!s) |
83 |
3029614 |
return NULL; |
|
84 |
18761901 |
len = strlen(s)+1; |
|
85 |
18761901 |
p = alloc(len, ap); |
|
86 |
18761901 |
strlcpy(p, s, len); |
|
87 |
18761901 |
return (p); |
|
88 |
21791515 |
} |
|
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 |
✗✓ | 53998866 |
if (n < 0) |
100 |
return 0; |
||
101 |
26999433 |
ns = alloc(n + 1, ap); |
|
102 |
26999433 |
ns[0] = '\0'; |
|
103 |
26999433 |
return strncat(ns, s, n); |
|
104 |
26999433 |
} |
|
105 |
|||
106 |
/* called from expand.h:XcheckN() to grow buffer */ |
||
107 |
char * |
||
108 |
Xcheck_grow_(XString *xsp, char *xp, int more) |
||
109 |
{ |
||
110 |
6777866 |
char *old_beg = xsp->beg; |
|
111 |
|||
112 |
✓✓ | 10166799 |
xsp->len += more > xsp->len ? more : xsp->len; |
113 |
3388933 |
xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap); |
|
114 |
3388933 |
xsp->end = xsp->beg + xsp->len; |
|
115 |
3388933 |
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 |
{ "emacs-usemeta", 0, OF_ANY }, /* XXX delete after 6.2 */ |
||
133 |
#endif |
||
134 |
{ "errexit", 'e', OF_ANY }, |
||
135 |
#ifdef EMACS |
||
136 |
{ "gmacs", 0, OF_ANY }, |
||
137 |
#endif |
||
138 |
{ "ignoreeof", 0, OF_ANY }, |
||
139 |
{ "interactive",'i', OF_CMDLINE }, |
||
140 |
{ "keyword", 'k', OF_ANY }, |
||
141 |
{ "login", 'l', OF_CMDLINE }, |
||
142 |
{ "markdirs", 'X', OF_ANY }, |
||
143 |
#ifdef JOBS |
||
144 |
{ "monitor", 'm', OF_ANY }, |
||
145 |
#else /* JOBS */ |
||
146 |
{ NULL, 'm', 0 }, /* so FMONITOR not ifdef'd */ |
||
147 |
#endif /* JOBS */ |
||
148 |
{ "noclobber", 'C', OF_ANY }, |
||
149 |
{ "noexec", 'n', OF_ANY }, |
||
150 |
{ "noglob", 'f', OF_ANY }, |
||
151 |
{ "nohup", 0, OF_ANY }, |
||
152 |
{ "nolog", 0, OF_ANY }, /* no effect */ |
||
153 |
#ifdef JOBS |
||
154 |
{ "notify", 'b', OF_ANY }, |
||
155 |
#endif /* JOBS */ |
||
156 |
{ "nounset", 'u', OF_ANY }, |
||
157 |
{ "physical", 0, OF_ANY }, /* non-standard */ |
||
158 |
{ "posix", 0, OF_ANY }, /* non-standard */ |
||
159 |
{ "privileged", 'p', OF_ANY }, |
||
160 |
{ "restricted", 'r', OF_CMDLINE }, |
||
161 |
{ "sh", 0, OF_ANY }, /* non-standard */ |
||
162 |
{ "stdin", 's', OF_CMDLINE }, /* pseudo non-standard */ |
||
163 |
{ "trackall", 'h', OF_ANY }, |
||
164 |
{ "verbose", 'v', OF_ANY }, |
||
165 |
#ifdef VI |
||
166 |
{ "vi", 0, OF_ANY }, |
||
167 |
{ "viraw", 0, OF_ANY }, /* no effect */ |
||
168 |
{ "vi-show8", 0, OF_ANY }, /* non-standard */ |
||
169 |
{ "vi-tabcomplete", 0, OF_ANY }, /* non-standard */ |
||
170 |
{ "vi-esccomplete", 0, OF_ANY }, /* non-standard */ |
||
171 |
#endif |
||
172 |
{ "xtrace", 'x', OF_ANY }, |
||
173 |
/* Anonymous flags: used internally by shell only |
||
174 |
* (not visible to user) |
||
175 |
*/ |
||
176 |
{ NULL, 0, OF_INTERNAL }, /* FTALKING_I */ |
||
177 |
}; |
||
178 |
|||
179 |
/* |
||
180 |
* translate -o option into F* constant (also used for test -o option) |
||
181 |
*/ |
||
182 |
int |
||
183 |
option(const char *n) |
||
184 |
{ |
||
185 |
int i; |
||
186 |
|||
187 |
✓✗ | 158437 |
for (i = 0; i < NELEM(options); i++) |
188 |
✓✓✓✓ |
151936 |
if (options[i].name && strcmp(options[i].name, n) == 0) { |
189 |
#ifdef EMACS |
||
190 |
✗✓ | 3263 |
if (i == FEMACSUSEMETA) |
191 |
warningf(true, "%s: deprecated option", n); |
||
192 |
#endif |
||
193 |
3263 |
return i; |
|
194 |
} |
||
195 |
|||
196 |
return -1; |
||
197 |
3263 |
} |
|
198 |
|||
199 |
struct options_info { |
||
200 |
int opt_width; |
||
201 |
struct { |
||
202 |
const char *name; |
||
203 |
int flag; |
||
204 |
} opts[NELEM(options)]; |
||
205 |
}; |
||
206 |
|||
207 |
static char *options_fmt_entry(void *arg, int i, char *buf, int buflen); |
||
208 |
static void printoptions(int verbose); |
||
209 |
|||
210 |
/* format a single select menu item */ |
||
211 |
static char * |
||
212 |
options_fmt_entry(void *arg, int i, char *buf, int buflen) |
||
213 |
{ |
||
214 |
2652 |
struct options_info *oi = (struct options_info *) arg; |
|
215 |
|||
216 |
1326 |
shf_snprintf(buf, buflen, "%-*s %s", |
|
217 |
1326 |
oi->opt_width, oi->opts[i].name, |
|
218 |
1326 |
Flag(oi->opts[i].flag) ? "on" : "off"); |
|
219 |
1326 |
return buf; |
|
220 |
} |
||
221 |
|||
222 |
static void |
||
223 |
printoptions(int verbose) |
||
224 |
{ |
||
225 |
int i; |
||
226 |
|||
227 |
✓✗ | 78 |
if (verbose) { |
228 |
39 |
struct options_info oi; |
|
229 |
int n, len; |
||
230 |
|||
231 |
/* verbose version */ |
||
232 |
39 |
shprintf("Current option settings\n"); |
|
233 |
|||
234 |
✓✓ | 2964 |
for (i = n = oi.opt_width = 0; i < NELEM(options); i++) { |
235 |
#ifdef EMACS |
||
236 |
✓✓ | 1443 |
if (i == FEMACSUSEMETA) |
237 |
continue; |
||
238 |
#endif |
||
239 |
✓✓ | 1404 |
if (options[i].name) { |
240 |
1326 |
len = strlen(options[i].name); |
|
241 |
1326 |
oi.opts[n].name = options[i].name; |
|
242 |
1326 |
oi.opts[n++].flag = i; |
|
243 |
✓✓ | 1326 |
if (len > oi.opt_width) |
244 |
117 |
oi.opt_width = len; |
|
245 |
} |
||
246 |
} |
||
247 |
39 |
print_columns(shl_stdout, n, options_fmt_entry, &oi, |
|
248 |
39 |
oi.opt_width + 5, 1); |
|
249 |
39 |
} else { |
|
250 |
/* short version ala ksh93 */ |
||
251 |
shprintf("set"); |
||
252 |
for (i = 0; i < NELEM(options); i++) { |
||
253 |
#ifdef EMACS |
||
254 |
if (i == FEMACSUSEMETA) |
||
255 |
continue; |
||
256 |
#endif |
||
257 |
if (options[i].name) |
||
258 |
shprintf(" %co %s", |
||
259 |
Flag(i) ? '-' : '+', |
||
260 |
options[i].name); |
||
261 |
} |
||
262 |
shprintf("\n"); |
||
263 |
} |
||
264 |
39 |
} |
|
265 |
|||
266 |
char * |
||
267 |
getoptions(void) |
||
268 |
{ |
||
269 |
int i; |
||
270 |
120 |
char m[(int) FNFLAGS + 1]; |
|
271 |
60 |
char *cp = m; |
|
272 |
|||
273 |
✓✓ | 4560 |
for (i = 0; i < NELEM(options); i++) |
274 |
✓✓✓✓ |
3360 |
if (options[i].c && Flag(i)) |
275 |
168 |
*cp++ = options[i].c; |
|
276 |
60 |
*cp = 0; |
|
277 |
120 |
return str_save(m, ATEMP); |
|
278 |
60 |
} |
|
279 |
|||
280 |
/* change a Flag(*) value; takes care of special actions */ |
||
281 |
void |
||
282 |
change_flag(enum sh_flag f, |
||
283 |
int what, /* flag to change */ |
||
284 |
int newval) /* what is changing the flag (command line vs set) */ |
||
285 |
{ |
||
286 |
int oldval; |
||
287 |
|||
288 |
1075552 |
oldval = Flag(f); |
|
289 |
537776 |
Flag(f) = newval; |
|
290 |
#ifdef JOBS |
||
291 |
✗✓ | 537776 |
if (f == FMONITOR) { |
292 |
if (what != OF_CMDLINE && newval != oldval) |
||
293 |
j_change(); |
||
294 |
} else |
||
295 |
#endif /* JOBS */ |
||
296 |
#ifdef EDIT |
||
297 |
if (0 |
||
298 |
# ifdef VI |
||
299 |
537776 |
|| f == FVI |
|
300 |
# endif /* VI */ |
||
301 |
# ifdef EMACS |
||
302 |
✓✓ | 537776 |
|| f == FEMACS || f == FGMACS |
303 |
# endif /* EMACS */ |
||
304 |
) |
||
305 |
{ |
||
306 |
✓✗ | 225995 |
if (newval) { |
307 |
# ifdef VI |
||
308 |
225995 |
Flag(FVI) = 0; |
|
309 |
# endif /* VI */ |
||
310 |
# ifdef EMACS |
||
311 |
225995 |
Flag(FEMACS) = Flag(FGMACS) = 0; |
|
312 |
# endif /* EMACS */ |
||
313 |
225995 |
Flag(f) = newval; |
|
314 |
225995 |
} |
|
315 |
} else |
||
316 |
#endif /* EDIT */ |
||
317 |
/* Turning off -p? */ |
||
318 |
✗✓ | 311781 |
if (f == FPRIVILEGED && oldval && !newval) { |
319 |
gid_t gid = getgid(); |
||
320 |
|||
321 |
setresgid(gid, gid, gid); |
||
322 |
setgroups(1, &gid); |
||
323 |
setresuid(ksheuid, ksheuid, ksheuid); |
||
324 |
✓✓ | 311781 |
} else if (f == FPOSIX && newval) { |
325 |
#ifdef BRACE_EXPAND |
||
326 |
3256 |
Flag(FBRACEEXPAND) = 0 |
|
327 |
#endif /* BRACE_EXPAND */ |
||
328 |
; |
||
329 |
3256 |
} |
|
330 |
/* Changing interactive flag? */ |
||
331 |
✓✓ | 537776 |
if (f == FTALKING) { |
332 |
✓✗✓✗ |
1750 |
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) |
333 |
875 |
Flag(FTALKING_I) = newval; |
|
334 |
} |
||
335 |
537776 |
} |
|
336 |
|||
337 |
/* parse command line & set command arguments. returns the index of |
||
338 |
* non-option arguments, -1 if there is an error. |
||
339 |
*/ |
||
340 |
int |
||
341 |
parse_args(char **argv, |
||
342 |
int what, /* OF_CMDLINE or OF_SET */ |
||
343 |
int *setargsp) |
||
344 |
{ |
||
345 |
static char cmd_opts[NELEM(options) + 3]; /* o:\0 */ |
||
346 |
static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */ |
||
347 |
char *opts; |
||
348 |
char *array = NULL; |
||
349 |
620790 |
Getopt go; |
|
350 |
int i, optc, set, sortargs = 0, arrayset = 0; |
||
351 |
|||
352 |
/* First call? Build option strings... */ |
||
353 |
✓✓ | 310395 |
if (cmd_opts[0] == '\0') { |
354 |
char *p, *q; |
||
355 |
|||
356 |
/* see cmd_opts[] declaration */ |
||
357 |
217770 |
strlcpy(cmd_opts, "o:", sizeof cmd_opts); |
|
358 |
217770 |
p = cmd_opts + strlen(cmd_opts); |
|
359 |
/* see set_opts[] declaration */ |
||
360 |
217770 |
strlcpy(set_opts, "A:o;s", sizeof set_opts); |
|
361 |
217770 |
q = set_opts + strlen(set_opts); |
|
362 |
✓✓ | 16550520 |
for (i = 0; i < NELEM(options); i++) { |
363 |
✓✓ | 8057490 |
if (options[i].c) { |
364 |
✓✗ | 4137630 |
if (options[i].flags & OF_CMDLINE) |
365 |
4137630 |
*p++ = options[i].c; |
|
366 |
✓✓ | 4137630 |
if (options[i].flags & OF_SET) |
367 |
3048780 |
*q++ = options[i].c; |
|
368 |
} |
||
369 |
} |
||
370 |
217770 |
*p = '\0'; |
|
371 |
217770 |
*q = '\0'; |
|
372 |
217770 |
} |
|
373 |
|||
374 |
✓✓ | 310395 |
if (what == OF_CMDLINE) { |
375 |
char *p; |
||
376 |
/* Set FLOGIN before parsing options so user can clear |
||
377 |
* flag using +l. |
||
378 |
*/ |
||
379 |
✓✓ | 435540 |
Flag(FLOGIN) = (argv[0][0] == '-' || |
380 |
✓✓ | 392646 |
((p = strrchr(argv[0], '/')) && *++p == '-')); |
381 |
opts = cmd_opts; |
||
382 |
217770 |
} else |
|
383 |
opts = set_opts; |
||
384 |
310395 |
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); |
|
385 |
✓✓ | 982660 |
while ((optc = ksh_getopt(argv, &go, opts)) != -1) { |
386 |
361870 |
set = (go.info & GI_PLUS) ? 0 : 1; |
|
387 |
✓✓✗✓ |
361870 |
switch (optc) { |
388 |
case 'A': |
||
389 |
50043 |
arrayset = set ? 1 : -1; |
|
390 |
50043 |
array = go.optarg; |
|
391 |
50043 |
break; |
|
392 |
|||
393 |
case 'o': |
||
394 |
✓✓ | 3302 |
if (go.optarg == NULL) { |
395 |
/* lone -o: print options |
||
396 |
* |
||
397 |
* Note that on the command line, -o requires |
||
398 |
* an option (ie, can't get here if what is |
||
399 |
* OF_CMDLINE). |
||
400 |
*/ |
||
401 |
39 |
printoptions(set); |
|
402 |
39 |
break; |
|
403 |
} |
||
404 |
3263 |
i = option(go.optarg); |
|
405 |
✓✗✓✓ |
6526 |
if (i >= 0 && set == Flag(i)) |
406 |
/* Don't check the context if the flag |
||
407 |
* isn't changing - makes "set -o interactive" |
||
408 |
* work if you're already interactive. Needed |
||
409 |
* if the output of "set +o" is to be used. |
||
410 |
*/ |
||
411 |
; |
||
412 |
✓✗✓✗ |
6476 |
else if (i >= 0 && (options[i].flags & what)) |
413 |
3238 |
change_flag((enum sh_flag) i, what, set); |
|
414 |
else { |
||
415 |
bi_errorf("%s: bad option", go.optarg); |
||
416 |
return -1; |
||
417 |
} |
||
418 |
break; |
||
419 |
|||
420 |
case '?': |
||
421 |
return -1; |
||
422 |
|||
423 |
default: |
||
424 |
/* -s: sort positional params (at&t ksh stupidity) */ |
||
425 |
✓✓ | 308525 |
if (what == OF_SET && optc == 's') { |
426 |
sortargs = 1; |
||
427 |
25 |
break; |
|
428 |
} |
||
429 |
✓✗ | 3825032 |
for (i = 0; i < NELEM(options); i++) |
430 |
✓✓✓✗ |
2221016 |
if (optc == options[i].c && |
431 |
308500 |
(what & options[i].flags)) { |
|
432 |
308500 |
change_flag((enum sh_flag) i, what, |
|
433 |
set); |
||
434 |
308500 |
break; |
|
435 |
} |
||
436 |
✓✗ | 308500 |
if (i == NELEM(options)) { |
437 |
internal_errorf(1, "parse_args: `%c'", optc); |
||
438 |
return -1; /* not reached */ |
||
439 |
} |
||
440 |
} |
||
441 |
} |
||
442 |
✓✓✓✓ ✓✗ |
570245 |
if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && |
443 |
✓✓✗✓ |
458379 |
(argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && |
444 |
323 |
argv[go.optind][1] == '\0') { |
|
445 |
/* lone - clears -v and -x flags */ |
||
446 |
✓✗ | 323 |
if (argv[go.optind][0] == '-' && !Flag(FPOSIX)) |
447 |
323 |
Flag(FVERBOSE) = Flag(FXTRACE) = 0; |
|
448 |
/* set skips lone - or + option */ |
||
449 |
323 |
go.optind++; |
|
450 |
323 |
} |
|
451 |
✓✓ | 310395 |
if (setargsp) |
452 |
/* -- means set $#/$* even if there are no arguments */ |
||
453 |
✓✓✓✓ |
269504 |
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || |
454 |
41672 |
argv[go.optind]); |
|
455 |
|||
456 |
✓✓✓✗ ✗✓ |
410481 |
if (arrayset && (!*array || *skip_varname(array, false))) { |
457 |
bi_errorf("%s: is not an identifier", array); |
||
458 |
return -1; |
||
459 |
} |
||
460 |
✓✓ | 310395 |
if (sortargs) { |
461 |
✓✓ | 250 |
for (i = go.optind; argv[i]; i++) |
462 |
; |
||
463 |
25 |
qsortp((void **) &argv[go.optind], (size_t) (i - go.optind), |
|
464 |
xstrcmp); |
||
465 |
25 |
} |
|
466 |
✓✓ | 310395 |
if (arrayset) { |
467 |
50043 |
set_array(array, arrayset, argv + go.optind); |
|
468 |
✓✓ | 213586 |
for (; argv[go.optind]; go.optind++) |
469 |
; |
||
470 |
} |
||
471 |
|||
472 |
310395 |
return go.optind; |
|
473 |
310395 |
} |
|
474 |
|||
475 |
/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */ |
||
476 |
int |
||
477 |
getn(const char *as, int *ai) |
||
478 |
{ |
||
479 |
648570 |
char *p; |
|
480 |
long n; |
||
481 |
|||
482 |
324285 |
n = strtol(as, &p, 10); |
|
483 |
|||
484 |
✓✗✓✓ |
648570 |
if (!*as || *p || INT_MIN >= n || n >= INT_MAX) |
485 |
150 |
return 0; |
|
486 |
|||
487 |
324135 |
*ai = (int)n; |
|
488 |
324135 |
return 1; |
|
489 |
324285 |
} |
|
490 |
|||
491 |
/* getn() that prints error */ |
||
492 |
int |
||
493 |
bi_getn(const char *as, int *ai) |
||
494 |
{ |
||
495 |
12070 |
int rv = getn(as, ai); |
|
496 |
|||
497 |
✓✓ | 6035 |
if (!rv) |
498 |
50 |
bi_errorf("%s: bad number", as); |
|
499 |
5985 |
return rv; |
|
500 |
} |
||
501 |
|||
502 |
/* -------- gmatch.c -------- */ |
||
503 |
|||
504 |
/* |
||
505 |
* int gmatch(string, pattern) |
||
506 |
* char *string, *pattern; |
||
507 |
* |
||
508 |
* Match a pattern as in sh(1). |
||
509 |
* pattern character are prefixed with MAGIC by expand. |
||
510 |
*/ |
||
511 |
|||
512 |
int |
||
513 |
gmatch(const char *s, const char *p, int isfile) |
||
514 |
{ |
||
515 |
const char *se, *pe; |
||
516 |
|||
517 |
✗✓ | 34957962 |
if (s == NULL || p == NULL) |
518 |
return 0; |
||
519 |
17478981 |
se = s + strlen(s); |
|
520 |
17478981 |
pe = p + strlen(p); |
|
521 |
/* isfile is false iff no syntax check has been done on |
||
522 |
* the pattern. If check fails, just to a strcmp(). |
||
523 |
*/ |
||
524 |
✓✓✓✓ |
26860663 |
if (!isfile && !has_globbing(p, pe)) { |
525 |
3905897 |
int len = pe - p + 1; |
|
526 |
3905897 |
char tbuf[64]; |
|
527 |
✓✗ | 11717691 |
char *t = len <= sizeof(tbuf) ? tbuf : |
528 |
alloc(len, ATEMP); |
||
529 |
3905897 |
debunk(t, p, len); |
|
530 |
3905897 |
return !strcmp(t, s); |
|
531 |
3905897 |
} |
|
532 |
13573084 |
return do_gmatch((const unsigned char *) s, (const unsigned char *) se, |
|
533 |
(const unsigned char *) p, (const unsigned char *) pe); |
||
534 |
17478981 |
} |
|
535 |
|||
536 |
/* Returns if p is a syntacticly correct globbing pattern, false |
||
537 |
* if it contains no pattern characters or if there is a syntax error. |
||
538 |
* Syntax errors are: |
||
539 |
* - [ with no closing ] |
||
540 |
* - imbalanced $(...) expression |
||
541 |
* - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d)) |
||
542 |
*/ |
||
543 |
/*XXX |
||
544 |
- if no magic, |
||
545 |
if dest given, copy to dst |
||
546 |
return ? |
||
547 |
- if magic && (no globbing || syntax error) |
||
548 |
debunk to dst |
||
549 |
return ? |
||
550 |
- return ? |
||
551 |
*/ |
||
552 |
int |
||
553 |
has_globbing(const char *xp, const char *xpe) |
||
554 |
{ |
||
555 |
const unsigned char *p = (const unsigned char *) xp; |
||
556 |
const unsigned char *pe = (const unsigned char *) xpe; |
||
557 |
int c; |
||
558 |
int nest = 0, bnest = 0; |
||
559 |
int saw_glob = 0; |
||
560 |
int in_bracket = 0; /* inside [...] */ |
||
561 |
|||
562 |
✓✓ | 233400929 |
for (; p < pe; p++) { |
563 |
✓✓ | 98150032 |
if (!ISMAGIC(*p)) |
564 |
continue; |
||
565 |
✓✓ | 35807588 |
if ((c = *++p) == '*' || c == '?') |
566 |
5062744 |
saw_glob = 1; |
|
567 |
✓✓ | 30744844 |
else if (c == '[') { |
568 |
✓✓ | 6383289 |
if (!in_bracket) { |
569 |
saw_glob = 1; |
||
570 |
in_bracket = 1; |
||
571 |
✓✓✓✓ |
7100248 |
if (ISMAGIC(p[1]) && p[2] == '!') |
572 |
716945 |
p += 2; |
|
573 |
✓✓✓✓ |
6383328 |
if (ISMAGIC(p[1]) && p[2] == ']') |
574 |
25 |
p += 2; |
|
575 |
} |
||
576 |
/* XXX Do we need to check ranges here? POSIX Q */ |
||
577 |
✓✓ | 24361555 |
} else if (c == ']') { |
578 |
✓✓ | 3483794 |
if (in_bracket) { |
579 |
✓✓ | 2149322 |
if (bnest) /* [a*(b]) */ |
580 |
25 |
return 0; |
|
581 |
in_bracket = 0; |
||
582 |
2149297 |
} |
|
583 |
✓✓✓✗ |
24621284 |
} else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) { |
584 |
saw_glob = 1; |
||
585 |
✓✓ | 3743523 |
if (in_bracket) |
586 |
50 |
bnest++; |
|
587 |
else |
||
588 |
3743473 |
nest++; |
|
589 |
✓✓ | 17134238 |
} else if (c == '|') { |
590 |
✗✓ | 194968 |
if (in_bracket && !bnest) /* *(a[foo|bar]) */ |
591 |
return 0; |
||
592 |
✓✓ | 16939270 |
} else if (c == /*(*/ ')') { |
593 |
✓✓ | 3743498 |
if (in_bracket) { |
594 |
✓✓ | 150 |
if (!bnest--) /* *(a[b)c] */ |
595 |
125 |
return 0; |
|
596 |
✓✗ | 3743348 |
} else if (nest) |
597 |
3743348 |
nest--; |
|
598 |
} |
||
599 |
/* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-] |
||
600 |
MAGIC-{, MAGIC-,, MAGIC-} */ |
||
601 |
} |
||
602 |
✓✓ | 30260475 |
return saw_glob && !in_bracket && !nest; |
603 |
12367055 |
} |
|
604 |
|||
605 |
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ |
||
606 |
static int |
||
607 |
do_gmatch(const unsigned char *s, const unsigned char *se, |
||
608 |
const unsigned char *p, const unsigned char *pe) |
||
609 |
{ |
||
610 |
int sc, pc; |
||
611 |
const unsigned char *prest, *psub, *pnext; |
||
612 |
const unsigned char *srest; |
||
613 |
|||
614 |
✗✓ | 794129412 |
if (s == NULL || p == NULL) |
615 |
return 0; |
||
616 |
✓✓ | 646089089 |
while (p < pe) { |
617 |
424629994 |
pc = *p++; |
|
618 |
✓✓ | 1261600068 |
sc = s < se ? *s : '\0'; |
619 |
424629994 |
s++; |
|
620 |
✓✓ | 424629994 |
if (!ISMAGIC(pc)) { |
621 |
✓✓ | 125160765 |
if (sc != pc) |
622 |
95389699 |
return 0; |
|
623 |
continue; |
||
624 |
} |
||
625 |
✓✓✓✗ ✓✗✗✓ ✓✓ |
299469229 |
switch (*p++) { |
626 |
case '[': |
||
627 |
✓✓✓✓ |
537920134 |
if (sc == 0 || (p = cclass(p, sc)) == NULL) |
628 |
60437897 |
return 0; |
|
629 |
break; |
||
630 |
|||
631 |
case '?': |
||
632 |
✓✓ | 22674 |
if (sc == 0) |
633 |
5842 |
return 0; |
|
634 |
break; |
||
635 |
|||
636 |
case '*': |
||
637 |
✓✓ | 7111087 |
if (p == pe) |
638 |
450283 |
return 1; |
|
639 |
6660804 |
s--; |
|
640 |
6660804 |
do { |
|
641 |
✓✓ | 80827046 |
if (do_gmatch(s, se, p, pe)) |
642 |
5302085 |
return 1; |
|
643 |
✓✓ | 75524961 |
} while (s++ < se); |
644 |
1358719 |
return 0; |
|
645 |
|||
646 |
/* |
||
647 |
* [*+?@!](pattern|pattern|..) |
||
648 |
* |
||
649 |
* Not ifdef'd KSH as this is needed for ${..%..}, etc. |
||
650 |
*/ |
||
651 |
case 0x80|'+': /* matches one or more times */ |
||
652 |
case 0x80|'*': /* matches zero or more times */ |
||
653 |
✗✓ | 10592002 |
if (!(prest = pat_scan(p, pe, 0))) |
654 |
return 0; |
||
655 |
10592002 |
s--; |
|
656 |
/* take care of zero matches */ |
||
657 |
✓✓✓✓ |
21182996 |
if (p[-1] == (0x80 | '*') && |
658 |
10590994 |
do_gmatch(s, se, prest, pe)) |
|
659 |
117646 |
return 1; |
|
660 |
10475446 |
for (psub = p; ; psub = pnext) { |
|
661 |
10475446 |
pnext = pat_scan(psub, pe, 1); |
|
662 |
✓✓ | 567912224 |
for (srest = s; srest <= se; srest++) { |
663 |
✓✓✓✓ |
282002075 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
664 |
✓✓ | 8046965 |
(do_gmatch(srest, se, prest, pe) || |
665 |
✓✓ | 15977573 |
(s != srest && do_gmatch(srest, |
666 |
7988379 |
se, p - 2, pe)))) |
|
667 |
533030 |
return 1; |
|
668 |
} |
||
669 |
✓✓ | 9942416 |
if (pnext == prest) |
670 |
break; |
||
671 |
} |
||
672 |
9941326 |
return 0; |
|
673 |
|||
674 |
case 0x80|'?': /* matches zero or once */ |
||
675 |
case 0x80|'@': /* matches one of the patterns */ |
||
676 |
case 0x80|' ': /* simile for @ */ |
||
677 |
✗✓ | 1750208 |
if (!(prest = pat_scan(p, pe, 0))) |
678 |
return 0; |
||
679 |
1750208 |
s--; |
|
680 |
/* Take care of zero matches */ |
||
681 |
✗✓✗✗ |
1750208 |
if (p[-1] == (0x80 | '?') && |
682 |
do_gmatch(s, se, prest, pe)) |
||
683 |
return 1; |
||
684 |
1821631 |
for (psub = p; ; psub = pnext) { |
|
685 |
1821631 |
pnext = pat_scan(psub, pe, 1); |
|
686 |
1821631 |
srest = prest == pe ? se : s; |
|
687 |
✓✓ | 6882552 |
for (; srest <= se; srest++) { |
688 |
✓✓✓✗ |
2023617 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
689 |
201986 |
do_gmatch(srest, se, prest, pe)) |
|
690 |
201986 |
return 1; |
|
691 |
} |
||
692 |
✓✓ | 1619645 |
if (pnext == prest) |
693 |
break; |
||
694 |
} |
||
695 |
1548222 |
return 0; |
|
696 |
|||
697 |
case 0x80|'!': /* matches none of the patterns */ |
||
698 |
✗✓ | 125 |
if (!(prest = pat_scan(p, pe, 0))) |
699 |
return 0; |
||
700 |
125 |
s--; |
|
701 |
✓✓ | 1050 |
for (srest = s; srest <= se; srest++) { |
702 |
int matched = 0; |
||
703 |
|||
704 |
600 |
for (psub = p; ; psub = pnext) { |
|
705 |
600 |
pnext = pat_scan(psub, pe, 1); |
|
706 |
✓✓ | 600 |
if (do_gmatch(s, srest, psub, |
707 |
600 |
pnext - 2)) { |
|
708 |
matched = 1; |
||
709 |
100 |
break; |
|
710 |
} |
||
711 |
✓✓ | 500 |
if (pnext == prest) |
712 |
break; |
||
713 |
} |
||
714 |
✓✓✓✓ |
750 |
if (!matched && |
715 |
325 |
do_gmatch(srest, se, prest, pe)) |
|
716 |
25 |
return 1; |
|
717 |
✓✓ | 400 |
} |
718 |
100 |
return 0; |
|
719 |
|||
720 |
default: |
||
721 |
✓✓ | 5791942 |
if (sc != p[-1]) |
722 |
318751 |
return 0; |
|
723 |
break; |
||
724 |
} |
||
725 |
} |
||
726 |
221459095 |
return s == se; |
|
727 |
397064706 |
} |
|
728 |
|||
729 |
static int |
||
730 |
posix_cclass(const unsigned char *pattern, int test, const unsigned char **ep) |
||
731 |
{ |
||
732 |
struct cclass *cc; |
||
733 |
const unsigned char *colon; |
||
734 |
size_t len; |
||
735 |
int rval = 0; |
||
736 |
|||
737 |
✓✗✗✓ |
139499247 |
if ((colon = strchr(pattern, ':')) == NULL || colon[1] != MAGIC) { |
738 |
*ep = pattern - 2; |
||
739 |
return -1; |
||
740 |
} |
||
741 |
46499749 |
*ep = colon + 3; /* skip MAGIC */ |
|
742 |
46499749 |
len = (size_t)(colon - pattern); |
|
743 |
|||
744 |
✓✗ | 278996248 |
for (cc = cclasses; cc->name != NULL; cc++) { |
745 |
✓✓✓✗ |
185997873 |
if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') { |
746 |
✓✓ | 46499749 |
if (cc->isctype(test)) |
747 |
10087502 |
rval = 1; |
|
748 |
break; |
||
749 |
} |
||
750 |
} |
||
751 |
✗✓ | 46499749 |
if (cc->name == NULL) { |
752 |
rval = -2; /* invalid character class */ |
||
753 |
} |
||
754 |
46499749 |
return rval; |
|
755 |
46499749 |
} |
|
756 |
|||
757 |
static const unsigned char * |
||
758 |
cclass(const unsigned char *p, int sub) |
||
759 |
{ |
||
760 |
int c, d, rv, not, found = 0; |
||
761 |
const unsigned char *orig_p = p; |
||
762 |
|||
763 |
✓✓✓✓ |
791028753 |
if ((not = (ISMAGIC(*p) && *++p == '!'))) |
764 |
217091526 |
p++; |
|
765 |
do { |
||
766 |
/* check for POSIX character class (e.g. [[:alpha:]]) */ |
||
767 |
✓✓✓✓ ✗✓✓✗ |
310454855 |
if ((p[0] == MAGIC && p[1] == '[' && p[2] == ':') || |
768 |
✓✓ | 310452456 |
(p[0] == '[' && p[1] == ':')) { |
769 |
do { |
||
770 |
56587251 |
const char *pp = p + (*p == MAGIC) + 2; |
|
771 |
56587251 |
rv = posix_cclass(pp, sub, &p); |
|
772 |
✓✗✓ | 56587251 |
switch (rv) { |
773 |
case 1: |
||
774 |
found = 1; |
||
775 |
10087502 |
break; |
|
776 |
case -2: |
||
777 |
return NULL; |
||
778 |
} |
||
779 |
✓✗✓✗ ✓✗✗✓ ✗✗ |
185998996 |
} while (rv != -1 && p[0] == MAGIC && p[1] == '[' && p[2] == ':'); |
780 |
✓✗✗✓ |
92999498 |
if (p[0] == MAGIC && p[1] == ']') |
781 |
break; |
||
782 |
} |
||
783 |
|||
784 |
217454174 |
c = *p++; |
|
785 |
✓✓ | 217454174 |
if (ISMAGIC(c)) { |
786 |
575 |
c = *p++; |
|
787 |
✓✓✓✗ |
650 |
if ((c & 0x80) && !ISMAGIC(c)) { |
788 |
75 |
c &= 0x7f;/* extended pattern matching: *+?@! */ |
|
789 |
/* XXX the ( char isn't handled as part of [] */ |
||
790 |
75 |
if (c == ' ') /* simile for @: plain (..) */ |
|
791 |
c = '(' /*)*/; |
||
792 |
75 |
} |
|
793 |
} |
||
794 |
✗✓ | 217454174 |
if (c == '\0') |
795 |
/* No closing ] - act as if the opening [ was quoted */ |
||
796 |
return sub == '[' ? orig_p : NULL; |
||
797 |
✓✓✓✓ ✓✓ |
434674576 |
if (ISMAGIC(p[0]) && p[1] == '-' && |
798 |
✓✓ | 659 |
(!ISMAGIC(p[2]) || p[3] != ']')) { |
799 |
434 |
p += 2; /* MAGIC- */ |
|
800 |
434 |
d = *p++; |
|
801 |
✓✓ | 434 |
if (ISMAGIC(d)) { |
802 |
25 |
d = *p++; |
|
803 |
✗✓✗✗ |
25 |
if ((d & 0x80) && !ISMAGIC(d)) |
804 |
d &= 0x7f; |
||
805 |
} |
||
806 |
/* POSIX says this is an invalid expression */ |
||
807 |
✓✓ | 434 |
if (c > d) |
808 |
25 |
return NULL; |
|
809 |
} else |
||
810 |
d = c; |
||
811 |
✓✓✓✓ ✓✓ |
624267152 |
if (c == sub || (c <= sub && sub <= d)) |
812 |
13557034 |
found = 1; |
|
813 |
✓✓✓✓ |
652128550 |
} while (!(ISMAGIC(p[0]) && p[1] == ']')); |
814 |
|||
815 |
263718918 |
return (found != not) ? p+2 : NULL; |
|
816 |
263718943 |
} |
|
817 |
|||
818 |
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ |
||
819 |
const unsigned char * |
||
820 |
pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep) |
||
821 |
{ |
||
822 |
int nest = 0; |
||
823 |
|||
824 |
✓✗ | 410469926 |
for (; p < pe; p++) { |
825 |
✓✓ | 192914957 |
if (!ISMAGIC(*p)) |
826 |
continue; |
||
827 |
✓✓✓✓ |
145439629 |
if ((*++p == /*(*/ ')' && nest-- == 0) || |
828 |
✓✓ | 92400733 |
(*p == '|' && match_sep && nest == 0)) |
829 |
24640012 |
return ++p; |
|
830 |
✓✓✓✗ |
96271273 |
if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f)) |
831 |
3982208 |
nest++; |
|
832 |
} |
||
833 |
return NULL; |
||
834 |
24640012 |
} |
|
835 |
|||
836 |
/* |
||
837 |
* quick sort of array of generic pointers to objects. |
||
838 |
*/ |
||
839 |
void |
||
840 |
qsortp(void **base, /* base address */ |
||
841 |
size_t n, /* elements */ |
||
842 |
int (*f) (const void *, const void *)) /* compare function */ |
||
843 |
{ |
||
844 |
5852392 |
qsort(base, n, sizeof(char *), f); |
|
845 |
2926196 |
} |
|
846 |
|||
847 |
int |
||
848 |
xstrcmp(const void *p1, const void *p2) |
||
849 |
{ |
||
850 |
84219478 |
return (strcmp(*(char **)p1, *(char **)p2)); |
|
851 |
} |
||
852 |
|||
853 |
/* Initialize a Getopt structure */ |
||
854 |
void |
||
855 |
ksh_getopt_reset(Getopt *go, int flags) |
||
856 |
{ |
||
857 |
22376556 |
go->optind = 1; |
|
858 |
11188278 |
go->optarg = NULL; |
|
859 |
11188278 |
go->p = 0; |
|
860 |
11188278 |
go->flags = flags; |
|
861 |
11188278 |
go->info = 0; |
|
862 |
11188278 |
go->buf[1] = '\0'; |
|
863 |
11188278 |
} |
|
864 |
|||
865 |
|||
866 |
/* getopt() used for shell built-in commands, the getopts command, and |
||
867 |
* command line options. |
||
868 |
* A leading ':' in options means don't print errors, instead return '?' |
||
869 |
* or ':' and set go->optarg to the offending option character. |
||
870 |
* If GF_ERROR is set (and option doesn't start with :), errors result in |
||
871 |
* a call to bi_errorf(). |
||
872 |
* |
||
873 |
* Non-standard features: |
||
874 |
* - ';' is like ':' in options, except the argument is optional |
||
875 |
* (if it isn't present, optarg is set to 0). |
||
876 |
* Used for 'set -o'. |
||
877 |
* - ',' is like ':' in options, except the argument always immediately |
||
878 |
* follows the option character (optarg is set to the null string if |
||
879 |
* the option is missing). |
||
880 |
* Used for 'read -u2', 'print -u2' and fc -40. |
||
881 |
* - '#' is like ':' in options, expect that the argument is optional |
||
882 |
* and must start with a digit or be the string "unlimited". If the |
||
883 |
* argument doesn't match, it is assumed to be missing and normal option |
||
884 |
* processing continues (optarg is set to 0 if the option is missing). |
||
885 |
* Used for 'typeset -LZ4' and 'ulimit -adunlimited'. |
||
886 |
* - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an |
||
887 |
* option starting with + is accepted, the GI_PLUS flag will be set |
||
888 |
* in go->info. |
||
889 |
*/ |
||
890 |
int |
||
891 |
ksh_getopt(char **argv, Getopt *go, const char *options) |
||
892 |
{ |
||
893 |
char c; |
||
894 |
char *o; |
||
895 |
|||
896 |
✓✓✓✓ |
17165541 |
if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { |
897 |
✓✓ | 19446638 |
char *arg = argv[go->optind], flag = arg ? *arg : '\0'; |
898 |
|||
899 |
6641437 |
go->p = 1; |
|
900 |
✓✓✓✓ ✓✗ |
9676569 |
if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { |
901 |
486680 |
go->optind++; |
|
902 |
486680 |
go->p = 0; |
|
903 |
486680 |
go->info |= GI_MINUSMINUS; |
|
904 |
486680 |
return -1; |
|
905 |
} |
||
906 |
✓✓✓✓ |
8218523 |
if (arg == NULL || |
907 |
✓✓ | 5677084 |
((flag != '-' ) && /* neither a - nor a + (if + allowed) */ |
908 |
✓✓✓✓ |
5699196 |
(!(go->flags & GF_PLUSOPT) || flag != '+')) || |
909 |
2063766 |
(c = arg[1]) == '\0') { |
|
910 |
4091427 |
go->p = 0; |
|
911 |
4091427 |
return -1; |
|
912 |
} |
||
913 |
2063330 |
go->optind++; |
|
914 |
2063330 |
go->info &= ~(GI_MINUS|GI_PLUS); |
|
915 |
2063330 |
go->info |= flag == '-' ? GI_MINUS : GI_PLUS; |
|
916 |
✓✓ | 2063330 |
} |
917 |
2689128 |
go->p++; |
|
918 |
✓✗✓✗ ✓✗✓✗ ✓✗✓✓ |
16134768 |
if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || |
919 |
2689128 |
!(o = strchr(options, c))) { |
|
920 |
✓✗ | 625 |
if (options[0] == ':') { |
921 |
625 |
go->buf[0] = c; |
|
922 |
625 |
go->optarg = go->buf; |
|
923 |
625 |
} else { |
|
924 |
warningf(true, "%s%s-%c: unknown option", |
||
925 |
(go->flags & GF_NONAME) ? "" : argv[0], |
||
926 |
(go->flags & GF_NONAME) ? "" : ": ", c); |
||
927 |
if (go->flags & GF_ERROR) |
||
928 |
bi_errorf(NULL); |
||
929 |
} |
||
930 |
625 |
return '?'; |
|
931 |
} |
||
932 |
/* : means argument must be present, may be part of option argument |
||
933 |
* or the next argument |
||
934 |
* ; same as : but argument may be missing |
||
935 |
* , means argument is part of option argument, and may be null. |
||
936 |
*/ |
||
937 |
✓✓✓✓ |
5326693 |
if (*++o == ':' || *o == ';') { |
938 |
✗✓ | 53615 |
if (argv[go->optind - 1][go->p]) |
939 |
go->optarg = argv[go->optind - 1] + go->p; |
||
940 |
✓✓ | 53615 |
else if (argv[go->optind]) |
941 |
53576 |
go->optarg = argv[go->optind++]; |
|
942 |
✓✗ | 39 |
else if (*o == ';') |
943 |
go->optarg = NULL; |
||
944 |
else { |
||
945 |
if (options[0] == ':') { |
||
946 |
go->buf[0] = c; |
||
947 |
go->optarg = go->buf; |
||
948 |
return ':'; |
||
949 |
} |
||
950 |
warningf(true, "%s%s-`%c' requires argument", |
||
951 |
(go->flags & GF_NONAME) ? "" : argv[0], |
||
952 |
(go->flags & GF_NONAME) ? "" : ": ", c); |
||
953 |
if (go->flags & GF_ERROR) |
||
954 |
bi_errorf(NULL); |
||
955 |
return '?'; |
||
956 |
} |
||
957 |
53615 |
go->p = 0; |
|
958 |
✓✓ | 2688503 |
} else if (*o == ',') { |
959 |
/* argument is attached to option character, even if null */ |
||
960 |
200 |
go->optarg = argv[go->optind - 1] + go->p; |
|
961 |
200 |
go->p = 0; |
|
962 |
✓✓ | 2634888 |
} else if (*o == '#') { |
963 |
/* argument is optional and may be attached or unattached |
||
964 |
* but must start with a digit. optarg is set to 0 if the |
||
965 |
* argument is missing. |
||
966 |
*/ |
||
967 |
✓✓ | 657802 |
if (argv[go->optind - 1][go->p]) { |
968 |
✓✓✗✓ |
435815 |
if (digit(argv[go->optind - 1][go->p]) || |
969 |
217770 |
!strcmp(&argv[go->optind - 1][go->p], "unlimited")) { |
|
970 |
275 |
go->optarg = argv[go->optind - 1] + go->p; |
|
971 |
275 |
go->p = 0; |
|
972 |
275 |
} else |
|
973 |
217770 |
go->optarg = NULL; |
|
974 |
} else { |
||
975 |
✓✓✓✓ ✗✓ |
1315779 |
if (argv[go->optind] && (digit(argv[go->optind][0]) || |
976 |
436340 |
!strcmp(argv[go->optind], "unlimited"))) { |
|
977 |
3342 |
go->optarg = argv[go->optind++]; |
|
978 |
3342 |
go->p = 0; |
|
979 |
3342 |
} else |
|
980 |
436415 |
go->optarg = NULL; |
|
981 |
} |
||
982 |
} |
||
983 |
2688503 |
return c; |
|
984 |
7267235 |
} |
|
985 |
|||
986 |
/* print variable/alias value using necessary quotes |
||
987 |
* (POSIX says they should be suitable for re-entry...) |
||
988 |
* No trailing newline is printed. |
||
989 |
*/ |
||
990 |
void |
||
991 |
print_value_quoted(const char *s) |
||
992 |
{ |
||
993 |
const char *p; |
||
994 |
int inquote = 0; |
||
995 |
|||
996 |
/* Test if any quotes are needed */ |
||
997 |
✓✓ | 183186 |
for (p = s; *p; p++) |
998 |
✓✓ | 74937 |
if (ctype(*p, C_QUOTE)) |
999 |
break; |
||
1000 |
✓✓ | 13516 |
if (!*p) { |
1001 |
9898 |
shprintf("%s", s); |
|
1002 |
9898 |
return; |
|
1003 |
} |
||
1004 |
✓✓ | 369950 |
for (p = s; *p; p++) { |
1005 |
✓✓ | 181357 |
if (*p == '\'') { |
1006 |
1053 |
shprintf(inquote ? "'\\'" : "\\'"); |
|
1007 |
inquote = 0; |
||
1008 |
1053 |
} else { |
|
1009 |
✓✓ | 180304 |
if (!inquote) { |
1010 |
4566 |
shprintf("'"); |
|
1011 |
inquote = 1; |
||
1012 |
4566 |
} |
|
1013 |
✓✓ | 360608 |
shf_putc(*p, shl_stdout); |
1014 |
} |
||
1015 |
} |
||
1016 |
✓✓ | 3618 |
if (inquote) |
1017 |
3516 |
shprintf("'"); |
|
1018 |
17134 |
} |
|
1019 |
|||
1020 |
/* Print things in columns and rows - func() is called to format the ith |
||
1021 |
* element |
||
1022 |
*/ |
||
1023 |
void |
||
1024 |
print_columns(struct shf *shf, int n, char *(*func) (void *, int, char *, int), |
||
1025 |
void *arg, int max_width, int prefcol) |
||
1026 |
{ |
||
1027 |
206 |
char *str = alloc(max_width + 1, ATEMP); |
|
1028 |
int i; |
||
1029 |
int r, c; |
||
1030 |
int rows, cols; |
||
1031 |
int nspace; |
||
1032 |
int col_width; |
||
1033 |
|||
1034 |
/* max_width + 1 for the space. Note that no space |
||
1035 |
* is printed after the last column to avoid problems |
||
1036 |
* with terminals that have auto-wrap. |
||
1037 |
*/ |
||
1038 |
103 |
cols = x_cols / (max_width + 1); |
|
1039 |
103 |
if (!cols) |
|
1040 |
cols = 1; |
||
1041 |
103 |
rows = (n + cols - 1) / cols; |
|
1042 |
✓✓✗✓ |
142 |
if (prefcol && n && cols > rows) { |
1043 |
int tmp = rows; |
||
1044 |
|||
1045 |
rows = cols; |
||
1046 |
cols = tmp; |
||
1047 |
if (rows > n) |
||
1048 |
rows = n; |
||
1049 |
} |
||
1050 |
|||
1051 |
col_width = max_width; |
||
1052 |
103 |
if (cols == 1) |
|
1053 |
col_width = 0; /* Don't pad entries in single column output. */ |
||
1054 |
103 |
nspace = (x_cols - max_width * cols) / cols; |
|
1055 |
103 |
if (nspace <= 0) |
|
1056 |
nspace = 1; |
||
1057 |
✓✓ | 1084 |
for (r = 0; r < rows; r++) { |
1058 |
✓✓ | 5454 |
for (c = 0; c < cols; c++) { |
1059 |
2288 |
i = c * rows + r; |
|
1060 |
✓✓ | 2288 |
if (i < n) { |
1061 |
1778 |
shf_fprintf(shf, "%-*s", |
|
1062 |
col_width, |
||
1063 |
1778 |
(*func)(arg, i, str, max_width + 1)); |
|
1064 |
✓✓ | 1778 |
if (c + 1 < cols) |
1065 |
1485 |
shf_fprintf(shf, "%*s", nspace, ""); |
|
1066 |
} |
||
1067 |
} |
||
1068 |
439 |
shf_putchar('\n', shf); |
|
1069 |
} |
||
1070 |
103 |
afree(str, ATEMP); |
|
1071 |
103 |
} |
|
1072 |
|||
1073 |
/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ |
||
1074 |
int |
||
1075 |
strip_nuls(char *buf, int nbytes) |
||
1076 |
{ |
||
1077 |
char *dst; |
||
1078 |
|||
1079 |
✓✓ | 40146440 |
if ((dst = memchr(buf, '\0', nbytes))) { |
1080 |
23 |
char *end = buf + nbytes; |
|
1081 |
char *p, *q; |
||
1082 |
|||
1083 |
✓✓ | 92 |
for (p = dst; p < end; p = q) { |
1084 |
/* skip a block of nulls */ |
||
1085 |
✓✗✗✓ |
46 |
while (++p < end && *p == '\0') |
1086 |
; |
||
1087 |
/* find end of non-null block */ |
||
1088 |
✓✗ | 23 |
if (!(q = memchr(p, '\0', end - p))) |
1089 |
23 |
q = end; |
|
1090 |
23 |
memmove(dst, p, q - p); |
|
1091 |
23 |
dst += q - p; |
|
1092 |
} |
||
1093 |
23 |
*dst = '\0'; |
|
1094 |
23 |
return dst - buf; |
|
1095 |
} |
||
1096 |
20073197 |
return nbytes; |
|
1097 |
20073220 |
} |
|
1098 |
|||
1099 |
/* Like read(2), but if read fails due to non-blocking flag, resets flag |
||
1100 |
* and restarts read. |
||
1101 |
*/ |
||
1102 |
int |
||
1103 |
blocking_read(int fd, char *buf, int nbytes) |
||
1104 |
{ |
||
1105 |
int ret; |
||
1106 |
int tried_reset = 0; |
||
1107 |
|||
1108 |
✓✓ | 54317300 |
while ((ret = read(fd, buf, nbytes)) < 0) { |
1109 |
✗✓ | 18720538 |
if (!tried_reset && errno == EAGAIN) { |
1110 |
int oerrno = errno; |
||
1111 |
if (reset_nonblock(fd) > 0) { |
||
1112 |
tried_reset = 1; |
||
1113 |
continue; |
||
1114 |
} |
||
1115 |
errno = oerrno; |
||
1116 |
} |
||
1117 |
break; |
||
1118 |
} |
||
1119 |
13579325 |
return ret; |
|
1120 |
} |
||
1121 |
|||
1122 |
/* Reset the non-blocking flag on the specified file descriptor. |
||
1123 |
* Returns -1 if there was an error, 0 if non-blocking wasn't set, |
||
1124 |
* 1 if it was. |
||
1125 |
*/ |
||
1126 |
int |
||
1127 |
reset_nonblock(int fd) |
||
1128 |
{ |
||
1129 |
int flags; |
||
1130 |
|||
1131 |
✗✓ | 5498 |
if ((flags = fcntl(fd, F_GETFL)) < 0) |
1132 |
return -1; |
||
1133 |
✓✗ | 2749 |
if (!(flags & O_NONBLOCK)) |
1134 |
2749 |
return 0; |
|
1135 |
flags &= ~O_NONBLOCK; |
||
1136 |
if (fcntl(fd, F_SETFL, flags) < 0) |
||
1137 |
return -1; |
||
1138 |
return 1; |
||
1139 |
2749 |
} |
|
1140 |
|||
1141 |
|||
1142 |
/* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */ |
||
1143 |
char * |
||
1144 |
ksh_get_wd(char *buf, int bsize) |
||
1145 |
{ |
||
1146 |
char *b; |
||
1147 |
char *ret; |
||
1148 |
|||
1149 |
/* Note: we could just use plain getcwd(), but then we'd had to |
||
1150 |
* inject possibly allocated space into the ATEMP area. */ |
||
1151 |
/* Assume getcwd() available */ |
||
1152 |
✓✗ | 17452 |
if (!buf) { |
1153 |
bsize = PATH_MAX; |
||
1154 |
8726 |
b = alloc(PATH_MAX + 1, ATEMP); |
|
1155 |
8726 |
} else |
|
1156 |
b = buf; |
||
1157 |
|||
1158 |
8726 |
ret = getcwd(b, bsize); |
|
1159 |
|||
1160 |
✓✗ | 8726 |
if (!buf) { |
1161 |
✓✗ | 8726 |
if (ret) |
1162 |
8726 |
ret = aresize(b, strlen(b) + 1, ATEMP); |
|
1163 |
else |
||
1164 |
afree(b, ATEMP); |
||
1165 |
} |
||
1166 |
|||
1167 |
8726 |
return ret; |
|
1168 |
} |
Generated by: GCOVR (Version 3.3) |