GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: misc.c,v 1.55 2016/03/20 00:01:21 krw 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 |
24979 |
{ |
|
30 |
int i; |
||
31 |
|||
32 |
✓✓ | 24979 |
if (t & C_IFS) { |
33 |
✓✓ | 984053 |
for (i = 0; i < UCHAR_MAX+1; i++) |
34 |
980224 |
ctypes[i] &= ~C_IFS; |
|
35 |
3829 |
ctypes[0] |= C_IFS; /* include \0 in C_IFS */ |
|
36 |
} |
||
37 |
✓✓ | 194853 |
while (*s != 0) |
38 |
169874 |
ctypes[(unsigned char) *s++] |= t; |
|
39 |
24979 |
} |
|
40 |
|||
41 |
void |
||
42 |
initctypes(void) |
||
43 |
3525 |
{ |
|
44 |
int c; |
||
45 |
|||
46 |
✓✓ | 95175 |
for (c = 'a'; c <= 'z'; c++) |
47 |
91650 |
ctypes[c] |= C_ALPHA; |
|
48 |
✓✓ | 95175 |
for (c = 'A'; c <= 'Z'; c++) |
49 |
91650 |
ctypes[c] |= C_ALPHA; |
|
50 |
3525 |
ctypes['_'] |= C_ALPHA; |
|
51 |
3525 |
setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */ |
|
52 |
3525 |
setctypes("*@#!$-?", C_VAR1); |
|
53 |
3525 |
setctypes(" \t\n", C_IFSWS); |
|
54 |
3525 |
setctypes("=-+?", C_SUBOP1); |
|
55 |
3525 |
setctypes("#%", C_SUBOP2); |
|
56 |
3525 |
setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE); |
|
57 |
3525 |
} |
|
58 |
|||
59 |
/* convert unsigned long to base N string */ |
||
60 |
|||
61 |
char * |
||
62 |
ulton(long unsigned int n, int base) |
||
63 |
8 |
{ |
|
64 |
char *p; |
||
65 |
static char buf [20]; |
||
66 |
|||
67 |
8 |
p = &buf[sizeof(buf)]; |
|
68 |
8 |
*--p = '\0'; |
|
69 |
do { |
||
70 |
8 |
*--p = "0123456789ABCDEF"[n%base]; |
|
71 |
8 |
n /= base; |
|
72 |
✗✓ | 8 |
} while (n != 0); |
73 |
8 |
return p; |
|
74 |
} |
||
75 |
|||
76 |
char * |
||
77 |
str_save(const char *s, Area *ap) |
||
78 |
178312 |
{ |
|
79 |
size_t len; |
||
80 |
char *p; |
||
81 |
|||
82 |
✓✓ | 178312 |
if (!s) |
83 |
31041 |
return NULL; |
|
84 |
147271 |
len = strlen(s)+1; |
|
85 |
147271 |
p = alloc(len, ap); |
|
86 |
147271 |
strlcpy(p, s, len); |
|
87 |
147271 |
return (p); |
|
88 |
} |
||
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 |
161591 |
{ |
|
97 |
char *ns; |
||
98 |
|||
99 |
✗✓ | 161591 |
if (n < 0) |
100 |
return 0; |
||
101 |
161591 |
ns = alloc(n + 1, ap); |
|
102 |
161591 |
ns[0] = '\0'; |
|
103 |
161591 |
return strncat(ns, s, n); |
|
104 |
} |
||
105 |
|||
106 |
/* called from expand.h:XcheckN() to grow buffer */ |
||
107 |
char * |
||
108 |
Xcheck_grow_(XString *xsp, char *xp, int more) |
||
109 |
18432 |
{ |
|
110 |
18432 |
char *old_beg = xsp->beg; |
|
111 |
|||
112 |
18432 |
xsp->len += more > xsp->len ? more : xsp->len; |
|
113 |
18432 |
xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap); |
|
114 |
18432 |
xsp->end = xsp->beg + xsp->len; |
|
115 |
18432 |
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 }, /* non-standard */ |
||
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 |
12 |
{ |
|
185 |
int i; |
||
186 |
|||
187 |
✓✗ | 230 |
for (i = 0; i < NELEM(options); i++) |
188 |
✓✓✓✓ |
230 |
if (options[i].name && strcmp(options[i].name, n) == 0) |
189 |
12 |
return i; |
|
190 |
|||
191 |
return -1; |
||
192 |
} |
||
193 |
|||
194 |
struct options_info { |
||
195 |
int opt_width; |
||
196 |
struct { |
||
197 |
const char *name; |
||
198 |
int flag; |
||
199 |
} opts[NELEM(options)]; |
||
200 |
}; |
||
201 |
|||
202 |
static char *options_fmt_entry(void *arg, int i, char *buf, int buflen); |
||
203 |
static void printoptions(int verbose); |
||
204 |
|||
205 |
/* format a single select menu item */ |
||
206 |
static char * |
||
207 |
options_fmt_entry(void *arg, int i, char *buf, int buflen) |
||
208 |
280 |
{ |
|
209 |
280 |
struct options_info *oi = (struct options_info *) arg; |
|
210 |
|||
211 |
✓✓ | 280 |
shf_snprintf(buf, buflen, "%-*s %s", |
212 |
oi->opt_width, oi->opts[i].name, |
||
213 |
Flag(oi->opts[i].flag) ? "on" : "off"); |
||
214 |
280 |
return buf; |
|
215 |
} |
||
216 |
|||
217 |
static void |
||
218 |
printoptions(int verbose) |
||
219 |
8 |
{ |
|
220 |
int i; |
||
221 |
|||
222 |
✓✗ | 8 |
if (verbose) { |
223 |
struct options_info oi; |
||
224 |
int n, len; |
||
225 |
|||
226 |
/* verbose version */ |
||
227 |
8 |
shprintf("Current option settings\n"); |
|
228 |
|||
229 |
✓✓ | 304 |
for (i = n = oi.opt_width = 0; i < NELEM(options); i++) |
230 |
✓✓ | 296 |
if (options[i].name) { |
231 |
280 |
len = strlen(options[i].name); |
|
232 |
280 |
oi.opts[n].name = options[i].name; |
|
233 |
280 |
oi.opts[n++].flag = i; |
|
234 |
✓✓ | 280 |
if (len > oi.opt_width) |
235 |
32 |
oi.opt_width = len; |
|
236 |
} |
||
237 |
8 |
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 |
shprintf("\n"); |
||
248 |
} |
||
249 |
8 |
} |
|
250 |
|||
251 |
char * |
||
252 |
getoptions(void) |
||
253 |
9 |
{ |
|
254 |
int i; |
||
255 |
char m[(int) FNFLAGS + 1]; |
||
256 |
9 |
char *cp = m; |
|
257 |
|||
258 |
✓✓ | 342 |
for (i = 0; i < NELEM(options); i++) |
259 |
✓✓✓✓ |
333 |
if (options[i].c && Flag(i)) |
260 |
32 |
*cp++ = options[i].c; |
|
261 |
9 |
*cp = 0; |
|
262 |
9 |
return str_save(m, ATEMP); |
|
263 |
} |
||
264 |
|||
265 |
/* change a Flag(*) value; takes care of special actions */ |
||
266 |
void |
||
267 |
change_flag(enum sh_flag f, |
||
268 |
int what, /* flag to change */ |
||
269 |
int newval) /* what is changing the flag (command line vs set) */ |
||
270 |
9679 |
{ |
|
271 |
int oldval; |
||
272 |
|||
273 |
9679 |
oldval = Flag(f); |
|
274 |
9679 |
Flag(f) = newval; |
|
275 |
#ifdef JOBS |
||
276 |
✗✓ | 9679 |
if (f == FMONITOR) { |
277 |
if (what != OF_CMDLINE && newval != oldval) |
||
278 |
j_change(); |
||
279 |
} else |
||
280 |
#endif /* JOBS */ |
||
281 |
#ifdef EDIT |
||
282 |
✓✓✗✓ |
9679 |
if (0 |
283 |
# ifdef VI |
||
284 |
|| f == FVI |
||
285 |
# endif /* VI */ |
||
286 |
# ifdef EMACS |
||
287 |
|| f == FEMACS || f == FGMACS |
||
288 |
# endif /* EMACS */ |
||
289 |
) |
||
290 |
{ |
||
291 |
✓✗ | 3525 |
if (newval) { |
292 |
# ifdef VI |
||
293 |
3525 |
Flag(FVI) = 0; |
|
294 |
# endif /* VI */ |
||
295 |
# ifdef EMACS |
||
296 |
3525 |
Flag(FEMACS) = Flag(FGMACS) = 0; |
|
297 |
# endif /* EMACS */ |
||
298 |
3525 |
Flag(f) = newval; |
|
299 |
} |
||
300 |
} else |
||
301 |
#endif /* EDIT */ |
||
302 |
/* Turning off -p? */ |
||
303 |
✗✓✗✗ |
6154 |
if (f == FPRIVILEGED && oldval && !newval) { |
304 |
gid_t gid = getgid(); |
||
305 |
|||
306 |
setresgid(gid, gid, gid); |
||
307 |
setgroups(1, &gid); |
||
308 |
setresuid(ksheuid, ksheuid, ksheuid); |
||
309 |
✓✓ | 6154 |
} else if (f == FPOSIX && newval) { |
310 |
#ifdef BRACE_EXPAND |
||
311 |
10 |
Flag(FBRACEEXPAND) = 0 |
|
312 |
#endif /* BRACE_EXPAND */ |
||
313 |
; |
||
314 |
} |
||
315 |
/* Changing interactive flag? */ |
||
316 |
✓✓ | 9679 |
if (f == FTALKING) { |
317 |
✓✗✓✗ |
56 |
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) |
318 |
56 |
Flag(FTALKING_I) = newval; |
|
319 |
} |
||
320 |
9679 |
} |
|
321 |
|||
322 |
/* parse command line & set command arguments. returns the index of |
||
323 |
* non-option arguments, -1 if there is an error. |
||
324 |
*/ |
||
325 |
int |
||
326 |
parse_args(char **argv, |
||
327 |
int what, /* OF_CMDLINE or OF_SET */ |
||
328 |
int *setargsp) |
||
329 |
5058 |
{ |
|
330 |
static char cmd_opts[NELEM(options) + 3]; /* o:\0 */ |
||
331 |
static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */ |
||
332 |
char *opts; |
||
333 |
5058 |
char *array = NULL; |
|
334 |
Getopt go; |
||
335 |
5058 |
int i, optc, set, sortargs = 0, arrayset = 0; |
|
336 |
|||
337 |
/* First call? Build option strings... */ |
||
338 |
✓✓ | 5058 |
if (cmd_opts[0] == '\0') { |
339 |
char *p, *q; |
||
340 |
|||
341 |
/* see cmd_opts[] declaration */ |
||
342 |
3525 |
strlcpy(cmd_opts, "o:", sizeof cmd_opts); |
|
343 |
3525 |
p = cmd_opts + strlen(cmd_opts); |
|
344 |
/* see set_opts[] declaration */ |
||
345 |
3525 |
strlcpy(set_opts, "A:o;s", sizeof set_opts); |
|
346 |
3525 |
q = set_opts + strlen(set_opts); |
|
347 |
✓✓ | 133950 |
for (i = 0; i < NELEM(options); i++) { |
348 |
✓✓ | 130425 |
if (options[i].c) { |
349 |
✓✗ | 66975 |
if (options[i].flags & OF_CMDLINE) |
350 |
66975 |
*p++ = options[i].c; |
|
351 |
✓✓ | 66975 |
if (options[i].flags & OF_SET) |
352 |
49350 |
*q++ = options[i].c; |
|
353 |
} |
||
354 |
} |
||
355 |
3525 |
*p = '\0'; |
|
356 |
3525 |
*q = '\0'; |
|
357 |
} |
||
358 |
|||
359 |
✓✓ | 5058 |
if (what == OF_CMDLINE) { |
360 |
char *p; |
||
361 |
/* Set FLOGIN before parsing options so user can clear |
||
362 |
* flag using +l. |
||
363 |
*/ |
||
364 |
✓✓✓✓ ✗✓ |
3525 |
Flag(FLOGIN) = (argv[0][0] == '-' || |
365 |
((p = strrchr(argv[0], '/')) && *++p == '-')); |
||
366 |
3525 |
opts = cmd_opts; |
|
367 |
} else |
||
368 |
1533 |
opts = set_opts; |
|
369 |
5058 |
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); |
|
370 |
✓✓ | 5058 |
while ((optc = ksh_getopt(argv, &go, opts)) != -1) { |
371 |
6174 |
set = (go.info & GI_PLUS) ? 0 : 1; |
|
372 |
✓✓✗✓ |
6174 |
switch (optc) { |
373 |
case 'A': |
||
374 |
✓✗ | 10 |
arrayset = set ? 1 : -1; |
375 |
10 |
array = go.optarg; |
|
376 |
10 |
break; |
|
377 |
|||
378 |
case 'o': |
||
379 |
✓✓ | 20 |
if (go.optarg == NULL) { |
380 |
/* lone -o: print options |
||
381 |
* |
||
382 |
* Note that on the command line, -o requires |
||
383 |
* an option (ie, can't get here if what is |
||
384 |
* OF_CMDLINE). |
||
385 |
*/ |
||
386 |
8 |
printoptions(set); |
|
387 |
8 |
break; |
|
388 |
} |
||
389 |
12 |
i = option(go.optarg); |
|
390 |
✓✗✓✓ |
12 |
if (i >= 0 && set == Flag(i)) |
391 |
/* Don't check the context if the flag |
||
392 |
* isn't changing - makes "set -o interactive" |
||
393 |
* work if you're already interactive. Needed |
||
394 |
* if the output of "set +o" is to be used. |
||
395 |
*/ |
||
396 |
; |
||
397 |
✓✗✓✗ |
20 |
else if (i >= 0 && (options[i].flags & what)) |
398 |
10 |
change_flag((enum sh_flag) i, what, set); |
|
399 |
else { |
||
400 |
bi_errorf("%s: bad option", go.optarg); |
||
401 |
return -1; |
||
402 |
} |
||
403 |
break; |
||
404 |
|||
405 |
case '?': |
||
406 |
return -1; |
||
407 |
|||
408 |
default: |
||
409 |
/* -s: sort positional params (at&t ksh stupidity) */ |
||
410 |
✓✓ | 6144 |
if (what == OF_SET && optc == 's') { |
411 |
2 |
sortargs = 1; |
|
412 |
2 |
break; |
|
413 |
} |
||
414 |
✓✗ | 39136 |
for (i = 0; i < NELEM(options); i++) |
415 |
✓✓✓✗ |
39136 |
if (optc == options[i].c && |
416 |
(what & options[i].flags)) { |
||
417 |
6142 |
change_flag((enum sh_flag) i, what, |
|
418 |
set); |
||
419 |
6142 |
break; |
|
420 |
} |
||
421 |
✗✓ | 6142 |
if (i == NELEM(options)) { |
422 |
internal_errorf(1, "parse_args: `%c'", optc); |
||
423 |
return -1; /* not reached */ |
||
424 |
} |
||
425 |
} |
||
426 |
} |
||
427 |
✓✓✓✓ ✓✓✓✗ |
5058 |
if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && |
428 |
(argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && |
||
429 |
argv[go.optind][1] == '\0') { |
||
430 |
/* lone - clears -v and -x flags */ |
||
431 |
✓✗✓✗ |
5 |
if (argv[go.optind][0] == '-' && !Flag(FPOSIX)) |
432 |
5 |
Flag(FVERBOSE) = Flag(FXTRACE) = 0; |
|
433 |
/* set skips lone - or + option */ |
||
434 |
5 |
go.optind++; |
|
435 |
} |
||
436 |
✓✓ | 5058 |
if (setargsp) |
437 |
/* -- means set $#/$* even if there are no arguments */ |
||
438 |
✓✓✓✓ ✓✓ |
1533 |
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || |
439 |
argv[go.optind]); |
||
440 |
|||
441 |
✓✓✓✗ ✗✓ |
5058 |
if (arrayset && (!*array || *skip_varname(array, false))) { |
442 |
bi_errorf("%s: is not an identifier", array); |
||
443 |
return -1; |
||
444 |
} |
||
445 |
✓✓ | 5058 |
if (sortargs) { |
446 |
✓✓ | 2 |
for (i = go.optind; argv[i]; i++) |
447 |
; |
||
448 |
2 |
qsortp((void **) &argv[go.optind], (size_t) (i - go.optind), |
|
449 |
xstrcmp); |
||
450 |
} |
||
451 |
✓✓ | 5058 |
if (arrayset) { |
452 |
10 |
set_array(array, arrayset, argv + go.optind); |
|
453 |
✓✓ | 10 |
for (; argv[go.optind]; go.optind++) |
454 |
; |
||
455 |
} |
||
456 |
|||
457 |
5058 |
return go.optind; |
|
458 |
} |
||
459 |
|||
460 |
/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */ |
||
461 |
int |
||
462 |
getn(const char *as, int *ai) |
||
463 |
623 |
{ |
|
464 |
char *p; |
||
465 |
long n; |
||
466 |
|||
467 |
623 |
n = strtol(as, &p, 10); |
|
468 |
|||
469 |
✓✗✓✓ ✗✓ |
623 |
if (!*as || *p || INT_MIN >= n || n >= INT_MAX) |
470 |
12 |
return 0; |
|
471 |
|||
472 |
611 |
*ai = (int)n; |
|
473 |
611 |
return 1; |
|
474 |
} |
||
475 |
|||
476 |
/* getn() that prints error */ |
||
477 |
int |
||
478 |
bi_getn(const char *as, int *ai) |
||
479 |
57 |
{ |
|
480 |
57 |
int rv = getn(as, ai); |
|
481 |
|||
482 |
✓✓ | 57 |
if (!rv) |
483 |
4 |
bi_errorf("%s: bad number", as); |
|
484 |
53 |
return rv; |
|
485 |
} |
||
486 |
|||
487 |
/* -------- gmatch.c -------- */ |
||
488 |
|||
489 |
/* |
||
490 |
* int gmatch(string, pattern) |
||
491 |
* char *string, *pattern; |
||
492 |
* |
||
493 |
* Match a pattern as in sh(1). |
||
494 |
* pattern character are prefixed with MAGIC by expand. |
||
495 |
*/ |
||
496 |
|||
497 |
int |
||
498 |
gmatch(const char *s, const char *p, int isfile) |
||
499 |
67207 |
{ |
|
500 |
const char *se, *pe; |
||
501 |
|||
502 |
✗✓ | 67207 |
if (s == NULL || p == NULL) |
503 |
return 0; |
||
504 |
67207 |
se = s + strlen(s); |
|
505 |
67207 |
pe = p + strlen(p); |
|
506 |
/* isfile is false iff no syntax check has been done on |
||
507 |
* the pattern. If check fails, just to a strcmp(). |
||
508 |
*/ |
||
509 |
✓✓✓✓ |
67207 |
if (!isfile && !has_globbing(p, pe)) { |
510 |
6900 |
int len = pe - p + 1; |
|
511 |
char tbuf[64]; |
||
512 |
char *t = len <= sizeof(tbuf) ? tbuf : |
||
513 |
✓✗ | 6900 |
alloc(len, ATEMP); |
514 |
6900 |
debunk(t, p, len); |
|
515 |
6900 |
return !strcmp(t, s); |
|
516 |
} |
||
517 |
60307 |
return do_gmatch((const unsigned char *) s, (const unsigned char *) se, |
|
518 |
(const unsigned char *) p, (const unsigned char *) pe); |
||
519 |
} |
||
520 |
|||
521 |
/* Returns if p is a syntacticly correct globbing pattern, false |
||
522 |
* if it contains no pattern characters or if there is a syntax error. |
||
523 |
* Syntax errors are: |
||
524 |
* - [ with no closing ] |
||
525 |
* - imbalanced $(...) expression |
||
526 |
* - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d)) |
||
527 |
*/ |
||
528 |
/*XXX |
||
529 |
- if no magic, |
||
530 |
if dest given, copy to dst |
||
531 |
return ? |
||
532 |
- if magic && (no globbing || syntax error) |
||
533 |
debunk to dst |
||
534 |
return ? |
||
535 |
- return ? |
||
536 |
*/ |
||
537 |
int |
||
538 |
has_globbing(const char *xp, const char *xpe) |
||
539 |
48568 |
{ |
|
540 |
48568 |
const unsigned char *p = (const unsigned char *) xp; |
|
541 |
48568 |
const unsigned char *pe = (const unsigned char *) xpe; |
|
542 |
int c; |
||
543 |
48568 |
int nest = 0, bnest = 0; |
|
544 |
48568 |
int saw_glob = 0; |
|
545 |
48568 |
int in_bracket = 0; /* inside [...] */ |
|
546 |
|||
547 |
✓✓ | 376060 |
for (; p < pe; p++) { |
548 |
✓✓ | 327504 |
if (!ISMAGIC(*p)) |
549 |
235989 |
continue; |
|
550 |
✓✓✓✓ |
109734 |
if ((c = *++p) == '*' || c == '?') |
551 |
18219 |
saw_glob = 1; |
|
552 |
✓✓ | 73296 |
else if (c == '[') { |
553 |
✓✓ | 8296 |
if (!in_bracket) { |
554 |
8252 |
saw_glob = 1; |
|
555 |
8252 |
in_bracket = 1; |
|
556 |
✓✓✓✓ |
8252 |
if (ISMAGIC(p[1]) && p[2] == '!') |
557 |
4 |
p += 2; |
|
558 |
✓✓✓✓ |
8252 |
if (ISMAGIC(p[1]) && p[2] == ']') |
559 |
2 |
p += 2; |
|
560 |
} |
||
561 |
/* XXX Do we need to check ranges here? POSIX Q */ |
||
562 |
✓✓ | 65000 |
} else if (c == ']') { |
563 |
✓✓ | 893 |
if (in_bracket) { |
564 |
✓✓ | 849 |
if (bnest) /* [a*(b]) */ |
565 |
2 |
return 0; |
|
566 |
847 |
in_bracket = 0; |
|
567 |
} |
||
568 |
✓✓✓✗ |
64107 |
} else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) { |
569 |
21183 |
saw_glob = 1; |
|
570 |
✓✓ | 21183 |
if (in_bracket) |
571 |
4 |
bnest++; |
|
572 |
else |
||
573 |
21179 |
nest++; |
|
574 |
✓✓ | 42924 |
} else if (c == '|') { |
575 |
✗✓ | 76 |
if (in_bracket && !bnest) /* *(a[foo|bar]) */ |
576 |
return 0; |
||
577 |
✓✓ | 42848 |
} else if (c == /*(*/ ')') { |
578 |
✓✓ | 21181 |
if (in_bracket) { |
579 |
✓✓ | 12 |
if (!bnest--) /* *(a[b)c] */ |
580 |
10 |
return 0; |
|
581 |
✓✗ | 21169 |
} else if (nest) |
582 |
21169 |
nest--; |
|
583 |
} |
||
584 |
/* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-] |
||
585 |
MAGIC-{, MAGIC-,, MAGIC-} */ |
||
586 |
} |
||
587 |
✓✓✓✗ |
48556 |
return saw_glob && !in_bracket && !nest; |
588 |
} |
||
589 |
|||
590 |
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ |
||
591 |
static int |
||
592 |
do_gmatch(const unsigned char *s, const unsigned char *se, |
||
593 |
const unsigned char *p, const unsigned char *pe) |
||
594 |
345775 |
{ |
|
595 |
int sc, pc; |
||
596 |
const unsigned char *prest, *psub, *pnext; |
||
597 |
const unsigned char *srest; |
||
598 |
|||
599 |
✗✓ | 345775 |
if (s == NULL || p == NULL) |
600 |
return 0; |
||
601 |
✓✓ | 401640 |
while (p < pe) { |
602 |
401188 |
pc = *p++; |
|
603 |
✓✓ | 401188 |
sc = s < se ? *s : '\0'; |
604 |
401188 |
s++; |
|
605 |
✓✓ | 401188 |
if (!ISMAGIC(pc)) { |
606 |
✓✓ | 360667 |
if (sc != pc) |
607 |
313495 |
return 0; |
|
608 |
continue; |
||
609 |
} |
||
610 |
✓✓✓✓ ✓✓✓ |
40521 |
switch (*p++) { |
611 |
case '[': |
||
612 |
✓✓✓✓ |
389 |
if (sc == 0 || (p = cclass(p, sc)) == NULL) |
613 |
46 |
return 0; |
|
614 |
break; |
||
615 |
|||
616 |
case '?': |
||
617 |
✓✓ | 35 |
if (sc == 0) |
618 |
11 |
return 0; |
|
619 |
break; |
||
620 |
|||
621 |
case '*': |
||
622 |
✓✓ | 8947 |
if (p == pe) |
623 |
2472 |
return 1; |
|
624 |
6475 |
s--; |
|
625 |
do { |
||
626 |
✓✓ | 263561 |
if (do_gmatch(s, se, p, pe)) |
627 |
650 |
return 1; |
|
628 |
✓✓ | 262911 |
} while (s++ < se); |
629 |
5825 |
return 0; |
|
630 |
|||
631 |
/* |
||
632 |
* [*+?@!](pattern|pattern|..) |
||
633 |
* |
||
634 |
* Not ifdef'd KSH as this is needed for ${..%..}, etc. |
||
635 |
*/ |
||
636 |
case 0x80|'+': /* matches one or more times */ |
||
637 |
case 0x80|'*': /* matches zero or more times */ |
||
638 |
✗✓ | 92 |
if (!(prest = pat_scan(p, pe, 0))) |
639 |
return 0; |
||
640 |
92 |
s--; |
|
641 |
/* take care of zero matches */ |
||
642 |
✓✓✗✓ |
92 |
if (p[-1] == (0x80 | '*') && |
643 |
do_gmatch(s, se, prest, pe)) |
||
644 |
return 1; |
||
645 |
138 |
for (psub = p; ; psub = pnext) { |
|
646 |
138 |
pnext = pat_scan(psub, pe, 1); |
|
647 |
✓✓ | 438 |
for (srest = s; srest <= se; srest++) { |
648 |
✓✓✓✓ ✓✓✓✓ |
324 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
649 |
(do_gmatch(srest, se, prest, pe) || |
||
650 |
(s != srest && do_gmatch(srest, |
||
651 |
se, p - 2, pe)))) |
||
652 |
24 |
return 1; |
|
653 |
} |
||
654 |
✓✓ | 114 |
if (pnext == prest) |
655 |
68 |
break; |
|
656 |
46 |
} |
|
657 |
68 |
return 0; |
|
658 |
|||
659 |
case 0x80|'?': /* matches zero or once */ |
||
660 |
case 0x80|'@': /* matches one of the patterns */ |
||
661 |
case 0x80|' ': /* simile for @ */ |
||
662 |
✗✓ | 21147 |
if (!(prest = pat_scan(p, pe, 0))) |
663 |
return 0; |
||
664 |
21147 |
s--; |
|
665 |
/* Take care of zero matches */ |
||
666 |
✗✓✗✗ |
21147 |
if (p[-1] == (0x80 | '?') && |
667 |
do_gmatch(s, se, prest, pe)) |
||
668 |
return 1; |
||
669 |
21201 |
for (psub = p; ; psub = pnext) { |
|
670 |
21201 |
pnext = pat_scan(psub, pe, 1); |
|
671 |
✓✗ | 21201 |
srest = prest == pe ? se : s; |
672 |
✓✓ | 42292 |
for (; srest <= se; srest++) { |
673 |
✓✓✓✗ |
21201 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
674 |
do_gmatch(srest, se, prest, pe)) |
||
675 |
110 |
return 1; |
|
676 |
} |
||
677 |
✓✓ | 21091 |
if (pnext == prest) |
678 |
21037 |
break; |
|
679 |
54 |
} |
|
680 |
21037 |
return 0; |
|
681 |
|||
682 |
case 0x80|'!': /* matches none of the patterns */ |
||
683 |
✗✓ | 10 |
if (!(prest = pat_scan(p, pe, 0))) |
684 |
return 0; |
||
685 |
10 |
s--; |
|
686 |
✓✓ | 42 |
for (srest = s; srest <= se; srest++) { |
687 |
34 |
int matched = 0; |
|
688 |
|||
689 |
48 |
for (psub = p; ; psub = pnext) { |
|
690 |
48 |
pnext = pat_scan(psub, pe, 1); |
|
691 |
✓✓ | 48 |
if (do_gmatch(s, srest, psub, |
692 |
pnext - 2)) { |
||
693 |
8 |
matched = 1; |
|
694 |
8 |
break; |
|
695 |
} |
||
696 |
✓✓ | 40 |
if (pnext == prest) |
697 |
26 |
break; |
|
698 |
14 |
} |
|
699 |
✓✓✓✓ |
34 |
if (!matched && |
700 |
do_gmatch(srest, se, prest, pe)) |
||
701 |
2 |
return 1; |
|
702 |
} |
||
703 |
8 |
return 0; |
|
704 |
|||
705 |
default: |
||
706 |
✓✓ | 9901 |
if (sc != p[-1]) |
707 |
1575 |
return 0; |
|
708 |
break; |
||
709 |
} |
||
710 |
} |
||
711 |
452 |
return s == se; |
|
712 |
} |
||
713 |
|||
714 |
static int |
||
715 |
posix_cclass(const unsigned char *pattern, int test, const unsigned char **ep) |
||
716 |
{ |
||
717 |
struct cclass *cc; |
||
718 |
const unsigned char *colon; |
||
719 |
size_t len; |
||
720 |
int rval = 0; |
||
721 |
|||
722 |
if ((colon = strchr(pattern, ':')) == NULL || colon[1] != MAGIC) { |
||
723 |
*ep = pattern - 2; |
||
724 |
return -1; |
||
725 |
} |
||
726 |
*ep = colon + 3; /* skip MAGIC */ |
||
727 |
len = (size_t)(colon - pattern); |
||
728 |
|||
729 |
for (cc = cclasses; cc->name != NULL; cc++) { |
||
730 |
if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') { |
||
731 |
if (cc->isctype(test)) |
||
732 |
rval = 1; |
||
733 |
break; |
||
734 |
} |
||
735 |
} |
||
736 |
if (cc->name == NULL) { |
||
737 |
rval = -2; /* invalid character class */ |
||
738 |
} |
||
739 |
return rval; |
||
740 |
} |
||
741 |
|||
742 |
static const unsigned char * |
||
743 |
cclass(const unsigned char *p, int sub) |
||
744 |
379 |
{ |
|
745 |
379 |
int c, d, rv, not, found = 0; |
|
746 |
379 |
const unsigned char *orig_p = p; |
|
747 |
|||
748 |
✓✓✓✓ ✓✓ |
379 |
if ((not = (ISMAGIC(*p) && *++p == '!'))) |
749 |
16 |
p++; |
|
750 |
do { |
||
751 |
/* check for POSIX character class (e.g. [[:alpha:]]) */ |
||
752 |
✓✓✗✓ ✗✗✗✓ ✗✗ |
981 |
if ((p[0] == MAGIC && p[1] == '[' && p[2] == ':') || |
753 |
(p[0] == '[' && p[1] == ':')) { |
||
754 |
do { |
||
755 |
const char *pp = p + (*p == MAGIC) + 2; |
||
756 |
rv = posix_cclass(pp, sub, &p); |
||
757 |
switch (rv) { |
||
758 |
case 1: |
||
759 |
found = 1; |
||
760 |
break; |
||
761 |
case -2: |
||
762 |
return NULL; |
||
763 |
} |
||
764 |
} while (rv != -1 && p[0] == MAGIC && p[1] == '[' && p[2] == ':'); |
||
765 |
if (p[0] == MAGIC && p[1] == ']') |
||
766 |
break; |
||
767 |
} |
||
768 |
|||
769 |
981 |
c = *p++; |
|
770 |
✓✓ | 981 |
if (ISMAGIC(c)) { |
771 |
46 |
c = *p++; |
|
772 |
✓✓✓✗ |
46 |
if ((c & 0x80) && !ISMAGIC(c)) { |
773 |
6 |
c &= 0x7f;/* extended pattern matching: *+?@! */ |
|
774 |
/* XXX the ( char isn't handled as part of [] */ |
||
775 |
✗✓ | 6 |
if (c == ' ') /* simile for @: plain (..) */ |
776 |
c = '(' /*)*/; |
||
777 |
} |
||
778 |
} |
||
779 |
✗✓ | 981 |
if (c == '\0') |
780 |
/* No closing ] - act as if the opening [ was quoted */ |
||
781 |
return sub == '[' ? orig_p : NULL; |
||
782 |
✓✓✓✓ ✓✓✓✓ |
981 |
if (ISMAGIC(p[0]) && p[1] == '-' && |
783 |
(!ISMAGIC(p[2]) || p[3] != ']')) { |
||
784 |
30 |
p += 2; /* MAGIC- */ |
|
785 |
30 |
d = *p++; |
|
786 |
✓✓ | 30 |
if (ISMAGIC(d)) { |
787 |
2 |
d = *p++; |
|
788 |
✗✓✗✗ |
2 |
if ((d & 0x80) && !ISMAGIC(d)) |
789 |
d &= 0x7f; |
||
790 |
} |
||
791 |
/* POSIX says this is an invalid expression */ |
||
792 |
✓✓ | 30 |
if (c > d) |
793 |
2 |
return NULL; |
|
794 |
} else |
||
795 |
951 |
d = c; |
|
796 |
✓✓✓✓ |
979 |
if (c == sub || (c <= sub && sub <= d)) |
797 |
351 |
found = 1; |
|
798 |
✓✓✓✓ |
979 |
} while (!(ISMAGIC(p[0]) && p[1] == ']')); |
799 |
|||
800 |
✓✓ | 377 |
return (found != not) ? p+2 : NULL; |
801 |
} |
||
802 |
|||
803 |
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ |
||
804 |
const unsigned char * |
||
805 |
pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep) |
||
806 |
42636 |
{ |
|
807 |
42636 |
int nest = 0; |
|
808 |
|||
809 |
✓✗ | 276478 |
for (; p < pe; p++) { |
810 |
✓✓ | 276478 |
if (!ISMAGIC(*p)) |
811 |
231834 |
continue; |
|
812 |
✓✓✓✓ ✓✓✓✓ |
44644 |
if ((*++p == /*(*/ ')' && nest-- == 0) || |
813 |
(*p == '|' && match_sep && nest == 0)) |
||
814 |
42636 |
return ++p; |
|
815 |
✓✓✓✗ |
2008 |
if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f)) |
816 |
100 |
nest++; |
|
817 |
} |
||
818 |
return NULL; |
||
819 |
} |
||
820 |
|||
821 |
/* |
||
822 |
* quick sort of array of generic pointers to objects. |
||
823 |
*/ |
||
824 |
void |
||
825 |
qsortp(void **base, /* base address */ |
||
826 |
size_t n, /* elements */ |
||
827 |
int (*f) (const void *, const void *)) /* compare function */ |
||
828 |
7507 |
{ |
|
829 |
7507 |
qsort(base, n, sizeof(char *), f); |
|
830 |
7507 |
} |
|
831 |
|||
832 |
int |
||
833 |
xstrcmp(const void *p1, const void *p2) |
||
834 |
195 |
{ |
|
835 |
195 |
return (strcmp(*(char **)p1, *(char **)p2)); |
|
836 |
} |
||
837 |
|||
838 |
/* Initialize a Getopt structure */ |
||
839 |
void |
||
840 |
ksh_getopt_reset(Getopt *go, int flags) |
||
841 |
180118 |
{ |
|
842 |
180118 |
go->optind = 1; |
|
843 |
180118 |
go->optarg = NULL; |
|
844 |
180118 |
go->p = 0; |
|
845 |
180118 |
go->flags = flags; |
|
846 |
180118 |
go->info = 0; |
|
847 |
180118 |
go->buf[1] = '\0'; |
|
848 |
180118 |
} |
|
849 |
|||
850 |
|||
851 |
/* getopt() used for shell built-in commands, the getopts command, and |
||
852 |
* command line options. |
||
853 |
* A leading ':' in options means don't print errors, instead return '?' |
||
854 |
* or ':' and set go->optarg to the offending option character. |
||
855 |
* If GF_ERROR is set (and option doesn't start with :), errors result in |
||
856 |
* a call to bi_errorf(). |
||
857 |
* |
||
858 |
* Non-standard features: |
||
859 |
* - ';' is like ':' in options, except the argument is optional |
||
860 |
* (if it isn't present, optarg is set to 0). |
||
861 |
* Used for 'set -o'. |
||
862 |
* - ',' is like ':' in options, except the argument always immediately |
||
863 |
* follows the option character (optarg is set to the null string if |
||
864 |
* the option is missing). |
||
865 |
* Used for 'read -u2', 'print -u2' and fc -40. |
||
866 |
* - '#' is like ':' in options, expect that the argument is optional |
||
867 |
* and must start with a digit or be the string "unlimited". If the |
||
868 |
* argument doesn't match, it is assumed to be missing and normal option |
||
869 |
* processing continues (optarg is set to 0 if the option is missing). |
||
870 |
* Used for 'typeset -LZ4' and 'ulimit -adunlimited'. |
||
871 |
* - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an |
||
872 |
* option starting with + is accepted, the GI_PLUS flag will be set |
||
873 |
* in go->info. |
||
874 |
*/ |
||
875 |
int |
||
876 |
ksh_getopt(char **argv, Getopt *go, const char *options) |
||
877 |
74976 |
{ |
|
878 |
char c; |
||
879 |
char *o; |
||
880 |
|||
881 |
✓✓✓✓ |
74976 |
if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { |
882 |
✓✓ | 69441 |
char *arg = argv[go->optind], flag = arg ? *arg : '\0'; |
883 |
|||
884 |
69441 |
go->p = 1; |
|
885 |
✓✓✓✓ ✓✗ |
69441 |
if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { |
886 |
1677 |
go->optind++; |
|
887 |
1677 |
go->p = 0; |
|
888 |
1677 |
go->info |= GI_MINUSMINUS; |
|
889 |
1677 |
return -1; |
|
890 |
} |
||
891 |
✓✓✓✓ ✓✓✓✓ |
67764 |
if (arg == NULL || |
892 |
((flag != '-' ) && /* neither a - nor a + (if + allowed) */ |
||
893 |
(!(go->flags & GF_PLUSOPT) || flag != '+')) || |
||
894 |
(c = arg[1]) == '\0') { |
||
895 |
40181 |
go->p = 0; |
|
896 |
40181 |
return -1; |
|
897 |
} |
||
898 |
27583 |
go->optind++; |
|
899 |
27583 |
go->info &= ~(GI_MINUS|GI_PLUS); |
|
900 |
✓✓ | 27583 |
go->info |= flag == '-' ? GI_MINUS : GI_PLUS; |
901 |
} |
||
902 |
33118 |
go->p++; |
|
903 |
✓✗✓✗ ✓✗✓✓ |
33118 |
if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || |
904 |
!(o = strchr(options, c))) { |
||
905 |
✓✗ | 50 |
if (options[0] == ':') { |
906 |
50 |
go->buf[0] = c; |
|
907 |
50 |
go->optarg = go->buf; |
|
908 |
} else { |
||
909 |
warningf(true, "%s%s-%c: unknown option", |
||
910 |
(go->flags & GF_NONAME) ? "" : argv[0], |
||
911 |
(go->flags & GF_NONAME) ? "" : ": ", c); |
||
912 |
if (go->flags & GF_ERROR) |
||
913 |
bi_errorf(NULL); |
||
914 |
} |
||
915 |
50 |
return '?'; |
|
916 |
} |
||
917 |
/* : means argument must be present, may be part of option argument |
||
918 |
* or the next argument |
||
919 |
* ; same as : but argument may be missing |
||
920 |
* , means argument is part of option argument, and may be null. |
||
921 |
*/ |
||
922 |
✓✓✓✓ |
33118 |
if (*++o == ':' || *o == ';') { |
923 |
✗✓ | 50 |
if (argv[go->optind - 1][go->p]) |
924 |
go->optarg = argv[go->optind - 1] + go->p; |
||
925 |
✓✓ | 50 |
else if (argv[go->optind]) |
926 |
42 |
go->optarg = argv[go->optind++]; |
|
927 |
✓✗ | 8 |
else if (*o == ';') |
928 |
8 |
go->optarg = NULL; |
|
929 |
else { |
||
930 |
if (options[0] == ':') { |
||
931 |
go->buf[0] = c; |
||
932 |
go->optarg = go->buf; |
||
933 |
return ':'; |
||
934 |
} |
||
935 |
warningf(true, "%s%s-`%c' requires argument", |
||
936 |
(go->flags & GF_NONAME) ? "" : argv[0], |
||
937 |
(go->flags & GF_NONAME) ? "" : ": ", c); |
||
938 |
if (go->flags & GF_ERROR) |
||
939 |
bi_errorf(NULL); |
||
940 |
return '?'; |
||
941 |
} |
||
942 |
50 |
go->p = 0; |
|
943 |
✓✓ | 33018 |
} else if (*o == ',') { |
944 |
/* argument is attached to option character, even if null */ |
||
945 |
16 |
go->optarg = argv[go->optind - 1] + go->p; |
|
946 |
16 |
go->p = 0; |
|
947 |
✓✓ | 33002 |
} else if (*o == '#') { |
948 |
/* argument is optional and may be attached or unattached |
||
949 |
* but must start with a digit. optarg is set to 0 if the |
||
950 |
* argument is missing. |
||
951 |
*/ |
||
952 |
✓✓ | 10657 |
if (argv[go->optind - 1][go->p]) { |
953 |
✗✓✗✗ |
44 |
if (digit(argv[go->optind - 1][go->p]) || |
954 |
!strcmp(&argv[go->optind - 1][go->p], "unlimited")) { |
||
955 |
22 |
go->optarg = argv[go->optind - 1] + go->p; |
|
956 |
22 |
go->p = 0; |
|
957 |
} else |
||
958 |
go->optarg = NULL; |
||
959 |
} else { |
||
960 |
✓✓✓✓ ✗✓ |
10639 |
if (argv[go->optind] && (digit(argv[go->optind][0]) || |
961 |
!strcmp(argv[go->optind], "unlimited"))) { |
||
962 |
4 |
go->optarg = argv[go->optind++]; |
|
963 |
4 |
go->p = 0; |
|
964 |
} else |
||
965 |
10631 |
go->optarg = NULL; |
|
966 |
} |
||
967 |
} |
||
968 |
33068 |
return c; |
|
969 |
} |
||
970 |
|||
971 |
/* print variable/alias value using necessary quotes |
||
972 |
* (POSIX says they should be suitable for re-entry...) |
||
973 |
* No trailing newline is printed. |
||
974 |
*/ |
||
975 |
void |
||
976 |
print_value_quoted(const char *s) |
||
977 |
6319 |
{ |
|
978 |
const char *p; |
||
979 |
6319 |
int inquote = 0; |
|
980 |
|||
981 |
/* Test if any quotes are needed */ |
||
982 |
✓✓ | 36309 |
for (p = s; *p; p++) |
983 |
✓✓ | 31462 |
if (ctype(*p, C_QUOTE)) |
984 |
1472 |
break; |
|
985 |
✓✓ | 6319 |
if (!*p) { |
986 |
4847 |
shprintf("%s", s); |
|
987 |
4847 |
return; |
|
988 |
} |
||
989 |
✓✓ | 75241 |
for (p = s; *p; p++) { |
990 |
✓✓ | 73769 |
if (*p == '\'') { |
991 |
✓✓ | 490 |
shprintf(inquote ? "'\\'" : "\\'"); |
992 |
490 |
inquote = 0; |
|
993 |
} else { |
||
994 |
✓✓ | 73279 |
if (!inquote) { |
995 |
1920 |
shprintf("'"); |
|
996 |
1920 |
inquote = 1; |
|
997 |
} |
||
998 |
✓✓ | 73279 |
shf_putc(*p, shl_stdout); |
999 |
} |
||
1000 |
} |
||
1001 |
✓✓ | 1472 |
if (inquote) |
1002 |
1433 |
shprintf("'"); |
|
1003 |
} |
||
1004 |
|||
1005 |
/* Print things in columns and rows - func() is called to format the ith |
||
1006 |
* element |
||
1007 |
*/ |
||
1008 |
void |
||
1009 |
print_columns(struct shf *shf, int n, char *(*func) (void *, int, char *, int), |
||
1010 |
void *arg, int max_width, int prefcol) |
||
1011 |
8 |
{ |
|
1012 |
8 |
char *str = alloc(max_width + 1, ATEMP); |
|
1013 |
int i; |
||
1014 |
int r, c; |
||
1015 |
int rows, cols; |
||
1016 |
int nspace; |
||
1017 |
int col_width; |
||
1018 |
|||
1019 |
/* max_width + 1 for the space. Note that no space |
||
1020 |
* is printed after the last column to avoid problems |
||
1021 |
* with terminals that have auto-wrap. |
||
1022 |
*/ |
||
1023 |
8 |
cols = x_cols / (max_width + 1); |
|
1024 |
✗✓ | 8 |
if (!cols) |
1025 |
cols = 1; |
||
1026 |
8 |
rows = (n + cols - 1) / cols; |
|
1027 |
✓✗✗✓ |
8 |
if (prefcol && n && cols > rows) { |
1028 |
int tmp = rows; |
||
1029 |
|||
1030 |
rows = cols; |
||
1031 |
cols = tmp; |
||
1032 |
if (rows > n) |
||
1033 |
rows = n; |
||
1034 |
} |
||
1035 |
|||
1036 |
8 |
col_width = max_width; |
|
1037 |
✗✓ | 8 |
if (cols == 1) |
1038 |
col_width = 0; /* Don't pad entries in single column output. */ |
||
1039 |
8 |
nspace = (x_cols - max_width * cols) / cols; |
|
1040 |
✗✓ | 8 |
if (nspace <= 0) |
1041 |
nspace = 1; |
||
1042 |
✓✓ | 80 |
for (r = 0; r < rows; r++) { |
1043 |
✓✓ | 360 |
for (c = 0; c < cols; c++) { |
1044 |
288 |
i = c * rows + r; |
|
1045 |
✓✓ | 288 |
if (i < n) { |
1046 |
280 |
shf_fprintf(shf, "%-*s", |
|
1047 |
col_width, |
||
1048 |
(*func)(arg, i, str, max_width + 1)); |
||
1049 |
✓✓ | 280 |
if (c + 1 < cols) |
1050 |
216 |
shf_fprintf(shf, "%*s", nspace, ""); |
|
1051 |
} |
||
1052 |
} |
||
1053 |
72 |
shf_putchar('\n', shf); |
|
1054 |
} |
||
1055 |
8 |
afree(str, ATEMP); |
|
1056 |
8 |
} |
|
1057 |
|||
1058 |
/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ |
||
1059 |
int |
||
1060 |
strip_nuls(char *buf, int nbytes) |
||
1061 |
66834 |
{ |
|
1062 |
char *dst; |
||
1063 |
|||
1064 |
✗✓ | 66834 |
if ((dst = memchr(buf, '\0', nbytes))) { |
1065 |
char *end = buf + nbytes; |
||
1066 |
char *p, *q; |
||
1067 |
|||
1068 |
for (p = dst; p < end; p = q) { |
||
1069 |
/* skip a block of nulls */ |
||
1070 |
while (++p < end && *p == '\0') |
||
1071 |
; |
||
1072 |
/* find end of non-null block */ |
||
1073 |
if (!(q = memchr(p, '\0', end - p))) |
||
1074 |
q = end; |
||
1075 |
memmove(dst, p, q - p); |
||
1076 |
dst += q - p; |
||
1077 |
} |
||
1078 |
*dst = '\0'; |
||
1079 |
return dst - buf; |
||
1080 |
} |
||
1081 |
66834 |
return nbytes; |
|
1082 |
} |
||
1083 |
|||
1084 |
/* Like read(2), but if read fails due to non-blocking flag, resets flag |
||
1085 |
* and restarts read. |
||
1086 |
*/ |
||
1087 |
int |
||
1088 |
blocking_read(int fd, char *buf, int nbytes) |
||
1089 |
16105 |
{ |
|
1090 |
int ret; |
||
1091 |
16105 |
int tried_reset = 0; |
|
1092 |
|||
1093 |
✓✓ | 32210 |
while ((ret = read(fd, buf, nbytes)) < 0) { |
1094 |
✓✗✗✓ |
2762 |
if (!tried_reset && errno == EAGAIN) { |
1095 |
int oerrno = errno; |
||
1096 |
if (reset_nonblock(fd) > 0) { |
||
1097 |
tried_reset = 1; |
||
1098 |
continue; |
||
1099 |
} |
||
1100 |
errno = oerrno; |
||
1101 |
} |
||
1102 |
break; |
||
1103 |
} |
||
1104 |
16105 |
return ret; |
|
1105 |
} |
||
1106 |
|||
1107 |
/* Reset the non-blocking flag on the specified file descriptor. |
||
1108 |
* Returns -1 if there was an error, 0 if non-blocking wasn't set, |
||
1109 |
* 1 if it was. |
||
1110 |
*/ |
||
1111 |
int |
||
1112 |
reset_nonblock(int fd) |
||
1113 |
7 |
{ |
|
1114 |
int flags; |
||
1115 |
|||
1116 |
✗✓ | 7 |
if ((flags = fcntl(fd, F_GETFL)) < 0) |
1117 |
return -1; |
||
1118 |
✓✗ | 7 |
if (!(flags & O_NONBLOCK)) |
1119 |
7 |
return 0; |
|
1120 |
flags &= ~O_NONBLOCK; |
||
1121 |
if (fcntl(fd, F_SETFL, flags) < 0) |
||
1122 |
return -1; |
||
1123 |
return 1; |
||
1124 |
} |
||
1125 |
|||
1126 |
|||
1127 |
/* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */ |
||
1128 |
char * |
||
1129 |
ksh_get_wd(char *buf, int bsize) |
||
1130 |
464 |
{ |
|
1131 |
char *b; |
||
1132 |
char *ret; |
||
1133 |
|||
1134 |
/* Note: we could just use plain getcwd(), but then we'd had to |
||
1135 |
* inject possibly allocated space into the ATEMP area. */ |
||
1136 |
/* Assume getcwd() available */ |
||
1137 |
✓✗ | 464 |
if (!buf) { |
1138 |
464 |
bsize = PATH_MAX; |
|
1139 |
464 |
b = alloc(PATH_MAX + 1, ATEMP); |
|
1140 |
} else |
||
1141 |
b = buf; |
||
1142 |
|||
1143 |
464 |
ret = getcwd(b, bsize); |
|
1144 |
|||
1145 |
✓✗ | 464 |
if (!buf) { |
1146 |
✓✗ | 464 |
if (ret) |
1147 |
464 |
ret = aresize(b, strlen(b) + 1, ATEMP); |
|
1148 |
else |
||
1149 |
afree(b, ATEMP); |
||
1150 |
} |
||
1151 |
|||
1152 |
464 |
return ret; |
|
1153 |
} |
Generated by: GCOVR (Version 3.3) |