1 |
/* $OpenBSD: misc.c,v 1.39 2015/01/16 06:39:32 deraadt Exp $ */ |
2 |
/* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */ |
3 |
|
4 |
/*- |
5 |
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, |
6 |
* 2011, 2012, 2013, 2014, 2015 |
7 |
* Thorsten Glaser <tg@mirbsd.org> |
8 |
* |
9 |
* Provided that these terms and disclaimer and all copyright notices |
10 |
* are retained or reproduced in an accompanying document, permission |
11 |
* is granted to deal in this work without restriction, including un- |
12 |
* limited rights to use, publicly perform, distribute, sell, modify, |
13 |
* merge, give away, or sublicence. |
14 |
* |
15 |
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to |
16 |
* the utmost extent permitted by applicable law, neither express nor |
17 |
* implied; without malicious intent or gross negligence. In no event |
18 |
* may a licensor, author or contributor be held liable for indirect, |
19 |
* direct, other damage, loss, or other issues arising in any way out |
20 |
* of dealing in the work, even if advised of the possibility of such |
21 |
* damage or existence of a defect, except proven that it results out |
22 |
* of said person's immediate fault when using the work as intended. |
23 |
*/ |
24 |
|
25 |
#include "sh.h" |
26 |
#if !HAVE_GETRUSAGE |
27 |
#include <sys/times.h> |
28 |
#endif |
29 |
#if HAVE_GRP_H |
30 |
#include <grp.h> |
31 |
#endif |
32 |
|
33 |
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.219.2.2 2015/03/01 15:43:02 tg Exp $"); |
34 |
|
35 |
#define KSH_CHVT_FLAG |
36 |
#ifdef MKSH_SMALL |
37 |
#undef KSH_CHVT_FLAG |
38 |
#endif |
39 |
#ifdef TIOCSCTTY |
40 |
#define KSH_CHVT_CODE |
41 |
#define KSH_CHVT_FLAG |
42 |
#endif |
43 |
#ifdef MKSH_LEGACY_MODE |
44 |
#undef KSH_CHVT_CODE |
45 |
#undef KSH_CHVT_FLAG |
46 |
#endif |
47 |
|
48 |
/* type bits for unsigned char */ |
49 |
unsigned char chtypes[UCHAR_MAX + 1]; |
50 |
|
51 |
static const unsigned char *pat_scan(const unsigned char *, |
52 |
const unsigned char *, bool) MKSH_A_PURE; |
53 |
static int do_gmatch(const unsigned char *, const unsigned char *, |
54 |
const unsigned char *, const unsigned char *) MKSH_A_PURE; |
55 |
static const unsigned char *cclass(const unsigned char *, unsigned char) |
56 |
MKSH_A_PURE; |
57 |
#ifdef KSH_CHVT_CODE |
58 |
static void chvt(const Getopt *); |
59 |
#endif |
60 |
|
61 |
/*XXX this should go away */ |
62 |
static int make_path(const char *, const char *, char **, XString *, int *); |
63 |
|
64 |
#ifdef SETUID_CAN_FAIL_WITH_EAGAIN |
65 |
/* we don't need to check for other codes, EPERM won't happen */ |
66 |
#define DO_SETUID(func, argvec) do { \ |
67 |
if ((func argvec) && errno == EAGAIN) \ |
68 |
errorf("%s failed with EAGAIN, probably due to a" \ |
69 |
" too low process limit; aborting", #func); \ |
70 |
} while (/* CONSTCOND */ 0) |
71 |
#else |
72 |
#define DO_SETUID(func, argvec) func argvec |
73 |
#endif |
74 |
|
75 |
/* |
76 |
* Fast character classes |
77 |
*/ |
78 |
void |
79 |
setctypes(const char *s, int t) |
80 |
{ |
81 |
unsigned int i; |
82 |
|
83 |
if (t & C_IFS) { |
84 |
for (i = 0; i < UCHAR_MAX + 1; i++) |
85 |
chtypes[i] &= ~C_IFS; |
86 |
/* include \0 in C_IFS */ |
87 |
chtypes[0] |= C_IFS; |
88 |
} |
89 |
while (*s != 0) |
90 |
chtypes[(unsigned char)*s++] |= t; |
91 |
} |
92 |
|
93 |
void |
94 |
initctypes(void) |
95 |
{ |
96 |
int c; |
97 |
|
98 |
for (c = 'a'; c <= 'z'; c++) |
99 |
chtypes[c] |= C_ALPHA; |
100 |
for (c = 'A'; c <= 'Z'; c++) |
101 |
chtypes[c] |= C_ALPHA; |
102 |
chtypes['_'] |= C_ALPHA; |
103 |
setctypes("0123456789", C_DIGIT); |
104 |
/* \0 added automatically */ |
105 |
setctypes(TC_LEX1, C_LEX1); |
106 |
setctypes("*@#!$-?", C_VAR1); |
107 |
setctypes(TC_IFSWS, C_IFSWS); |
108 |
setctypes("=-+?", C_SUBOP1); |
109 |
setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE); |
110 |
} |
111 |
|
112 |
/* called from XcheckN() to grow buffer */ |
113 |
char * |
114 |
Xcheck_grow(XString *xsp, const char *xp, size_t more) |
115 |
{ |
116 |
const char *old_beg = xsp->beg; |
117 |
|
118 |
if (more < xsp->len) |
119 |
more = xsp->len; |
120 |
/* (xsp->len + X_EXTRA) never overflows */ |
121 |
checkoktoadd(more, xsp->len + X_EXTRA); |
122 |
xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap); |
123 |
xsp->end = xsp->beg + xsp->len; |
124 |
return (xsp->beg + (xp - old_beg)); |
125 |
} |
126 |
|
127 |
|
128 |
#define SHFLAGS_DEFNS |
129 |
#include "sh_flags.gen" |
130 |
|
131 |
#define OFC(i) (options[i][-2]) |
132 |
#define OFF(i) (((const unsigned char *)options[i])[-1]) |
133 |
#define OFN(i) (options[i]) |
134 |
|
135 |
const char * const options[] = { |
136 |
#define SHFLAGS_ITEMS |
137 |
#include "sh_flags.gen" |
138 |
}; |
139 |
|
140 |
/* |
141 |
* translate -o option into F* constant (also used for test -o option) |
142 |
*/ |
143 |
size_t |
144 |
option(const char *n) |
145 |
{ |
146 |
size_t i = 0; |
147 |
|
148 |
if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2]) |
149 |
while (i < NELEM(options)) { |
150 |
if (OFC(i) == n[1]) |
151 |
return (i); |
152 |
++i; |
153 |
} |
154 |
else |
155 |
while (i < NELEM(options)) { |
156 |
if (!strcmp(OFN(i), n)) |
157 |
return (i); |
158 |
++i; |
159 |
} |
160 |
|
161 |
return ((size_t)-1); |
162 |
} |
163 |
|
164 |
struct options_info { |
165 |
int opt_width; |
166 |
int opts[NELEM(options)]; |
167 |
}; |
168 |
|
169 |
static char *options_fmt_entry(char *, size_t, unsigned int, const void *); |
170 |
static void printoptions(bool); |
171 |
|
172 |
/* format a single select menu item */ |
173 |
static char * |
174 |
options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) |
175 |
{ |
176 |
const struct options_info *oi = (const struct options_info *)arg; |
177 |
|
178 |
shf_snprintf(buf, buflen, "%-*s %s", |
179 |
oi->opt_width, OFN(oi->opts[i]), |
180 |
Flag(oi->opts[i]) ? "on" : "off"); |
181 |
return (buf); |
182 |
} |
183 |
|
184 |
static void |
185 |
printoptions(bool verbose) |
186 |
{ |
187 |
size_t i = 0; |
188 |
|
189 |
if (verbose) { |
190 |
size_t n = 0, len, octs = 0; |
191 |
struct options_info oi; |
192 |
|
193 |
/* verbose version */ |
194 |
shf_puts("Current option settings\n", shl_stdout); |
195 |
|
196 |
oi.opt_width = 0; |
197 |
while (i < NELEM(options)) { |
198 |
if ((len = strlen(OFN(i)))) { |
199 |
oi.opts[n++] = i; |
200 |
if (len > octs) |
201 |
octs = len; |
202 |
len = utf_mbswidth(OFN(i)); |
203 |
if ((int)len > oi.opt_width) |
204 |
oi.opt_width = (int)len; |
205 |
} |
206 |
++i; |
207 |
} |
208 |
print_columns(shl_stdout, n, options_fmt_entry, &oi, |
209 |
octs + 4, oi.opt_width + 4, true); |
210 |
} else { |
211 |
/* short version like AT&T ksh93 */ |
212 |
shf_puts(Tset, shl_stdout); |
213 |
while (i < NELEM(options)) { |
214 |
if (Flag(i) && OFN(i)[0]) |
215 |
shprintf(" -o %s", OFN(i)); |
216 |
++i; |
217 |
} |
218 |
shf_putc('\n', shl_stdout); |
219 |
} |
220 |
} |
221 |
|
222 |
char * |
223 |
getoptions(void) |
224 |
{ |
225 |
size_t i = 0; |
226 |
char c, m[(int)FNFLAGS + 1]; |
227 |
char *cp = m; |
228 |
|
229 |
while (i < NELEM(options)) { |
230 |
if ((c = OFC(i)) && Flag(i)) |
231 |
*cp++ = c; |
232 |
++i; |
233 |
} |
234 |
strndupx(cp, m, cp - m, ATEMP); |
235 |
return (cp); |
236 |
} |
237 |
|
238 |
/* change a Flag(*) value; takes care of special actions */ |
239 |
void |
240 |
change_flag(enum sh_flag f, int what, bool newset) |
241 |
{ |
242 |
unsigned char oldval; |
243 |
unsigned char newval = (newset ? 1 : 0); |
244 |
|
245 |
if (f == FXTRACE) { |
246 |
change_xtrace(newval, true); |
247 |
return; |
248 |
} |
249 |
oldval = Flag(f); |
250 |
Flag(f) = newval = (newset ? 1 : 0); |
251 |
#ifndef MKSH_UNEMPLOYED |
252 |
if (f == FMONITOR) { |
253 |
if (what != OF_CMDLINE && newval != oldval) |
254 |
j_change(); |
255 |
} else |
256 |
#endif |
257 |
#ifndef MKSH_NO_CMDLINE_EDITING |
258 |
if (( |
259 |
#if !MKSH_S_NOVI |
260 |
f == FVI || |
261 |
#endif |
262 |
f == FEMACS || f == FGMACS) && newval) { |
263 |
#if !MKSH_S_NOVI |
264 |
Flag(FVI) = |
265 |
#endif |
266 |
Flag(FEMACS) = Flag(FGMACS) = 0; |
267 |
Flag(f) = newval; |
268 |
} else |
269 |
#endif |
270 |
if (f == FPRIVILEGED && oldval && !newval) { |
271 |
/* Turning off -p? */ |
272 |
|
273 |
/*XXX this can probably be optimised */ |
274 |
kshegid = kshgid = getgid(); |
275 |
ksheuid = kshuid = getuid(); |
276 |
#if HAVE_SETRESUGID |
277 |
DO_SETUID(setresgid, (kshegid, kshegid, kshegid)); |
278 |
#if HAVE_SETGROUPS |
279 |
/* setgroups doesn't EAGAIN on Linux */ |
280 |
setgroups(1, &kshegid); |
281 |
#endif |
282 |
DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid)); |
283 |
#else /* !HAVE_SETRESUGID */ |
284 |
/* setgid, setegid, seteuid don't EAGAIN on Linux */ |
285 |
setgid(kshegid); |
286 |
#ifndef MKSH__NO_SETEUGID |
287 |
setegid(kshegid); |
288 |
#endif |
289 |
DO_SETUID(setuid, (ksheuid)); |
290 |
#ifndef MKSH__NO_SETEUGID |
291 |
seteuid(ksheuid); |
292 |
#endif |
293 |
#endif /* !HAVE_SETRESUGID */ |
294 |
} else if ((f == FPOSIX || f == FSH) && newval) { |
295 |
/* Turning on -o posix or -o sh? */ |
296 |
Flag(FBRACEEXPAND) = 0; |
297 |
} else if (f == FTALKING) { |
298 |
/* Changing interactive flag? */ |
299 |
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid) |
300 |
Flag(FTALKING_I) = newval; |
301 |
} |
302 |
} |
303 |
|
304 |
void |
305 |
change_xtrace(unsigned char newval, bool dosnapshot) |
306 |
{ |
307 |
static bool in_xtrace; |
308 |
|
309 |
if (in_xtrace) |
310 |
return; |
311 |
|
312 |
if (!dosnapshot && newval == Flag(FXTRACE)) |
313 |
return; |
314 |
|
315 |
if (Flag(FXTRACE) == 2) { |
316 |
shf_putc('\n', shl_xtrace); |
317 |
Flag(FXTRACE) = 1; |
318 |
shf_flush(shl_xtrace); |
319 |
} |
320 |
|
321 |
if (!dosnapshot && Flag(FXTRACE) == 1) |
322 |
switch (newval) { |
323 |
case 1: |
324 |
return; |
325 |
case 2: |
326 |
goto changed_xtrace; |
327 |
} |
328 |
|
329 |
shf_flush(shl_xtrace); |
330 |
if (shl_xtrace->fd != 2) |
331 |
close(shl_xtrace->fd); |
332 |
if (!newval || (shl_xtrace->fd = savefd(2)) == -1) |
333 |
shl_xtrace->fd = 2; |
334 |
|
335 |
changed_xtrace: |
336 |
if ((Flag(FXTRACE) = newval) == 2) { |
337 |
in_xtrace = true; |
338 |
Flag(FXTRACE) = 0; |
339 |
shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace); |
340 |
Flag(FXTRACE) = 2; |
341 |
in_xtrace = false; |
342 |
} |
343 |
} |
344 |
|
345 |
/* |
346 |
* Parse command line and set command arguments. Returns the index of |
347 |
* non-option arguments, -1 if there is an error. |
348 |
*/ |
349 |
int |
350 |
parse_args(const char **argv, |
351 |
/* OF_CMDLINE or OF_SET */ |
352 |
int what, |
353 |
bool *setargsp) |
354 |
{ |
355 |
static const char cmd_opts[] = |
356 |
#define SHFLAGS_NOT_SET |
357 |
#define SHFLAGS_OPTCS |
358 |
#include "sh_flags.gen" |
359 |
#undef SHFLAGS_NOT_SET |
360 |
; |
361 |
static const char set_opts[] = |
362 |
#define SHFLAGS_NOT_CMD |
363 |
#define SHFLAGS_OPTCS |
364 |
#include "sh_flags.gen" |
365 |
#undef SHFLAGS_NOT_CMD |
366 |
; |
367 |
bool set; |
368 |
const char *opts; |
369 |
const char *array = NULL; |
370 |
Getopt go; |
371 |
size_t i; |
372 |
int optc, arrayset = 0; |
373 |
bool sortargs = false; |
374 |
bool fcompatseen = false; |
375 |
|
376 |
if (what == OF_CMDLINE) { |
377 |
const char *p = argv[0], *q; |
378 |
/* |
379 |
* Set FLOGIN before parsing options so user can clear |
380 |
* flag using +l. |
381 |
*/ |
382 |
if (*p != '-') |
383 |
for (q = p; *q; ) |
384 |
if (*q++ == '/') |
385 |
p = q; |
386 |
Flag(FLOGIN) = (*p == '-'); |
387 |
opts = cmd_opts; |
388 |
} else if (what == OF_FIRSTTIME) { |
389 |
opts = cmd_opts; |
390 |
} else |
391 |
opts = set_opts; |
392 |
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT); |
393 |
while ((optc = ksh_getopt(argv, &go, opts)) != -1) { |
394 |
set = tobool(!(go.info & GI_PLUS)); |
395 |
switch (optc) { |
396 |
case 'A': |
397 |
if (what == OF_FIRSTTIME) |
398 |
break; |
399 |
arrayset = set ? 1 : -1; |
400 |
array = go.optarg; |
401 |
break; |
402 |
|
403 |
case 'o': |
404 |
if (what == OF_FIRSTTIME) |
405 |
break; |
406 |
if (go.optarg == NULL) { |
407 |
/* |
408 |
* lone -o: print options |
409 |
* |
410 |
* Note that on the command line, -o requires |
411 |
* an option (ie, can't get here if what is |
412 |
* OF_CMDLINE). |
413 |
*/ |
414 |
printoptions(set); |
415 |
break; |
416 |
} |
417 |
i = option(go.optarg); |
418 |
if ((i == FPOSIX || i == FSH) && set && !fcompatseen) { |
419 |
/* |
420 |
* If running 'set -o posix' or |
421 |
* 'set -o sh', turn off the other; |
422 |
* if running 'set -o posix -o sh' |
423 |
* allow both to be set though. |
424 |
*/ |
425 |
Flag(FPOSIX) = 0; |
426 |
Flag(FSH) = 0; |
427 |
fcompatseen = true; |
428 |
} |
429 |
if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i)) |
430 |
/* |
431 |
* Don't check the context if the flag |
432 |
* isn't changing - makes "set -o interactive" |
433 |
* work if you're already interactive. Needed |
434 |
* if the output of "set +o" is to be used. |
435 |
*/ |
436 |
; |
437 |
else if ((i != (size_t)-1) && (OFF(i) & what)) |
438 |
change_flag((enum sh_flag)i, what, set); |
439 |
else { |
440 |
bi_errorf("%s: %s", go.optarg, "bad option"); |
441 |
return (-1); |
442 |
} |
443 |
break; |
444 |
|
445 |
#ifdef KSH_CHVT_FLAG |
446 |
case 'T': |
447 |
if (what != OF_FIRSTTIME) |
448 |
break; |
449 |
#ifndef KSH_CHVT_CODE |
450 |
errorf("no TIOCSCTTY ioctl"); |
451 |
#else |
452 |
change_flag(FTALKING, OF_CMDLINE, true); |
453 |
chvt(&go); |
454 |
break; |
455 |
#endif |
456 |
#endif |
457 |
|
458 |
case '?': |
459 |
return (-1); |
460 |
|
461 |
default: |
462 |
if (what == OF_FIRSTTIME) |
463 |
break; |
464 |
/* -s: sort positional params (AT&T ksh stupidity) */ |
465 |
if (what == OF_SET && optc == 's') { |
466 |
sortargs = true; |
467 |
break; |
468 |
} |
469 |
for (i = 0; i < NELEM(options); i++) |
470 |
if (optc == OFC(i) && |
471 |
(what & OFF(i))) { |
472 |
change_flag((enum sh_flag)i, what, set); |
473 |
break; |
474 |
} |
475 |
if (i == NELEM(options)) |
476 |
internal_errorf("parse_args: '%c'", optc); |
477 |
} |
478 |
} |
479 |
if (!(go.info & GI_MINUSMINUS) && argv[go.optind] && |
480 |
(argv[go.optind][0] == '-' || argv[go.optind][0] == '+') && |
481 |
argv[go.optind][1] == '\0') { |
482 |
/* lone - clears -v and -x flags */ |
483 |
if (argv[go.optind][0] == '-') { |
484 |
Flag(FVERBOSE) = 0; |
485 |
change_xtrace(0, false); |
486 |
} |
487 |
/* set skips lone - or + option */ |
488 |
go.optind++; |
489 |
} |
490 |
if (setargsp) |
491 |
/* -- means set $#/$* even if there are no arguments */ |
492 |
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || |
493 |
argv[go.optind]); |
494 |
|
495 |
if (arrayset) { |
496 |
const char *ccp = NULL; |
497 |
|
498 |
if (*array) |
499 |
ccp = skip_varname(array, false); |
500 |
if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) { |
501 |
bi_errorf("%s: %s", array, "is not an identifier"); |
502 |
return (-1); |
503 |
} |
504 |
} |
505 |
if (sortargs) { |
506 |
for (i = go.optind; argv[i]; i++) |
507 |
; |
508 |
qsort(&argv[go.optind], i - go.optind, sizeof(void *), |
509 |
xstrcmp); |
510 |
} |
511 |
if (arrayset) |
512 |
go.optind += set_array(array, tobool(arrayset > 0), |
513 |
argv + go.optind); |
514 |
|
515 |
return (go.optind); |
516 |
} |
517 |
|
518 |
/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */ |
519 |
int |
520 |
getn(const char *s, int *ai) |
521 |
{ |
522 |
char c; |
523 |
mksh_ari_u num; |
524 |
bool neg = false; |
525 |
|
526 |
num.u = 0; |
527 |
|
528 |
do { |
529 |
c = *s++; |
530 |
} while (ksh_isspace(c)); |
531 |
|
532 |
switch (c) { |
533 |
case '-': |
534 |
neg = true; |
535 |
/* FALLTHROUGH */ |
536 |
case '+': |
537 |
c = *s++; |
538 |
break; |
539 |
} |
540 |
|
541 |
do { |
542 |
if (!ksh_isdigit(c)) |
543 |
/* not numeric */ |
544 |
return (0); |
545 |
if (num.u > 214748364U) |
546 |
/* overflow on multiplication */ |
547 |
return (0); |
548 |
num.u = num.u * 10U + (unsigned int)(c - '0'); |
549 |
/* now: num.u <= 2147483649U */ |
550 |
} while ((c = *s++)); |
551 |
|
552 |
if (num.u > (neg ? 2147483648U : 2147483647U)) |
553 |
/* overflow for signed 32-bit int */ |
554 |
return (0); |
555 |
|
556 |
if (neg) |
557 |
num.u = -num.u; |
558 |
*ai = num.i; |
559 |
return (1); |
560 |
} |
561 |
|
562 |
/** |
563 |
* pattern simplifications: |
564 |
* - @(x) -> x (not @(x|y) though) |
565 |
* - ** -> * |
566 |
*/ |
567 |
static void * |
568 |
simplify_gmatch_pattern(const unsigned char *sp) |
569 |
{ |
570 |
uint8_t c; |
571 |
unsigned char *cp, *dp; |
572 |
const unsigned char *ps, *se; |
573 |
|
574 |
cp = alloc(strlen((const void *)sp) + 1, ATEMP); |
575 |
goto simplify_gmatch_pat1a; |
576 |
|
577 |
/* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */ |
578 |
simplify_gmatch_pat1: |
579 |
sp = cp; |
580 |
simplify_gmatch_pat1a: |
581 |
dp = cp; |
582 |
se = sp + strlen((const void *)sp); |
583 |
while ((c = *sp++)) { |
584 |
if (!ISMAGIC(c)) { |
585 |
*dp++ = c; |
586 |
continue; |
587 |
} |
588 |
switch ((c = *sp++)) { |
589 |
case 0x80|'@': |
590 |
/* simile for @ */ |
591 |
case 0x80|' ': |
592 |
/* check whether it has only one clause */ |
593 |
ps = pat_scan(sp, se, true); |
594 |
if (!ps || ps[-1] != /*(*/ ')') |
595 |
/* nope */ |
596 |
break; |
597 |
/* copy inner clause until matching close */ |
598 |
ps -= 2; |
599 |
while ((const unsigned char *)sp < ps) |
600 |
*dp++ = *sp++; |
601 |
/* skip MAGIC and closing parenthesis */ |
602 |
sp += 2; |
603 |
/* copy the rest of the pattern */ |
604 |
memmove(dp, sp, strlen((const void *)sp) + 1); |
605 |
/* redo from start */ |
606 |
goto simplify_gmatch_pat1; |
607 |
} |
608 |
*dp++ = MAGIC; |
609 |
*dp++ = c; |
610 |
} |
611 |
*dp = '\0'; |
612 |
|
613 |
/* collapse adjacent asterisk wildcards */ |
614 |
sp = dp = cp; |
615 |
while ((c = *sp++)) { |
616 |
if (!ISMAGIC(c)) { |
617 |
*dp++ = c; |
618 |
continue; |
619 |
} |
620 |
switch ((c = *sp++)) { |
621 |
case '*': |
622 |
while (ISMAGIC(sp[0]) && sp[1] == c) |
623 |
sp += 2; |
624 |
break; |
625 |
} |
626 |
*dp++ = MAGIC; |
627 |
*dp++ = c; |
628 |
} |
629 |
*dp = '\0'; |
630 |
|
631 |
/* return the result, allocated from ATEMP */ |
632 |
return (cp); |
633 |
} |
634 |
|
635 |
/* -------- gmatch.c -------- */ |
636 |
|
637 |
/* |
638 |
* int gmatch(string, pattern) |
639 |
* char *string, *pattern; |
640 |
* |
641 |
* Match a pattern as in sh(1). |
642 |
* pattern character are prefixed with MAGIC by expand. |
643 |
*/ |
644 |
int |
645 |
gmatchx(const char *s, const char *p, bool isfile) |
646 |
{ |
647 |
const char *se, *pe; |
648 |
char *pnew; |
649 |
int rv; |
650 |
|
651 |
if (s == NULL || p == NULL) |
652 |
return (0); |
653 |
|
654 |
se = s + strlen(s); |
655 |
pe = p + strlen(p); |
656 |
/* |
657 |
* isfile is false iff no syntax check has been done on |
658 |
* the pattern. If check fails, just to a strcmp(). |
659 |
*/ |
660 |
if (!isfile && !has_globbing(p, pe)) { |
661 |
size_t len = pe - p + 1; |
662 |
char tbuf[64]; |
663 |
char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP); |
664 |
debunk(t, p, len); |
665 |
return (!strcmp(t, s)); |
666 |
} |
667 |
|
668 |
/* |
669 |
* since the do_gmatch() engine sucks so much, we must do some |
670 |
* pattern simplifications |
671 |
*/ |
672 |
pnew = simplify_gmatch_pattern((const unsigned char *)p); |
673 |
pe = pnew + strlen(pnew); |
674 |
|
675 |
rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se, |
676 |
(const unsigned char *)pnew, (const unsigned char *)pe); |
677 |
afree(pnew, ATEMP); |
678 |
return (rv); |
679 |
} |
680 |
|
681 |
/** |
682 |
* Returns if p is a syntacticly correct globbing pattern, false |
683 |
* if it contains no pattern characters or if there is a syntax error. |
684 |
* Syntax errors are: |
685 |
* - [ with no closing ] |
686 |
* - imbalanced $(...) expression |
687 |
* - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d)) |
688 |
*/ |
689 |
/*XXX |
690 |
* - if no magic, |
691 |
* if dest given, copy to dst |
692 |
* return ? |
693 |
* - if magic && (no globbing || syntax error) |
694 |
* debunk to dst |
695 |
* return ? |
696 |
* - return ? |
697 |
*/ |
698 |
int |
699 |
has_globbing(const char *xp, const char *xpe) |
700 |
{ |
701 |
const unsigned char *p = (const unsigned char *) xp; |
702 |
const unsigned char *pe = (const unsigned char *) xpe; |
703 |
int c; |
704 |
int nest = 0, bnest = 0; |
705 |
bool saw_glob = false; |
706 |
/* inside [...] */ |
707 |
bool in_bracket = false; |
708 |
|
709 |
for (; p < pe; p++) { |
710 |
if (!ISMAGIC(*p)) |
711 |
continue; |
712 |
if ((c = *++p) == '*' || c == '?') |
713 |
saw_glob = true; |
714 |
else if (c == '[') { |
715 |
if (!in_bracket) { |
716 |
saw_glob = true; |
717 |
in_bracket = true; |
718 |
if (ISMAGIC(p[1]) && p[2] == '!') |
719 |
p += 2; |
720 |
if (ISMAGIC(p[1]) && p[2] == ']') |
721 |
p += 2; |
722 |
} |
723 |
/*XXX Do we need to check ranges here? POSIX Q */ |
724 |
} else if (c == ']') { |
725 |
if (in_bracket) { |
726 |
if (bnest) |
727 |
/* [a*(b]) */ |
728 |
return (0); |
729 |
in_bracket = false; |
730 |
} |
731 |
} else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) { |
732 |
saw_glob = true; |
733 |
if (in_bracket) |
734 |
bnest++; |
735 |
else |
736 |
nest++; |
737 |
} else if (c == '|') { |
738 |
if (in_bracket && !bnest) |
739 |
/* *(a[foo|bar]) */ |
740 |
return (0); |
741 |
} else if (c == /*(*/ ')') { |
742 |
if (in_bracket) { |
743 |
if (!bnest--) |
744 |
/* *(a[b)c] */ |
745 |
return (0); |
746 |
} else if (nest) |
747 |
nest--; |
748 |
} |
749 |
/* |
750 |
* else must be a MAGIC-MAGIC, or MAGIC-!, |
751 |
* MAGIC--, MAGIC-], MAGIC-{, MAGIC-, MAGIC-} |
752 |
*/ |
753 |
} |
754 |
return (saw_glob && !in_bracket && !nest); |
755 |
} |
756 |
|
757 |
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ |
758 |
static int |
759 |
do_gmatch(const unsigned char *s, const unsigned char *se, |
760 |
const unsigned char *p, const unsigned char *pe) |
761 |
{ |
762 |
unsigned char sc, pc; |
763 |
const unsigned char *prest, *psub, *pnext; |
764 |
const unsigned char *srest; |
765 |
|
766 |
if (s == NULL || p == NULL) |
767 |
return (0); |
768 |
while (p < pe) { |
769 |
pc = *p++; |
770 |
sc = s < se ? *s : '\0'; |
771 |
s++; |
772 |
if (!ISMAGIC(pc)) { |
773 |
if (sc != pc) |
774 |
return (0); |
775 |
continue; |
776 |
} |
777 |
switch (*p++) { |
778 |
case '[': |
779 |
if (sc == 0 || (p = cclass(p, sc)) == NULL) |
780 |
return (0); |
781 |
break; |
782 |
|
783 |
case '?': |
784 |
if (sc == 0) |
785 |
return (0); |
786 |
if (UTFMODE) { |
787 |
--s; |
788 |
s += utf_ptradj((const void *)s); |
789 |
} |
790 |
break; |
791 |
|
792 |
case '*': |
793 |
if (p == pe) |
794 |
return (1); |
795 |
s--; |
796 |
do { |
797 |
if (do_gmatch(s, se, p, pe)) |
798 |
return (1); |
799 |
} while (s++ < se); |
800 |
return (0); |
801 |
|
802 |
/** |
803 |
* [*+?@!](pattern|pattern|..) |
804 |
* This is also needed for ${..%..}, etc. |
805 |
*/ |
806 |
|
807 |
/* matches one or more times */ |
808 |
case 0x80|'+': |
809 |
/* matches zero or more times */ |
810 |
case 0x80|'*': |
811 |
if (!(prest = pat_scan(p, pe, false))) |
812 |
return (0); |
813 |
s--; |
814 |
/* take care of zero matches */ |
815 |
if (p[-1] == (0x80 | '*') && |
816 |
do_gmatch(s, se, prest, pe)) |
817 |
return (1); |
818 |
for (psub = p; ; psub = pnext) { |
819 |
pnext = pat_scan(psub, pe, true); |
820 |
for (srest = s; srest <= se; srest++) { |
821 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
822 |
(do_gmatch(srest, se, prest, pe) || |
823 |
(s != srest && do_gmatch(srest, |
824 |
se, p - 2, pe)))) |
825 |
return (1); |
826 |
} |
827 |
if (pnext == prest) |
828 |
break; |
829 |
} |
830 |
return (0); |
831 |
|
832 |
/* matches zero or once */ |
833 |
case 0x80|'?': |
834 |
/* matches one of the patterns */ |
835 |
case 0x80|'@': |
836 |
/* simile for @ */ |
837 |
case 0x80|' ': |
838 |
if (!(prest = pat_scan(p, pe, false))) |
839 |
return (0); |
840 |
s--; |
841 |
/* Take care of zero matches */ |
842 |
if (p[-1] == (0x80 | '?') && |
843 |
do_gmatch(s, se, prest, pe)) |
844 |
return (1); |
845 |
for (psub = p; ; psub = pnext) { |
846 |
pnext = pat_scan(psub, pe, true); |
847 |
srest = prest == pe ? se : s; |
848 |
for (; srest <= se; srest++) { |
849 |
if (do_gmatch(s, srest, psub, pnext - 2) && |
850 |
do_gmatch(srest, se, prest, pe)) |
851 |
return (1); |
852 |
} |
853 |
if (pnext == prest) |
854 |
break; |
855 |
} |
856 |
return (0); |
857 |
|
858 |
/* matches none of the patterns */ |
859 |
case 0x80|'!': |
860 |
if (!(prest = pat_scan(p, pe, false))) |
861 |
return (0); |
862 |
s--; |
863 |
for (srest = s; srest <= se; srest++) { |
864 |
int matched = 0; |
865 |
|
866 |
for (psub = p; ; psub = pnext) { |
867 |
pnext = pat_scan(psub, pe, true); |
868 |
if (do_gmatch(s, srest, psub, |
869 |
pnext - 2)) { |
870 |
matched = 1; |
871 |
break; |
872 |
} |
873 |
if (pnext == prest) |
874 |
break; |
875 |
} |
876 |
if (!matched && |
877 |
do_gmatch(srest, se, prest, pe)) |
878 |
return (1); |
879 |
} |
880 |
return (0); |
881 |
|
882 |
default: |
883 |
if (sc != p[-1]) |
884 |
return (0); |
885 |
break; |
886 |
} |
887 |
} |
888 |
return (s == se); |
889 |
} |
890 |
|
891 |
static const unsigned char * |
892 |
cclass(const unsigned char *p, unsigned char sub) |
893 |
{ |
894 |
unsigned char c, d; |
895 |
bool notp, found = false; |
896 |
const unsigned char *orig_p = p; |
897 |
|
898 |
if ((notp = tobool(ISMAGIC(*p) && *++p == '!'))) |
899 |
p++; |
900 |
do { |
901 |
c = *p++; |
902 |
if (ISMAGIC(c)) { |
903 |
c = *p++; |
904 |
if ((c & 0x80) && !ISMAGIC(c)) { |
905 |
/* extended pattern matching: *+?@! */ |
906 |
c &= 0x7F; |
907 |
/* XXX the ( char isn't handled as part of [] */ |
908 |
if (c == ' ') |
909 |
/* simile for @: plain (..) */ |
910 |
c = '(' /*)*/; |
911 |
} |
912 |
} |
913 |
if (c == '\0') |
914 |
/* No closing ] - act as if the opening [ was quoted */ |
915 |
return (sub == '[' ? orig_p : NULL); |
916 |
if (ISMAGIC(p[0]) && p[1] == '-' && |
917 |
(!ISMAGIC(p[2]) || p[3] != ']')) { |
918 |
/* MAGIC- */ |
919 |
p += 2; |
920 |
d = *p++; |
921 |
if (ISMAGIC(d)) { |
922 |
d = *p++; |
923 |
if ((d & 0x80) && !ISMAGIC(d)) |
924 |
d &= 0x7f; |
925 |
} |
926 |
/* POSIX says this is an invalid expression */ |
927 |
if (c > d) |
928 |
return (NULL); |
929 |
} else |
930 |
d = c; |
931 |
if (c == sub || (c <= sub && sub <= d)) |
932 |
found = true; |
933 |
} while (!(ISMAGIC(p[0]) && p[1] == ']')); |
934 |
|
935 |
return ((found != notp) ? p+2 : NULL); |
936 |
} |
937 |
|
938 |
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ |
939 |
static const unsigned char * |
940 |
pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep) |
941 |
{ |
942 |
int nest = 0; |
943 |
|
944 |
for (; p < pe; p++) { |
945 |
if (!ISMAGIC(*p)) |
946 |
continue; |
947 |
if ((*++p == /*(*/ ')' && nest-- == 0) || |
948 |
(*p == '|' && match_sep && nest == 0)) |
949 |
return (p + 1); |
950 |
if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f)) |
951 |
nest++; |
952 |
} |
953 |
return (NULL); |
954 |
} |
955 |
|
956 |
int |
957 |
xstrcmp(const void *p1, const void *p2) |
958 |
{ |
959 |
return (strcmp(*(const char * const *)p1, *(const char * const *)p2)); |
960 |
} |
961 |
|
962 |
/* Initialise a Getopt structure */ |
963 |
void |
964 |
ksh_getopt_reset(Getopt *go, int flags) |
965 |
{ |
966 |
go->optind = 1; |
967 |
go->optarg = NULL; |
968 |
go->p = 0; |
969 |
go->flags = flags; |
970 |
go->info = 0; |
971 |
go->buf[1] = '\0'; |
972 |
} |
973 |
|
974 |
|
975 |
/** |
976 |
* getopt() used for shell built-in commands, the getopts command, and |
977 |
* command line options. |
978 |
* A leading ':' in options means don't print errors, instead return '?' |
979 |
* or ':' and set go->optarg to the offending option character. |
980 |
* If GF_ERROR is set (and option doesn't start with :), errors result in |
981 |
* a call to bi_errorf(). |
982 |
* |
983 |
* Non-standard features: |
984 |
* - ';' is like ':' in options, except the argument is optional |
985 |
* (if it isn't present, optarg is set to 0). |
986 |
* Used for 'set -o'. |
987 |
* - ',' is like ':' in options, except the argument always immediately |
988 |
* follows the option character (optarg is set to the null string if |
989 |
* the option is missing). |
990 |
* Used for 'read -u2', 'print -u2' and fc -40. |
991 |
* - '#' is like ':' in options, expect that the argument is optional |
992 |
* and must start with a digit. If the argument doesn't start with a |
993 |
* digit, it is assumed to be missing and normal option processing |
994 |
* continues (optarg is set to 0 if the option is missing). |
995 |
* Used for 'typeset -LZ4'. |
996 |
* - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an |
997 |
* option starting with + is accepted, the GI_PLUS flag will be set |
998 |
* in go->info. |
999 |
*/ |
1000 |
int |
1001 |
ksh_getopt(const char **argv, Getopt *go, const char *optionsp) |
1002 |
{ |
1003 |
char c; |
1004 |
const char *o; |
1005 |
|
1006 |
if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') { |
1007 |
const char *arg = argv[go->optind], flag = arg ? *arg : '\0'; |
1008 |
|
1009 |
go->p = 1; |
1010 |
if (flag == '-' && arg[1] == '-' && arg[2] == '\0') { |
1011 |
go->optind++; |
1012 |
go->p = 0; |
1013 |
go->info |= GI_MINUSMINUS; |
1014 |
return (-1); |
1015 |
} |
1016 |
if (arg == NULL || |
1017 |
((flag != '-' ) && |
1018 |
/* neither a - nor a + (if + allowed) */ |
1019 |
(!(go->flags & GF_PLUSOPT) || flag != '+')) || |
1020 |
(c = arg[1]) == '\0') { |
1021 |
go->p = 0; |
1022 |
return (-1); |
1023 |
} |
1024 |
go->optind++; |
1025 |
go->info &= ~(GI_MINUS|GI_PLUS); |
1026 |
go->info |= flag == '-' ? GI_MINUS : GI_PLUS; |
1027 |
} |
1028 |
go->p++; |
1029 |
if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' || |
1030 |
!(o = cstrchr(optionsp, c))) { |
1031 |
if (optionsp[0] == ':') { |
1032 |
go->buf[0] = c; |
1033 |
go->optarg = go->buf; |
1034 |
} else { |
1035 |
warningf(true, "%s%s-%c: %s", |
1036 |
(go->flags & GF_NONAME) ? "" : argv[0], |
1037 |
(go->flags & GF_NONAME) ? "" : ": ", c, |
1038 |
"unknown option"); |
1039 |
if (go->flags & GF_ERROR) |
1040 |
bi_errorfz(); |
1041 |
} |
1042 |
return ('?'); |
1043 |
} |
1044 |
/** |
1045 |
* : means argument must be present, may be part of option argument |
1046 |
* or the next argument |
1047 |
* ; same as : but argument may be missing |
1048 |
* , means argument is part of option argument, and may be null. |
1049 |
*/ |
1050 |
if (*++o == ':' || *o == ';') { |
1051 |
if (argv[go->optind - 1][go->p]) |
1052 |
go->optarg = argv[go->optind - 1] + go->p; |
1053 |
else if (argv[go->optind]) |
1054 |
go->optarg = argv[go->optind++]; |
1055 |
else if (*o == ';') |
1056 |
go->optarg = NULL; |
1057 |
else { |
1058 |
if (optionsp[0] == ':') { |
1059 |
go->buf[0] = c; |
1060 |
go->optarg = go->buf; |
1061 |
return (':'); |
1062 |
} |
1063 |
warningf(true, "%s%s-%c: %s", |
1064 |
(go->flags & GF_NONAME) ? "" : argv[0], |
1065 |
(go->flags & GF_NONAME) ? "" : ": ", c, |
1066 |
"requires an argument"); |
1067 |
if (go->flags & GF_ERROR) |
1068 |
bi_errorfz(); |
1069 |
return ('?'); |
1070 |
} |
1071 |
go->p = 0; |
1072 |
} else if (*o == ',') { |
1073 |
/* argument is attached to option character, even if null */ |
1074 |
go->optarg = argv[go->optind - 1] + go->p; |
1075 |
go->p = 0; |
1076 |
} else if (*o == '#') { |
1077 |
/* |
1078 |
* argument is optional and may be attached or unattached |
1079 |
* but must start with a digit. optarg is set to 0 if the |
1080 |
* argument is missing. |
1081 |
*/ |
1082 |
if (argv[go->optind - 1][go->p]) { |
1083 |
if (ksh_isdigit(argv[go->optind - 1][go->p])) { |
1084 |
go->optarg = argv[go->optind - 1] + go->p; |
1085 |
go->p = 0; |
1086 |
} else |
1087 |
go->optarg = NULL; |
1088 |
} else { |
1089 |
if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) { |
1090 |
go->optarg = argv[go->optind++]; |
1091 |
go->p = 0; |
1092 |
} else |
1093 |
go->optarg = NULL; |
1094 |
} |
1095 |
} |
1096 |
return (c); |
1097 |
} |
1098 |
|
1099 |
/* |
1100 |
* print variable/alias value using necessary quotes |
1101 |
* (POSIX says they should be suitable for re-entry...) |
1102 |
* No trailing newline is printed. |
1103 |
*/ |
1104 |
void |
1105 |
print_value_quoted(struct shf *shf, const char *s) |
1106 |
{ |
1107 |
unsigned char c; |
1108 |
const unsigned char *p = (const unsigned char *)s; |
1109 |
bool inquote = true; |
1110 |
|
1111 |
/* first, check whether any quotes are needed */ |
1112 |
while ((c = *p++) >= 32) |
1113 |
if (ctype(c, C_QUOTE)) |
1114 |
inquote = false; |
1115 |
|
1116 |
p = (const unsigned char *)s; |
1117 |
if (c == 0) { |
1118 |
if (inquote) { |
1119 |
/* nope, use the shortcut */ |
1120 |
shf_puts(s, shf); |
1121 |
return; |
1122 |
} |
1123 |
|
1124 |
/* otherwise, quote nicely via state machine */ |
1125 |
while ((c = *p++) != 0) { |
1126 |
if (c == '\'') { |
1127 |
/* |
1128 |
* multiple single quotes or any of them |
1129 |
* at the beginning of a string look nicer |
1130 |
* this way than when simply substituting |
1131 |
*/ |
1132 |
if (inquote) { |
1133 |
shf_putc('\'', shf); |
1134 |
inquote = false; |
1135 |
} |
1136 |
shf_putc('\\', shf); |
1137 |
} else if (!inquote) { |
1138 |
shf_putc('\'', shf); |
1139 |
inquote = true; |
1140 |
} |
1141 |
shf_putc(c, shf); |
1142 |
} |
1143 |
} else { |
1144 |
unsigned int wc; |
1145 |
size_t n; |
1146 |
|
1147 |
/* use $'...' quote format */ |
1148 |
shf_putc('$', shf); |
1149 |
shf_putc('\'', shf); |
1150 |
while ((c = *p) != 0) { |
1151 |
if (c >= 0xC2) { |
1152 |
n = utf_mbtowc(&wc, (const char *)p); |
1153 |
if (n != (size_t)-1) { |
1154 |
p += n; |
1155 |
shf_fprintf(shf, "\\u%04X", wc); |
1156 |
continue; |
1157 |
} |
1158 |
} |
1159 |
++p; |
1160 |
switch (c) { |
1161 |
/* see unbksl() in this file for comments */ |
1162 |
case 7: |
1163 |
c = 'a'; |
1164 |
if (0) |
1165 |
/* FALLTHROUGH */ |
1166 |
case '\b': |
1167 |
c = 'b'; |
1168 |
if (0) |
1169 |
/* FALLTHROUGH */ |
1170 |
case '\f': |
1171 |
c = 'f'; |
1172 |
if (0) |
1173 |
/* FALLTHROUGH */ |
1174 |
case '\n': |
1175 |
c = 'n'; |
1176 |
if (0) |
1177 |
/* FALLTHROUGH */ |
1178 |
case '\r': |
1179 |
c = 'r'; |
1180 |
if (0) |
1181 |
/* FALLTHROUGH */ |
1182 |
case '\t': |
1183 |
c = 't'; |
1184 |
if (0) |
1185 |
/* FALLTHROUGH */ |
1186 |
case 11: |
1187 |
c = 'v'; |
1188 |
if (0) |
1189 |
/* FALLTHROUGH */ |
1190 |
case '\033': |
1191 |
/* take E not e because \e is \ in *roff */ |
1192 |
c = 'E'; |
1193 |
/* FALLTHROUGH */ |
1194 |
case '\\': |
1195 |
shf_putc('\\', shf); |
1196 |
|
1197 |
if (0) |
1198 |
/* FALLTHROUGH */ |
1199 |
default: |
1200 |
if (c < 32 || c > 0x7E) { |
1201 |
/* FALLTHROUGH */ |
1202 |
case '\'': |
1203 |
shf_fprintf(shf, "\\%03o", c); |
1204 |
break; |
1205 |
} |
1206 |
|
1207 |
shf_putc(c, shf); |
1208 |
break; |
1209 |
} |
1210 |
} |
1211 |
inquote = true; |
1212 |
} |
1213 |
if (inquote) |
1214 |
shf_putc('\'', shf); |
1215 |
} |
1216 |
|
1217 |
/* |
1218 |
* Print things in columns and rows - func() is called to format |
1219 |
* the i-th element |
1220 |
*/ |
1221 |
void |
1222 |
print_columns(struct shf *shf, unsigned int n, |
1223 |
char *(*func)(char *, size_t, unsigned int, const void *), |
1224 |
const void *arg, size_t max_oct, size_t max_colz, bool prefcol) |
1225 |
{ |
1226 |
unsigned int i, r, c, rows, cols, nspace, max_col; |
1227 |
char *str; |
1228 |
|
1229 |
if (!n) |
1230 |
return; |
1231 |
|
1232 |
if (max_colz > 2147483646) { |
1233 |
#ifndef MKSH_SMALL |
1234 |
internal_warningf("print_columns called with %s=%zu >= INT_MAX", |
1235 |
"max_col", max_colz); |
1236 |
#endif |
1237 |
return; |
1238 |
} |
1239 |
max_col = (unsigned int)max_colz; |
1240 |
|
1241 |
if (max_oct > 2147483646) { |
1242 |
#ifndef MKSH_SMALL |
1243 |
internal_warningf("print_columns called with %s=%zu >= INT_MAX", |
1244 |
"max_oct", max_oct); |
1245 |
#endif |
1246 |
return; |
1247 |
} |
1248 |
++max_oct; |
1249 |
str = alloc(max_oct, ATEMP); |
1250 |
|
1251 |
/* |
1252 |
* We use (max_col + 1) to consider the space separator. |
1253 |
* Note that no space is printed after the last column |
1254 |
* to avoid problems with terminals that have auto-wrap. |
1255 |
*/ |
1256 |
cols = x_cols / (max_col + 1); |
1257 |
|
1258 |
/* if we can only print one column anyway, skip the goo */ |
1259 |
if (cols < 2) { |
1260 |
for (i = 0; i < n; ++i) |
1261 |
shf_fprintf(shf, "%s\n", |
1262 |
(*func)(str, max_oct, i, arg)); |
1263 |
goto out; |
1264 |
} |
1265 |
|
1266 |
rows = (n + cols - 1) / cols; |
1267 |
if (prefcol && cols > rows) { |
1268 |
cols = rows; |
1269 |
rows = (n + cols - 1) / cols; |
1270 |
} |
1271 |
|
1272 |
nspace = (x_cols - max_col * cols) / cols; |
1273 |
max_col = -max_col; |
1274 |
if (nspace <= 0) |
1275 |
nspace = 1; |
1276 |
for (r = 0; r < rows; r++) { |
1277 |
for (c = 0; c < cols; c++) { |
1278 |
i = c * rows + r; |
1279 |
if (i < n) { |
1280 |
shf_fprintf(shf, "%*s", max_col, |
1281 |
(*func)(str, max_oct, i, arg)); |
1282 |
if (c + 1 < cols) |
1283 |
shf_fprintf(shf, "%*s", nspace, null); |
1284 |
} |
1285 |
} |
1286 |
shf_putchar('\n', shf); |
1287 |
} |
1288 |
out: |
1289 |
afree(str, ATEMP); |
1290 |
} |
1291 |
|
1292 |
/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */ |
1293 |
void |
1294 |
strip_nuls(char *buf, int nbytes) |
1295 |
{ |
1296 |
char *dst; |
1297 |
|
1298 |
/* |
1299 |
* nbytes check because some systems (older FreeBSDs) have a |
1300 |
* buggy memchr() |
1301 |
*/ |
1302 |
if (nbytes && (dst = memchr(buf, '\0', nbytes))) { |
1303 |
char *end = buf + nbytes; |
1304 |
char *p, *q; |
1305 |
|
1306 |
for (p = dst; p < end; p = q) { |
1307 |
/* skip a block of nulls */ |
1308 |
while (++p < end && *p == '\0') |
1309 |
; |
1310 |
/* find end of non-null block */ |
1311 |
if (!(q = memchr(p, '\0', end - p))) |
1312 |
q = end; |
1313 |
memmove(dst, p, q - p); |
1314 |
dst += q - p; |
1315 |
} |
1316 |
*dst = '\0'; |
1317 |
} |
1318 |
} |
1319 |
|
1320 |
/* |
1321 |
* Like read(2), but if read fails due to non-blocking flag, |
1322 |
* resets flag and restarts read. |
1323 |
*/ |
1324 |
ssize_t |
1325 |
blocking_read(int fd, char *buf, size_t nbytes) |
1326 |
{ |
1327 |
ssize_t ret; |
1328 |
bool tried_reset = false; |
1329 |
|
1330 |
while ((ret = read(fd, buf, nbytes)) < 0) { |
1331 |
if (!tried_reset && errno == EAGAIN) { |
1332 |
if (reset_nonblock(fd) > 0) { |
1333 |
tried_reset = true; |
1334 |
continue; |
1335 |
} |
1336 |
errno = EAGAIN; |
1337 |
} |
1338 |
break; |
1339 |
} |
1340 |
return (ret); |
1341 |
} |
1342 |
|
1343 |
/* |
1344 |
* Reset the non-blocking flag on the specified file descriptor. |
1345 |
* Returns -1 if there was an error, 0 if non-blocking wasn't set, |
1346 |
* 1 if it was. |
1347 |
*/ |
1348 |
int |
1349 |
reset_nonblock(int fd) |
1350 |
{ |
1351 |
int flags; |
1352 |
|
1353 |
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) |
1354 |
return (-1); |
1355 |
if (!(flags & O_NONBLOCK)) |
1356 |
return (0); |
1357 |
flags &= ~O_NONBLOCK; |
1358 |
if (fcntl(fd, F_SETFL, flags) < 0) |
1359 |
return (-1); |
1360 |
return (1); |
1361 |
} |
1362 |
|
1363 |
/* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */ |
1364 |
char * |
1365 |
ksh_get_wd(void) |
1366 |
{ |
1367 |
#ifdef MKSH__NO_PATH_MAX |
1368 |
char *rv, *cp; |
1369 |
|
1370 |
if ((cp = get_current_dir_name())) { |
1371 |
strdupx(rv, cp, ATEMP); |
1372 |
free_gnu_gcdn(cp); |
1373 |
} else |
1374 |
rv = NULL; |
1375 |
#else |
1376 |
char *rv; |
1377 |
|
1378 |
if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) { |
1379 |
afree(rv, ATEMP); |
1380 |
rv = NULL; |
1381 |
} |
1382 |
#endif |
1383 |
|
1384 |
return (rv); |
1385 |
} |
1386 |
|
1387 |
#ifndef ELOOP |
1388 |
#define ELOOP E2BIG |
1389 |
#endif |
1390 |
|
1391 |
char * |
1392 |
do_realpath(const char *upath) |
1393 |
{ |
1394 |
char *xp, *ip, *tp, *ipath, *ldest = NULL; |
1395 |
XString xs; |
1396 |
size_t pos, len; |
1397 |
int llen; |
1398 |
struct stat sb; |
1399 |
#ifdef MKSH__NO_PATH_MAX |
1400 |
size_t ldestlen = 0; |
1401 |
#define pathlen sb.st_size |
1402 |
#define pathcnd (ldestlen < (pathlen + 1)) |
1403 |
#else |
1404 |
#define pathlen PATH_MAX |
1405 |
#define pathcnd (!ldest) |
1406 |
#endif |
1407 |
/* max. recursion depth */ |
1408 |
int symlinks = 32; |
1409 |
|
1410 |
if (upath[0] == '/') { |
1411 |
/* upath is an absolute pathname */ |
1412 |
strdupx(ipath, upath, ATEMP); |
1413 |
} else { |
1414 |
/* upath is a relative pathname, prepend cwd */ |
1415 |
if ((tp = ksh_get_wd()) == NULL || tp[0] != '/') |
1416 |
return (NULL); |
1417 |
ipath = shf_smprintf("%s%s%s", tp, "/", upath); |
1418 |
afree(tp, ATEMP); |
1419 |
} |
1420 |
|
1421 |
/* ipath and upath are in memory at the same time -> unchecked */ |
1422 |
Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP); |
1423 |
|
1424 |
/* now jump into the deep of the loop */ |
1425 |
goto beginning_of_a_pathname; |
1426 |
|
1427 |
while (*ip) { |
1428 |
/* skip slashes in input */ |
1429 |
while (*ip == '/') |
1430 |
++ip; |
1431 |
if (!*ip) |
1432 |
break; |
1433 |
|
1434 |
/* get next pathname component from input */ |
1435 |
tp = ip; |
1436 |
while (*ip && *ip != '/') |
1437 |
++ip; |
1438 |
len = ip - tp; |
1439 |
|
1440 |
/* check input for "." and ".." */ |
1441 |
if (tp[0] == '.') { |
1442 |
if (len == 1) |
1443 |
/* just continue with the next one */ |
1444 |
continue; |
1445 |
else if (len == 2 && tp[1] == '.') { |
1446 |
/* strip off last pathname component */ |
1447 |
while (xp > Xstring(xs, xp)) |
1448 |
if (*--xp == '/') |
1449 |
break; |
1450 |
/* then continue with the next one */ |
1451 |
continue; |
1452 |
} |
1453 |
} |
1454 |
|
1455 |
/* store output position away, then append slash to output */ |
1456 |
pos = Xsavepos(xs, xp); |
1457 |
/* 1 for the '/' and len + 1 for tp and the NUL from below */ |
1458 |
XcheckN(xs, xp, 1 + len + 1); |
1459 |
Xput(xs, xp, '/'); |
1460 |
|
1461 |
/* append next pathname component to output */ |
1462 |
memcpy(xp, tp, len); |
1463 |
xp += len; |
1464 |
*xp = '\0'; |
1465 |
|
1466 |
/* lstat the current output, see if it's a symlink */ |
1467 |
if (mksh_lstat(Xstring(xs, xp), &sb)) { |
1468 |
/* lstat failed */ |
1469 |
if (errno == ENOENT) { |
1470 |
/* because the pathname does not exist */ |
1471 |
while (*ip == '/') |
1472 |
/* skip any trailing slashes */ |
1473 |
++ip; |
1474 |
/* no more components left? */ |
1475 |
if (!*ip) |
1476 |
/* we can still return successfully */ |
1477 |
break; |
1478 |
/* more components left? fall through */ |
1479 |
} |
1480 |
/* not ENOENT or not at the end of ipath */ |
1481 |
goto notfound; |
1482 |
} |
1483 |
|
1484 |
/* check if we encountered a symlink? */ |
1485 |
if (S_ISLNK(sb.st_mode)) { |
1486 |
#ifndef MKSH__NO_SYMLINK |
1487 |
/* reached maximum recursion depth? */ |
1488 |
if (!symlinks--) { |
1489 |
/* yep, prevent infinite loops */ |
1490 |
errno = ELOOP; |
1491 |
goto notfound; |
1492 |
} |
1493 |
|
1494 |
/* get symlink(7) target */ |
1495 |
if (pathcnd) { |
1496 |
#ifdef MKSH__NO_PATH_MAX |
1497 |
if (notoktoadd(pathlen, 1)) { |
1498 |
errno = ENAMETOOLONG; |
1499 |
goto notfound; |
1500 |
} |
1501 |
#endif |
1502 |
ldest = aresize(ldest, pathlen + 1, ATEMP); |
1503 |
} |
1504 |
llen = readlink(Xstring(xs, xp), ldest, pathlen); |
1505 |
if (llen < 0) |
1506 |
/* oops... */ |
1507 |
goto notfound; |
1508 |
ldest[llen] = '\0'; |
1509 |
|
1510 |
/* |
1511 |
* restart if symlink target is an absolute path, |
1512 |
* otherwise continue with currently resolved prefix |
1513 |
*/ |
1514 |
/* append rest of current input path to link target */ |
1515 |
tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip); |
1516 |
afree(ipath, ATEMP); |
1517 |
ip = ipath = tp; |
1518 |
if (ldest[0] != '/') { |
1519 |
/* symlink target is a relative path */ |
1520 |
xp = Xrestpos(xs, xp, pos); |
1521 |
} else |
1522 |
#endif |
1523 |
{ |
1524 |
/* symlink target is an absolute path */ |
1525 |
xp = Xstring(xs, xp); |
1526 |
beginning_of_a_pathname: |
1527 |
/* assert: (ip == ipath)[0] == '/' */ |
1528 |
/* assert: xp == xs.beg => start of path */ |
1529 |
|
1530 |
/* exactly two leading slashes? (SUSv4 3.266) */ |
1531 |
if (ip[1] == '/' && ip[2] != '/') { |
1532 |
/* keep them, e.g. for UNC pathnames */ |
1533 |
Xput(xs, xp, '/'); |
1534 |
} |
1535 |
} |
1536 |
} |
1537 |
/* otherwise (no symlink) merely go on */ |
1538 |
} |
1539 |
|
1540 |
/* |
1541 |
* either found the target and successfully resolved it, |
1542 |
* or found its parent directory and may create it |
1543 |
*/ |
1544 |
if (Xlength(xs, xp) == 0) |
1545 |
/* |
1546 |
* if the resolved pathname is "", make it "/", |
1547 |
* otherwise do not add a trailing slash |
1548 |
*/ |
1549 |
Xput(xs, xp, '/'); |
1550 |
Xput(xs, xp, '\0'); |
1551 |
|
1552 |
/* |
1553 |
* if source path had a trailing slash, check if target path |
1554 |
* is not a non-directory existing file |
1555 |
*/ |
1556 |
if (ip > ipath && ip[-1] == '/') { |
1557 |
if (stat(Xstring(xs, xp), &sb)) { |
1558 |
if (errno != ENOENT) |
1559 |
goto notfound; |
1560 |
} else if (!S_ISDIR(sb.st_mode)) { |
1561 |
errno = ENOTDIR; |
1562 |
goto notfound; |
1563 |
} |
1564 |
/* target now either does not exist or is a directory */ |
1565 |
} |
1566 |
|
1567 |
/* return target path */ |
1568 |
if (ldest != NULL) |
1569 |
afree(ldest, ATEMP); |
1570 |
afree(ipath, ATEMP); |
1571 |
return (Xclose(xs, xp)); |
1572 |
|
1573 |
notfound: |
1574 |
/* save; freeing memory might trash it */ |
1575 |
llen = errno; |
1576 |
if (ldest != NULL) |
1577 |
afree(ldest, ATEMP); |
1578 |
afree(ipath, ATEMP); |
1579 |
Xfree(xs, xp); |
1580 |
errno = llen; |
1581 |
return (NULL); |
1582 |
|
1583 |
#undef pathlen |
1584 |
#undef pathcnd |
1585 |
} |
1586 |
|
1587 |
/** |
1588 |
* Makes a filename into result using the following algorithm. |
1589 |
* - make result NULL |
1590 |
* - if file starts with '/', append file to result & set cdpathp to NULL |
1591 |
* - if file starts with ./ or ../ append cwd and file to result |
1592 |
* and set cdpathp to NULL |
1593 |
* - if the first element of cdpathp doesnt start with a '/' xx or '.' xx |
1594 |
* then cwd is appended to result. |
1595 |
* - the first element of cdpathp is appended to result |
1596 |
* - file is appended to result |
1597 |
* - cdpathp is set to the start of the next element in cdpathp (or NULL |
1598 |
* if there are no more elements. |
1599 |
* The return value indicates whether a non-null element from cdpathp |
1600 |
* was appended to result. |
1601 |
*/ |
1602 |
static int |
1603 |
make_path(const char *cwd, const char *file, |
1604 |
/* pointer to colon-separated list */ |
1605 |
char **cdpathp, |
1606 |
XString *xsp, |
1607 |
int *phys_pathp) |
1608 |
{ |
1609 |
int rval = 0; |
1610 |
bool use_cdpath = true; |
1611 |
char *plist; |
1612 |
size_t len, plen = 0; |
1613 |
char *xp = Xstring(*xsp, xp); |
1614 |
|
1615 |
if (!file) |
1616 |
file = null; |
1617 |
|
1618 |
if (file[0] == '/') { |
1619 |
*phys_pathp = 0; |
1620 |
use_cdpath = false; |
1621 |
} else { |
1622 |
if (file[0] == '.') { |
1623 |
char c = file[1]; |
1624 |
|
1625 |
if (c == '.') |
1626 |
c = file[2]; |
1627 |
if (c == '/' || c == '\0') |
1628 |
use_cdpath = false; |
1629 |
} |
1630 |
|
1631 |
plist = *cdpathp; |
1632 |
if (!plist) |
1633 |
use_cdpath = false; |
1634 |
else if (use_cdpath) { |
1635 |
char *pend; |
1636 |
|
1637 |
for (pend = plist; *pend && *pend != ':'; pend++) |
1638 |
; |
1639 |
plen = pend - plist; |
1640 |
*cdpathp = *pend ? pend + 1 : NULL; |
1641 |
} |
1642 |
|
1643 |
if ((!use_cdpath || !plen || plist[0] != '/') && |
1644 |
(cwd && *cwd)) { |
1645 |
len = strlen(cwd); |
1646 |
XcheckN(*xsp, xp, len); |
1647 |
memcpy(xp, cwd, len); |
1648 |
xp += len; |
1649 |
if (cwd[len - 1] != '/') |
1650 |
Xput(*xsp, xp, '/'); |
1651 |
} |
1652 |
*phys_pathp = Xlength(*xsp, xp); |
1653 |
if (use_cdpath && plen) { |
1654 |
XcheckN(*xsp, xp, plen); |
1655 |
memcpy(xp, plist, plen); |
1656 |
xp += plen; |
1657 |
if (plist[plen - 1] != '/') |
1658 |
Xput(*xsp, xp, '/'); |
1659 |
rval = 1; |
1660 |
} |
1661 |
} |
1662 |
|
1663 |
len = strlen(file) + 1; |
1664 |
XcheckN(*xsp, xp, len); |
1665 |
memcpy(xp, file, len); |
1666 |
|
1667 |
if (!use_cdpath) |
1668 |
*cdpathp = NULL; |
1669 |
|
1670 |
return (rval); |
1671 |
} |
1672 |
|
1673 |
/*- |
1674 |
* Simplify pathnames containing "." and ".." entries. |
1675 |
* |
1676 |
* simplify_path(this) = that |
1677 |
* /a/b/c/./../d/.. /a/b |
1678 |
* //./C/foo/bar/../baz //C/foo/baz |
1679 |
* /foo/ /foo |
1680 |
* /foo/../../bar /bar |
1681 |
* /foo/./blah/.. /foo |
1682 |
* . . |
1683 |
* .. .. |
1684 |
* ./foo foo |
1685 |
* foo/../../../bar ../../bar |
1686 |
*/ |
1687 |
void |
1688 |
simplify_path(char *p) |
1689 |
{ |
1690 |
char *dp, *ip, *sp, *tp; |
1691 |
size_t len; |
1692 |
bool needslash; |
1693 |
|
1694 |
switch (*p) { |
1695 |
case 0: |
1696 |
return; |
1697 |
case '/': |
1698 |
/* exactly two leading slashes? (SUSv4 3.266) */ |
1699 |
if (p[1] == '/' && p[2] != '/') |
1700 |
/* keep them, e.g. for UNC pathnames */ |
1701 |
++p; |
1702 |
needslash = true; |
1703 |
break; |
1704 |
default: |
1705 |
needslash = false; |
1706 |
} |
1707 |
dp = ip = sp = p; |
1708 |
|
1709 |
while (*ip) { |
1710 |
/* skip slashes in input */ |
1711 |
while (*ip == '/') |
1712 |
++ip; |
1713 |
if (!*ip) |
1714 |
break; |
1715 |
|
1716 |
/* get next pathname component from input */ |
1717 |
tp = ip; |
1718 |
while (*ip && *ip != '/') |
1719 |
++ip; |
1720 |
len = ip - tp; |
1721 |
|
1722 |
/* check input for "." and ".." */ |
1723 |
if (tp[0] == '.') { |
1724 |
if (len == 1) |
1725 |
/* just continue with the next one */ |
1726 |
continue; |
1727 |
else if (len == 2 && tp[1] == '.') { |
1728 |
/* parent level, but how? */ |
1729 |
if (*p == '/') |
1730 |
/* absolute path, only one way */ |
1731 |
goto strip_last_component; |
1732 |
else if (dp > sp) { |
1733 |
/* relative path, with subpaths */ |
1734 |
needslash = false; |
1735 |
strip_last_component: |
1736 |
/* strip off last pathname component */ |
1737 |
while (dp > sp) |
1738 |
if (*--dp == '/') |
1739 |
break; |
1740 |
} else { |
1741 |
/* relative path, at its beginning */ |
1742 |
if (needslash) |
1743 |
/* or already dotdot-slash'd */ |
1744 |
*dp++ = '/'; |
1745 |
/* keep dotdot-slash if not absolute */ |
1746 |
*dp++ = '.'; |
1747 |
*dp++ = '.'; |
1748 |
needslash = true; |
1749 |
sp = dp; |
1750 |
} |
1751 |
/* then continue with the next one */ |
1752 |
continue; |
1753 |
} |
1754 |
} |
1755 |
|
1756 |
if (needslash) |
1757 |
*dp++ = '/'; |
1758 |
|
1759 |
/* append next pathname component to output */ |
1760 |
memmove(dp, tp, len); |
1761 |
dp += len; |
1762 |
|
1763 |
/* append slash if we continue */ |
1764 |
needslash = true; |
1765 |
/* try next component */ |
1766 |
} |
1767 |
if (dp == p) |
1768 |
/* empty path -> dot */ |
1769 |
*dp++ = needslash ? '/' : '.'; |
1770 |
*dp = '\0'; |
1771 |
} |
1772 |
|
1773 |
void |
1774 |
set_current_wd(const char *nwd) |
1775 |
{ |
1776 |
char *allocd = NULL; |
1777 |
|
1778 |
if (nwd == NULL) { |
1779 |
allocd = ksh_get_wd(); |
1780 |
nwd = allocd ? allocd : null; |
1781 |
} |
1782 |
|
1783 |
afree(current_wd, APERM); |
1784 |
strdupx(current_wd, nwd, APERM); |
1785 |
|
1786 |
afree(allocd, ATEMP); |
1787 |
} |
1788 |
|
1789 |
int |
1790 |
c_cd(const char **wp) |
1791 |
{ |
1792 |
int optc, rv, phys_path; |
1793 |
bool physical = tobool(Flag(FPHYSICAL)); |
1794 |
/* was a node from cdpath added in? */ |
1795 |
int cdnode; |
1796 |
/* show where we went?, error for $PWD */ |
1797 |
bool printpath = false, eflag = false; |
1798 |
struct tbl *pwd_s, *oldpwd_s; |
1799 |
XString xs; |
1800 |
char *dir, *allocd = NULL, *tryp, *pwd, *cdpath; |
1801 |
|
1802 |
while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1) |
1803 |
switch (optc) { |
1804 |
case 'e': |
1805 |
eflag = true; |
1806 |
break; |
1807 |
case 'L': |
1808 |
physical = false; |
1809 |
break; |
1810 |
case 'P': |
1811 |
physical = true; |
1812 |
break; |
1813 |
case '?': |
1814 |
return (2); |
1815 |
} |
1816 |
wp += builtin_opt.optind; |
1817 |
|
1818 |
if (Flag(FRESTRICTED)) { |
1819 |
bi_errorf("restricted shell - can't cd"); |
1820 |
return (2); |
1821 |
} |
1822 |
|
1823 |
pwd_s = global("PWD"); |
1824 |
oldpwd_s = global("OLDPWD"); |
1825 |
|
1826 |
if (!wp[0]) { |
1827 |
/* No arguments - go home */ |
1828 |
if ((dir = str_val(global("HOME"))) == null) { |
1829 |
bi_errorf("no home directory (HOME not set)"); |
1830 |
return (2); |
1831 |
} |
1832 |
} else if (!wp[1]) { |
1833 |
/* One argument: - or dir */ |
1834 |
strdupx(allocd, wp[0], ATEMP); |
1835 |
if (ksh_isdash((dir = allocd))) { |
1836 |
afree(allocd, ATEMP); |
1837 |
allocd = NULL; |
1838 |
dir = str_val(oldpwd_s); |
1839 |
if (dir == null) { |
1840 |
bi_errorf("no OLDPWD"); |
1841 |
return (2); |
1842 |
} |
1843 |
printpath = true; |
1844 |
} |
1845 |
} else if (!wp[2]) { |
1846 |
/* Two arguments - substitute arg1 in PWD for arg2 */ |
1847 |
size_t ilen, olen, nlen, elen; |
1848 |
char *cp; |
1849 |
|
1850 |
if (!current_wd[0]) { |
1851 |
bi_errorf("can't determine current directory"); |
1852 |
return (2); |
1853 |
} |
1854 |
/* |
1855 |
* substitute arg1 for arg2 in current path. |
1856 |
* if the first substitution fails because the cd fails |
1857 |
* we could try to find another substitution. For now |
1858 |
* we don't |
1859 |
*/ |
1860 |
if ((cp = strstr(current_wd, wp[0])) == NULL) { |
1861 |
bi_errorf("bad substitution"); |
1862 |
return (2); |
1863 |
} |
1864 |
/*- |
1865 |
* ilen = part of current_wd before wp[0] |
1866 |
* elen = part of current_wd after wp[0] |
1867 |
* because current_wd and wp[1] need to be in memory at the |
1868 |
* same time beforehand the addition can stay unchecked |
1869 |
*/ |
1870 |
ilen = cp - current_wd; |
1871 |
olen = strlen(wp[0]); |
1872 |
nlen = strlen(wp[1]); |
1873 |
elen = strlen(current_wd + ilen + olen) + 1; |
1874 |
dir = allocd = alloc(ilen + nlen + elen, ATEMP); |
1875 |
memcpy(dir, current_wd, ilen); |
1876 |
memcpy(dir + ilen, wp[1], nlen); |
1877 |
memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen); |
1878 |
printpath = true; |
1879 |
} else { |
1880 |
bi_errorf("too many arguments"); |
1881 |
return (2); |
1882 |
} |
1883 |
|
1884 |
#ifdef MKSH__NO_PATH_MAX |
1885 |
/* only a first guess; make_path will enlarge xs if necessary */ |
1886 |
XinitN(xs, 1024, ATEMP); |
1887 |
#else |
1888 |
XinitN(xs, PATH_MAX, ATEMP); |
1889 |
#endif |
1890 |
|
1891 |
cdpath = str_val(global("CDPATH")); |
1892 |
do { |
1893 |
cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path); |
1894 |
if (physical) |
1895 |
rv = chdir(tryp = Xstring(xs, xp) + phys_path); |
1896 |
else { |
1897 |
simplify_path(Xstring(xs, xp)); |
1898 |
rv = chdir(tryp = Xstring(xs, xp)); |
1899 |
} |
1900 |
} while (rv < 0 && cdpath != NULL); |
1901 |
|
1902 |
if (rv < 0) { |
1903 |
if (cdnode) |
1904 |
bi_errorf("%s: %s", dir, "bad directory"); |
1905 |
else |
1906 |
bi_errorf("%s: %s", tryp, cstrerror(errno)); |
1907 |
afree(allocd, ATEMP); |
1908 |
Xfree(xs, xp); |
1909 |
return (2); |
1910 |
} |
1911 |
|
1912 |
rv = 0; |
1913 |
|
1914 |
/* allocd (above) => dir, which is no longer used */ |
1915 |
afree(allocd, ATEMP); |
1916 |
allocd = NULL; |
1917 |
|
1918 |
/* Clear out tracked aliases with relative paths */ |
1919 |
flushcom(false); |
1920 |
|
1921 |
/* |
1922 |
* Set OLDPWD (note: unsetting OLDPWD does not disable this |
1923 |
* setting in AT&T ksh) |
1924 |
*/ |
1925 |
if (current_wd[0]) |
1926 |
/* Ignore failure (happens if readonly or integer) */ |
1927 |
setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR); |
1928 |
|
1929 |
if (Xstring(xs, xp)[0] != '/') { |
1930 |
pwd = NULL; |
1931 |
} else if (!physical) { |
1932 |
goto norealpath_PWD; |
1933 |
} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) { |
1934 |
if (eflag) |
1935 |
rv = 1; |
1936 |
norealpath_PWD: |
1937 |
pwd = Xstring(xs, xp); |
1938 |
} |
1939 |
|
1940 |
/* Set PWD */ |
1941 |
if (pwd) { |
1942 |
char *ptmp = pwd; |
1943 |
|
1944 |
set_current_wd(ptmp); |
1945 |
/* Ignore failure (happens if readonly or integer) */ |
1946 |
setstr(pwd_s, ptmp, KSH_RETURN_ERROR); |
1947 |
} else { |
1948 |
set_current_wd(null); |
1949 |
pwd = Xstring(xs, xp); |
1950 |
/* XXX unset $PWD? */ |
1951 |
if (eflag) |
1952 |
rv = 1; |
1953 |
} |
1954 |
if (printpath || cdnode) |
1955 |
shprintf("%s\n", pwd); |
1956 |
|
1957 |
afree(allocd, ATEMP); |
1958 |
Xfree(xs, xp); |
1959 |
return (rv); |
1960 |
} |
1961 |
|
1962 |
|
1963 |
#ifdef KSH_CHVT_CODE |
1964 |
extern void chvt_reinit(void); |
1965 |
|
1966 |
static void |
1967 |
chvt(const Getopt *go) |
1968 |
{ |
1969 |
const char *dv = go->optarg; |
1970 |
char *cp = NULL; |
1971 |
int fd; |
1972 |
|
1973 |
switch (*dv) { |
1974 |
case '-': |
1975 |
dv = "/dev/null"; |
1976 |
break; |
1977 |
case '!': |
1978 |
++dv; |
1979 |
/* FALLTHROUGH */ |
1980 |
default: { |
1981 |
struct stat sb; |
1982 |
|
1983 |
if (stat(dv, &sb)) { |
1984 |
cp = shf_smprintf("/dev/ttyC%s", dv); |
1985 |
dv = cp; |
1986 |
if (stat(dv, &sb)) { |
1987 |
memmove(cp + 1, cp, /* /dev/tty */ 8); |
1988 |
dv = cp + 1; |
1989 |
if (stat(dv, &sb)) { |
1990 |
errorf("%s: %s: %s", "chvt", |
1991 |
"can't find tty", go->optarg); |
1992 |
} |
1993 |
} |
1994 |
} |
1995 |
if (!(sb.st_mode & S_IFCHR)) |
1996 |
errorf("%s: %s: %s", "chvt", "not a char device", dv); |
1997 |
#ifndef MKSH_DISABLE_REVOKE_WARNING |
1998 |
#if HAVE_REVOKE |
1999 |
if (revoke(dv)) |
2000 |
#endif |
2001 |
warningf(false, "%s: %s %s", "chvt", |
2002 |
"new shell is potentially insecure, can't revoke", |
2003 |
dv); |
2004 |
#endif |
2005 |
} |
2006 |
} |
2007 |
if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) { |
2008 |
sleep(1); |
2009 |
if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) { |
2010 |
errorf("%s: %s %s", "chvt", "can't open", dv); |
2011 |
} |
2012 |
} |
2013 |
if (go->optarg[0] != '!') { |
2014 |
switch (fork()) { |
2015 |
case -1: |
2016 |
errorf("%s: %s %s", "chvt", "fork", "failed"); |
2017 |
case 0: |
2018 |
break; |
2019 |
default: |
2020 |
exit(0); |
2021 |
} |
2022 |
} |
2023 |
if (setsid() == -1) |
2024 |
errorf("%s: %s %s", "chvt", "setsid", "failed"); |
2025 |
if (go->optarg[0] != '-') { |
2026 |
if (ioctl(fd, TIOCSCTTY, NULL) == -1) |
2027 |
errorf("%s: %s %s", "chvt", "TIOCSCTTY", "failed"); |
2028 |
if (tcflush(fd, TCIOFLUSH)) |
2029 |
errorf("%s: %s %s", "chvt", "TCIOFLUSH", "failed"); |
2030 |
} |
2031 |
ksh_dup2(fd, 0, false); |
2032 |
ksh_dup2(fd, 1, false); |
2033 |
ksh_dup2(fd, 2, false); |
2034 |
if (fd > 2) |
2035 |
close(fd); |
2036 |
rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt))); |
2037 |
chvt_reinit(); |
2038 |
} |
2039 |
#endif |
2040 |
|
2041 |
#ifdef DEBUG |
2042 |
char * |
2043 |
strchr(char *p, int ch) |
2044 |
{ |
2045 |
for (;; ++p) { |
2046 |
if (*p == ch) |
2047 |
return (p); |
2048 |
if (!*p) |
2049 |
return (NULL); |
2050 |
} |
2051 |
/* NOTREACHED */ |
2052 |
} |
2053 |
|
2054 |
char * |
2055 |
strstr(char *b, const char *l) |
2056 |
{ |
2057 |
char first, c; |
2058 |
size_t n; |
2059 |
|
2060 |
if ((first = *l++) == '\0') |
2061 |
return (b); |
2062 |
n = strlen(l); |
2063 |
strstr_look: |
2064 |
while ((c = *b++) != first) |
2065 |
if (c == '\0') |
2066 |
return (NULL); |
2067 |
if (strncmp(b, l, n)) |
2068 |
goto strstr_look; |
2069 |
return (b - 1); |
2070 |
} |
2071 |
#endif |
2072 |
|
2073 |
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST) |
2074 |
char * |
2075 |
strndup_i(const char *src, size_t len, Area *ap) |
2076 |
{ |
2077 |
char *dst = NULL; |
2078 |
|
2079 |
if (src != NULL) { |
2080 |
dst = alloc(len + 1, ap); |
2081 |
memcpy(dst, src, len); |
2082 |
dst[len] = '\0'; |
2083 |
} |
2084 |
return (dst); |
2085 |
} |
2086 |
|
2087 |
char * |
2088 |
strdup_i(const char *src, Area *ap) |
2089 |
{ |
2090 |
return (src == NULL ? NULL : strndup_i(src, strlen(src), ap)); |
2091 |
} |
2092 |
#endif |
2093 |
|
2094 |
#if !HAVE_GETRUSAGE |
2095 |
#define INVTCK(r,t) do { \ |
2096 |
r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \ |
2097 |
r.tv_sec = (t) / CLK_TCK; \ |
2098 |
} while (/* CONSTCOND */ 0) |
2099 |
|
2100 |
int |
2101 |
getrusage(int what, struct rusage *ru) |
2102 |
{ |
2103 |
struct tms tms; |
2104 |
clock_t u, s; |
2105 |
|
2106 |
if (/* ru == NULL || */ times(&tms) == (clock_t)-1) |
2107 |
return (-1); |
2108 |
|
2109 |
switch (what) { |
2110 |
case RUSAGE_SELF: |
2111 |
u = tms.tms_utime; |
2112 |
s = tms.tms_stime; |
2113 |
break; |
2114 |
case RUSAGE_CHILDREN: |
2115 |
u = tms.tms_cutime; |
2116 |
s = tms.tms_cstime; |
2117 |
break; |
2118 |
default: |
2119 |
errno = EINVAL; |
2120 |
return (-1); |
2121 |
} |
2122 |
INVTCK(ru->ru_utime, u); |
2123 |
INVTCK(ru->ru_stime, s); |
2124 |
return (0); |
2125 |
} |
2126 |
#endif |
2127 |
|
2128 |
/* |
2129 |
* process the string available via fg (get a char) |
2130 |
* and fp (put back a char) for backslash escapes, |
2131 |
* assuming the first call to *fg gets the char di- |
2132 |
* rectly after the backslash; return the character |
2133 |
* (0..0xFF), Unicode (wc + 0x100), or -1 if no known |
2134 |
* escape sequence was found |
2135 |
*/ |
2136 |
int |
2137 |
unbksl(bool cstyle, int (*fg)(void), void (*fp)(int)) |
2138 |
{ |
2139 |
int wc, i, c, fc; |
2140 |
|
2141 |
fc = (*fg)(); |
2142 |
switch (fc) { |
2143 |
case 'a': |
2144 |
/* |
2145 |
* according to the comments in pdksh, \007 seems |
2146 |
* to be more portable than \a (due to HP-UX cc, |
2147 |
* Ultrix cc, old pcc, etc.) so we avoid the escape |
2148 |
* sequence altogether in mksh and assume ASCII |
2149 |
*/ |
2150 |
wc = 7; |
2151 |
break; |
2152 |
case 'b': |
2153 |
wc = '\b'; |
2154 |
break; |
2155 |
case 'c': |
2156 |
if (!cstyle) |
2157 |
goto unknown_escape; |
2158 |
c = (*fg)(); |
2159 |
wc = CTRL(c); |
2160 |
break; |
2161 |
case 'E': |
2162 |
case 'e': |
2163 |
wc = 033; |
2164 |
break; |
2165 |
case 'f': |
2166 |
wc = '\f'; |
2167 |
break; |
2168 |
case 'n': |
2169 |
wc = '\n'; |
2170 |
break; |
2171 |
case 'r': |
2172 |
wc = '\r'; |
2173 |
break; |
2174 |
case 't': |
2175 |
wc = '\t'; |
2176 |
break; |
2177 |
case 'v': |
2178 |
/* assume ASCII here as well */ |
2179 |
wc = 11; |
2180 |
break; |
2181 |
case '1': |
2182 |
case '2': |
2183 |
case '3': |
2184 |
case '4': |
2185 |
case '5': |
2186 |
case '6': |
2187 |
case '7': |
2188 |
if (!cstyle) |
2189 |
goto unknown_escape; |
2190 |
/* FALLTHROUGH */ |
2191 |
case '0': |
2192 |
if (cstyle) |
2193 |
(*fp)(fc); |
2194 |
/* |
2195 |
* look for an octal number with up to three |
2196 |
* digits, not counting the leading zero; |
2197 |
* convert it to a raw octet |
2198 |
*/ |
2199 |
wc = 0; |
2200 |
i = 3; |
2201 |
while (i--) |
2202 |
if ((c = (*fg)()) >= '0' && c <= '7') |
2203 |
wc = (wc << 3) + (c - '0'); |
2204 |
else { |
2205 |
(*fp)(c); |
2206 |
break; |
2207 |
} |
2208 |
break; |
2209 |
case 'U': |
2210 |
i = 8; |
2211 |
if (/* CONSTCOND */ 0) |
2212 |
/* FALLTHROUGH */ |
2213 |
case 'u': |
2214 |
i = 4; |
2215 |
if (/* CONSTCOND */ 0) |
2216 |
/* FALLTHROUGH */ |
2217 |
case 'x': |
2218 |
i = cstyle ? -1 : 2; |
2219 |
/** |
2220 |
* x: look for a hexadecimal number with up to |
2221 |
* two (C style: arbitrary) digits; convert |
2222 |
* to raw octet (C style: Unicode if >0xFF) |
2223 |
* u/U: look for a hexadecimal number with up to |
2224 |
* four (U: eight) digits; convert to Unicode |
2225 |
*/ |
2226 |
wc = 0; |
2227 |
while (i--) { |
2228 |
wc <<= 4; |
2229 |
if ((c = (*fg)()) >= '0' && c <= '9') |
2230 |
wc += c - '0'; |
2231 |
else if (c >= 'A' && c <= 'F') |
2232 |
wc += c - 'A' + 10; |
2233 |
else if (c >= 'a' && c <= 'f') |
2234 |
wc += c - 'a' + 10; |
2235 |
else { |
2236 |
wc >>= 4; |
2237 |
(*fp)(c); |
2238 |
break; |
2239 |
} |
2240 |
} |
2241 |
if ((cstyle && wc > 0xFF) || fc != 'x') |
2242 |
/* Unicode marker */ |
2243 |
wc += 0x100; |
2244 |
break; |
2245 |
case '\'': |
2246 |
if (!cstyle) |
2247 |
goto unknown_escape; |
2248 |
wc = '\''; |
2249 |
break; |
2250 |
case '\\': |
2251 |
wc = '\\'; |
2252 |
break; |
2253 |
default: |
2254 |
unknown_escape: |
2255 |
(*fp)(fc); |
2256 |
return (-1); |
2257 |
} |
2258 |
|
2259 |
return (wc); |
2260 |
} |