Build.sh but use 'if defined(PRECOND) && !defined(TOBEDEFINED)'if possible * for all of the source code, drop annotations "imake style" (if we check for specific OSes, bad, instead of using mirtoconf checks proper) and "conditions correct?" (if I'm not entirely sure if that #if catches all cases and no false positives) where I can see it by grepping immediately * bump mksh patchlevel * refresh Makefiles
1528 lines
34 KiB
C
1528 lines
34 KiB
C
/* $OpenBSD: misc.c,v 1.37 2009/04/19 20:34:05 sthen Exp $ */
|
|
/* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
|
|
* Thorsten Glaser <tg@mirbsd.org>
|
|
*
|
|
* Provided that these terms and disclaimer and all copyright notices
|
|
* are retained or reproduced in an accompanying document, permission
|
|
* is granted to deal in this work without restriction, including un-
|
|
* limited rights to use, publicly perform, distribute, sell, modify,
|
|
* merge, give away, or sublicence.
|
|
*
|
|
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
|
|
* the utmost extent permitted by applicable law, neither express nor
|
|
* implied; without malicious intent or gross negligence. In no event
|
|
* may a licensor, author or contributor be held liable for indirect,
|
|
* direct, other damage, loss, or other issues arising in any way out
|
|
* of dealing in the work, even if advised of the possibility of such
|
|
* damage or existence of a defect, except proven that it results out
|
|
* of said person's immediate fault when using the work as intended.
|
|
*/
|
|
|
|
#include "sh.h"
|
|
#if !HAVE_GETRUSAGE
|
|
#include <sys/times.h>
|
|
#endif
|
|
#if HAVE_GRP_H
|
|
#include <grp.h>
|
|
#endif
|
|
|
|
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.112 2009/07/25 21:31:26 tg Exp $");
|
|
|
|
#undef USE_CHVT
|
|
/* XXX conditions correct? */
|
|
#if defined(TIOCSCTTY) && !defined(MKSH_SMALL)
|
|
#define USE_CHVT
|
|
#endif
|
|
|
|
unsigned char chtypes[UCHAR_MAX + 1]; /* type bits for unsigned char */
|
|
|
|
#if !HAVE_SETRESUGID
|
|
uid_t kshuid;
|
|
gid_t kshgid, kshegid;
|
|
#endif
|
|
|
|
static int do_gmatch(const unsigned char *, const unsigned char *,
|
|
const unsigned char *, const unsigned char *);
|
|
static const unsigned char *cclass(const unsigned char *, int);
|
|
#ifdef USE_CHVT
|
|
static void chvt(const char *);
|
|
#endif
|
|
static char *do_phys_path(XString *, char *, const char *);
|
|
|
|
/*
|
|
* Fast character classes
|
|
*/
|
|
void
|
|
setctypes(const char *s, int t)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (t & C_IFS) {
|
|
for (i = 0; i < UCHAR_MAX + 1; i++)
|
|
chtypes[i] &= ~C_IFS;
|
|
chtypes[0] |= C_IFS; /* include \0 in C_IFS */
|
|
}
|
|
while (*s != 0)
|
|
chtypes[(unsigned char)*s++] |= t;
|
|
}
|
|
|
|
void
|
|
initctypes(void)
|
|
{
|
|
int c;
|
|
|
|
for (c = 'a'; c <= 'z'; c++)
|
|
chtypes[c] |= C_ALPHA;
|
|
for (c = 'A'; c <= 'Z'; c++)
|
|
chtypes[c] |= C_ALPHA;
|
|
chtypes['_'] |= C_ALPHA;
|
|
setctypes("0123456789", C_DIGIT);
|
|
setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */
|
|
setctypes("*@#!$-?", C_VAR1);
|
|
setctypes(" \t\n", C_IFSWS);
|
|
setctypes("=-+?", C_SUBOP1);
|
|
setctypes(" \n\t\"#$&'()*;<>?[]\\`|", C_QUOTE);
|
|
}
|
|
|
|
/* called from XcheckN() to grow buffer */
|
|
char *
|
|
Xcheck_grow_(XString *xsp, const char *xp, unsigned int more)
|
|
{
|
|
const char *old_beg = xsp->beg;
|
|
|
|
xsp->len += more > xsp->len ? more : xsp->len;
|
|
xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap);
|
|
xsp->end = xsp->beg + xsp->len;
|
|
return (xsp->beg + (xp - old_beg));
|
|
}
|
|
|
|
const struct shoption options[] = {
|
|
/* Special cases (see parse_args()): -A, -o, -s.
|
|
* Options are sorted by their longnames - the order of these
|
|
* entries MUST match the order of sh_flag F* enumerations in sh.h.
|
|
*/
|
|
{ "allexport", 'a', OF_ANY },
|
|
#if HAVE_ARC4RANDOM
|
|
{ "arc4random", 0, OF_ANY },
|
|
#endif
|
|
{ "braceexpand", 0, OF_ANY }, /* non-standard */
|
|
#if HAVE_NICE
|
|
{ "bgnice", 0, OF_ANY },
|
|
#endif
|
|
{ NULL, 'c', OF_CMDLINE },
|
|
{ "emacs", 0, OF_ANY },
|
|
{ "errexit", 'e', OF_ANY },
|
|
{ "gmacs", 0, OF_ANY },
|
|
{ "ignoreeof", 0, OF_ANY },
|
|
{ "interactive", 'i', OF_CMDLINE },
|
|
{ "keyword", 'k', OF_ANY },
|
|
{ "login", 'l', OF_CMDLINE },
|
|
{ "markdirs", 'X', OF_ANY },
|
|
#ifndef MKSH_UNEMPLOYED
|
|
{ "monitor", 'm', OF_ANY },
|
|
#else
|
|
{ NULL, 'm', 0 }, /* needed */
|
|
#endif
|
|
{ "noclobber", 'C', OF_ANY },
|
|
{ "noexec", 'n', OF_ANY },
|
|
{ "noglob", 'f', OF_ANY },
|
|
{ "nohup", 0, OF_ANY },
|
|
{ "nolog", 0, OF_ANY }, /* no effect */
|
|
#ifndef MKSH_UNEMPLOYED
|
|
{ "notify", 'b', OF_ANY },
|
|
#endif
|
|
{ "nounset", 'u', OF_ANY },
|
|
{ "physical", 0, OF_ANY }, /* non-standard */
|
|
{ "posix", 0, OF_ANY }, /* non-standard */
|
|
{ "privileged", 'p', OF_ANY },
|
|
{ "restricted", 'r', OF_CMDLINE },
|
|
{ "stdin", 's', OF_CMDLINE }, /* pseudo non-st.. */
|
|
{ "trackall", 'h', OF_ANY },
|
|
{ "utf8-mode", 'U', OF_ANY }, /* non-standard */
|
|
{ "verbose", 'v', OF_ANY },
|
|
#ifndef MKSH_NOVI
|
|
{ "vi", 0, OF_ANY },
|
|
{ "viraw", 0, OF_ANY }, /* no effect */
|
|
{ "vi-tabcomplete", 0, OF_ANY }, /* non-standard */
|
|
{ "vi-esccomplete", 0, OF_ANY }, /* non-standard */
|
|
#endif
|
|
{ "xtrace", 'x', OF_ANY },
|
|
/* Anonymous flags: used internally by shell only
|
|
* (not visible to user)
|
|
*/
|
|
{ NULL, 0, OF_INTERNAL }, /* FTALKING_I */
|
|
};
|
|
|
|
/*
|
|
* translate -o option into F* constant (also used for test -o option)
|
|
*/
|
|
size_t
|
|
option(const char *n)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < NELEM(options); i++)
|
|
if (options[i].name && strcmp(options[i].name, n) == 0)
|
|
return (i);
|
|
|
|
return ((size_t)-1);
|
|
}
|
|
|
|
struct options_info {
|
|
int opt_width;
|
|
int opts[NELEM(options)];
|
|
};
|
|
|
|
static char *options_fmt_entry(const void *arg, int, char *, int);
|
|
static void printoptions(int);
|
|
|
|
/* format a single select menu item */
|
|
static char *
|
|
options_fmt_entry(const void *arg, int i, char *buf, int buflen)
|
|
{
|
|
const struct options_info *oi = (const struct options_info *)arg;
|
|
|
|
shf_snprintf(buf, buflen, "%-*s %s",
|
|
oi->opt_width, options[oi->opts[i]].name,
|
|
Flag(oi->opts[i]) ? "on" : "off");
|
|
return (buf);
|
|
}
|
|
|
|
static void
|
|
printoptions(int verbose)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (verbose) {
|
|
struct options_info oi;
|
|
int n, len;
|
|
|
|
/* verbose version */
|
|
shf_puts("Current option settings\n", shl_stdout);
|
|
|
|
for (i = n = oi.opt_width = 0; i < NELEM(options); i++)
|
|
if (options[i].name) {
|
|
len = strlen(options[i].name);
|
|
oi.opts[n++] = i;
|
|
if (len > oi.opt_width)
|
|
oi.opt_width = len;
|
|
}
|
|
print_columns(shl_stdout, n, options_fmt_entry, &oi,
|
|
oi.opt_width + 5, 1);
|
|
} else {
|
|
/* short version ala ksh93 */
|
|
shf_puts("set", shl_stdout);
|
|
for (i = 0; i < NELEM(options); i++)
|
|
if (Flag(i) && options[i].name)
|
|
shprintf(" -o %s", options[i].name);
|
|
shf_putc('\n', shl_stdout);
|
|
}
|
|
}
|
|
|
|
char *
|
|
getoptions(void)
|
|
{
|
|
unsigned int i;
|
|
char m[(int) FNFLAGS + 1];
|
|
char *cp = m;
|
|
|
|
for (i = 0; i < NELEM(options); i++)
|
|
if (options[i].c && Flag(i))
|
|
*cp++ = options[i].c;
|
|
strndupx(cp, m, cp - m, ATEMP);
|
|
return (cp);
|
|
}
|
|
|
|
/* change a Flag(*) value; takes care of special actions */
|
|
void
|
|
change_flag(enum sh_flag f,
|
|
int what, /* flag to change */
|
|
char newval) /* what is changing the flag (command line vs set) */
|
|
{
|
|
char oldval;
|
|
|
|
oldval = Flag(f);
|
|
Flag(f) = newval ? 1 : 0; /* needed for tristates */
|
|
#ifndef MKSH_UNEMPLOYED
|
|
if (f == FMONITOR) {
|
|
if (what != OF_CMDLINE && newval != oldval)
|
|
j_change();
|
|
} else
|
|
#endif
|
|
if ((
|
|
#ifndef MKSH_NOVI
|
|
f == FVI ||
|
|
#endif
|
|
f == FEMACS || f == FGMACS) && newval) {
|
|
#ifndef MKSH_NOVI
|
|
Flag(FVI) =
|
|
#endif
|
|
Flag(FEMACS) = Flag(FGMACS) = 0;
|
|
Flag(f) = newval;
|
|
} else if (f == FPRIVILEGED && oldval && !newval) {
|
|
/* Turning off -p? */
|
|
#if HAVE_SETRESUGID
|
|
gid_t kshegid = getgid();
|
|
|
|
setresgid(kshegid, kshegid, kshegid);
|
|
#if HAVE_SETGROUPS
|
|
setgroups(1, &kshegid);
|
|
#endif
|
|
setresuid(ksheuid, ksheuid, ksheuid);
|
|
#else
|
|
seteuid(ksheuid = kshuid = getuid());
|
|
setuid(ksheuid);
|
|
setegid(kshegid = kshgid = getgid());
|
|
setgid(kshegid);
|
|
#endif
|
|
} else if (f == FPOSIX && newval) {
|
|
Flag(FBRACEEXPAND) = 0;
|
|
}
|
|
/* Changing interactive flag? */
|
|
if (f == FTALKING) {
|
|
if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
|
|
Flag(FTALKING_I) = newval;
|
|
}
|
|
}
|
|
|
|
/* Parse command line & set command arguments. Returns the index of
|
|
* non-option arguments, -1 if there is an error.
|
|
*/
|
|
int
|
|
parse_args(const char **argv,
|
|
int what, /* OF_CMDLINE or OF_SET */
|
|
int *setargsp)
|
|
{
|
|
static char cmd_opts[NELEM(options) + 5]; /* o:T:\0 */
|
|
static char set_opts[NELEM(options) + 6]; /* A:o;s\0 */
|
|
char set, *opts;
|
|
const char *array = NULL;
|
|
Getopt go;
|
|
size_t i;
|
|
int optc, sortargs = 0, arrayset = 0;
|
|
|
|
/* First call? Build option strings... */
|
|
if (cmd_opts[0] == '\0') {
|
|
char *p = cmd_opts, *q = set_opts;
|
|
|
|
/* see cmd_opts[] declaration */
|
|
*p++ = 'o';
|
|
*p++ = ':';
|
|
#ifndef MKSH_SMALL
|
|
*p++ = 'T';
|
|
*p++ = ':';
|
|
#endif
|
|
/* see set_opts[] declaration */
|
|
*q++ = 'A';
|
|
*q++ = ':';
|
|
*q++ = 'o';
|
|
*q++ = ';';
|
|
*q++ = 's';
|
|
|
|
for (i = 0; i < NELEM(options); i++) {
|
|
if (options[i].c) {
|
|
if (options[i].flags & OF_CMDLINE)
|
|
*p++ = options[i].c;
|
|
if (options[i].flags & OF_SET)
|
|
*q++ = options[i].c;
|
|
}
|
|
}
|
|
*p = '\0';
|
|
*q = '\0';
|
|
}
|
|
|
|
if (what == OF_CMDLINE) {
|
|
const char *p = argv[0], *q;
|
|
/* Set FLOGIN before parsing options so user can clear
|
|
* flag using +l.
|
|
*/
|
|
if (*p != '-')
|
|
for (q = p; *q; )
|
|
if (*q++ == '/')
|
|
p = q;
|
|
Flag(FLOGIN) = (*p == '-');
|
|
opts = cmd_opts;
|
|
} else if (what == OF_FIRSTTIME) {
|
|
opts = cmd_opts;
|
|
} else
|
|
opts = set_opts;
|
|
ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
|
|
while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
|
|
set = (go.info & GI_PLUS) ? 0 : 1;
|
|
switch (optc) {
|
|
case 'A':
|
|
if (what == OF_FIRSTTIME)
|
|
break;
|
|
arrayset = set ? 1 : -1;
|
|
array = go.optarg;
|
|
break;
|
|
|
|
case 'o':
|
|
if (what == OF_FIRSTTIME)
|
|
break;
|
|
if (go.optarg == NULL) {
|
|
/* lone -o: print options
|
|
*
|
|
* Note that on the command line, -o requires
|
|
* an option (ie, can't get here if what is
|
|
* OF_CMDLINE).
|
|
*/
|
|
printoptions(set);
|
|
break;
|
|
}
|
|
i = option(go.optarg);
|
|
if ((i != (size_t)-1) && set == Flag(i))
|
|
/* Don't check the context if the flag
|
|
* isn't changing - makes "set -o interactive"
|
|
* work if you're already interactive. Needed
|
|
* if the output of "set +o" is to be used.
|
|
*/
|
|
;
|
|
else if ((i != (size_t)-1) && (options[i].flags & what))
|
|
change_flag((enum sh_flag)i, what, set);
|
|
else {
|
|
bi_errorf("%s: bad option", go.optarg);
|
|
return (-1);
|
|
}
|
|
break;
|
|
|
|
#ifndef MKSH_SMALL
|
|
case 'T':
|
|
if (what != OF_FIRSTTIME)
|
|
break;
|
|
#ifndef USE_CHVT
|
|
errorf("no TIOCSCTTY ioctl");
|
|
#else
|
|
change_flag(FTALKING, OF_CMDLINE, 1);
|
|
chvt(go.optarg);
|
|
break;
|
|
#endif
|
|
#endif
|
|
|
|
case '?':
|
|
return (-1);
|
|
|
|
default:
|
|
if (what == OF_FIRSTTIME)
|
|
break;
|
|
/* -s: sort positional params (AT&T ksh stupidity) */
|
|
if (what == OF_SET && optc == 's') {
|
|
sortargs = 1;
|
|
break;
|
|
}
|
|
for (i = 0; i < NELEM(options); i++)
|
|
if (optc == options[i].c &&
|
|
(what & options[i].flags)) {
|
|
change_flag((enum sh_flag)i, what, set);
|
|
break;
|
|
}
|
|
if (i == NELEM(options))
|
|
internal_errorf("parse_args: '%c'", optc);
|
|
}
|
|
}
|
|
if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
|
|
(argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
|
|
argv[go.optind][1] == '\0') {
|
|
/* lone - clears -v and -x flags */
|
|
if (argv[go.optind][0] == '-')
|
|
Flag(FVERBOSE) = Flag(FXTRACE) = 0;
|
|
/* set skips lone - or + option */
|
|
go.optind++;
|
|
}
|
|
if (setargsp)
|
|
/* -- means set $#/$* even if there are no arguments */
|
|
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
|
|
argv[go.optind]);
|
|
|
|
if (arrayset && (!*array || *skip_varname(array, false))) {
|
|
bi_errorf("%s: is not an identifier", array);
|
|
return (-1);
|
|
}
|
|
if (sortargs) {
|
|
for (i = go.optind; argv[i]; i++)
|
|
;
|
|
qsort(&argv[go.optind], i - go.optind, sizeof(void *),
|
|
xstrcmp);
|
|
}
|
|
if (arrayset) {
|
|
set_array(array, arrayset, argv + go.optind);
|
|
for (; argv[go.optind]; go.optind++)
|
|
;
|
|
}
|
|
|
|
return (go.optind);
|
|
}
|
|
|
|
/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
|
|
int
|
|
getn(const char *s, int *ai)
|
|
{
|
|
int i, c, rv = 0;
|
|
bool neg = false;
|
|
|
|
do {
|
|
c = *s++;
|
|
} while (ksh_isspace(c));
|
|
if (c == '-') {
|
|
neg = true;
|
|
c = *s++;
|
|
} else if (c == '+')
|
|
c = *s++;
|
|
*ai = i = 0;
|
|
do {
|
|
if (!ksh_isdigit(c))
|
|
goto getn_out;
|
|
i *= 10;
|
|
if (i < *ai)
|
|
/* overflow */
|
|
goto getn_out;
|
|
i += c - '0';
|
|
*ai = i;
|
|
} while ((c = *s++));
|
|
rv = 1;
|
|
|
|
getn_out:
|
|
if (neg)
|
|
*ai = -*ai;
|
|
return (rv);
|
|
}
|
|
|
|
/* getn() that prints error */
|
|
int
|
|
bi_getn(const char *as, int *ai)
|
|
{
|
|
int rv;
|
|
|
|
if (!(rv = getn(as, ai)))
|
|
bi_errorf("%s: bad number", as);
|
|
return (rv);
|
|
}
|
|
|
|
/* -------- gmatch.c -------- */
|
|
|
|
/*
|
|
* int gmatch(string, pattern)
|
|
* char *string, *pattern;
|
|
*
|
|
* Match a pattern as in sh(1).
|
|
* pattern character are prefixed with MAGIC by expand.
|
|
*/
|
|
|
|
int
|
|
gmatchx(const char *s, const char *p, bool isfile)
|
|
{
|
|
const char *se, *pe;
|
|
|
|
if (s == NULL || p == NULL)
|
|
return (0);
|
|
|
|
se = s + strlen(s);
|
|
pe = p + strlen(p);
|
|
/* isfile is false iff no syntax check has been done on
|
|
* the pattern. If check fails, just to a strcmp().
|
|
*/
|
|
if (!isfile && !has_globbing(p, pe)) {
|
|
size_t len = pe - p + 1;
|
|
char tbuf[64];
|
|
char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
|
|
debunk(t, p, len);
|
|
return (!strcmp(t, s));
|
|
}
|
|
return (do_gmatch((const unsigned char *) s, (const unsigned char *) se,
|
|
(const unsigned char *) p, (const unsigned char *) pe));
|
|
}
|
|
|
|
/* Returns if p is a syntacticly correct globbing pattern, false
|
|
* if it contains no pattern characters or if there is a syntax error.
|
|
* Syntax errors are:
|
|
* - [ with no closing ]
|
|
* - imbalanced $(...) expression
|
|
* - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
|
|
*/
|
|
/*XXX
|
|
- if no magic,
|
|
if dest given, copy to dst
|
|
return ?
|
|
- if magic && (no globbing || syntax error)
|
|
debunk to dst
|
|
return ?
|
|
- return ?
|
|
*/
|
|
int
|
|
has_globbing(const char *xp, const char *xpe)
|
|
{
|
|
const unsigned char *p = (const unsigned char *) xp;
|
|
const unsigned char *pe = (const unsigned char *) xpe;
|
|
int c;
|
|
int nest = 0, bnest = 0;
|
|
int saw_glob = 0;
|
|
int in_bracket = 0; /* inside [...] */
|
|
|
|
for (; p < pe; p++) {
|
|
if (!ISMAGIC(*p))
|
|
continue;
|
|
if ((c = *++p) == '*' || c == '?')
|
|
saw_glob = 1;
|
|
else if (c == '[') {
|
|
if (!in_bracket) {
|
|
saw_glob = 1;
|
|
in_bracket = 1;
|
|
if (ISMAGIC(p[1]) && p[2] == NOT)
|
|
p += 2;
|
|
if (ISMAGIC(p[1]) && p[2] == ']')
|
|
p += 2;
|
|
}
|
|
/* XXX Do we need to check ranges here? POSIX Q */
|
|
} else if (c == ']') {
|
|
if (in_bracket) {
|
|
if (bnest) /* [a*(b]) */
|
|
return (0);
|
|
in_bracket = 0;
|
|
}
|
|
} else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) {
|
|
saw_glob = 1;
|
|
if (in_bracket)
|
|
bnest++;
|
|
else
|
|
nest++;
|
|
} else if (c == '|') {
|
|
if (in_bracket && !bnest) /* *(a[foo|bar]) */
|
|
return (0);
|
|
} else if (c == /*(*/ ')') {
|
|
if (in_bracket) {
|
|
if (!bnest--) /* *(a[b)c] */
|
|
return (0);
|
|
} else if (nest)
|
|
nest--;
|
|
}
|
|
/* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-]
|
|
MAGIC-{, MAGIC-,, MAGIC-} */
|
|
}
|
|
return (saw_glob && !in_bracket && !nest);
|
|
}
|
|
|
|
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
|
|
static int
|
|
do_gmatch(const unsigned char *s, const unsigned char *se,
|
|
const unsigned char *p, const unsigned char *pe)
|
|
{
|
|
int sc, pc;
|
|
const unsigned char *prest, *psub, *pnext;
|
|
const unsigned char *srest;
|
|
|
|
if (s == NULL || p == NULL)
|
|
return (0);
|
|
while (p < pe) {
|
|
pc = *p++;
|
|
sc = s < se ? *s : '\0';
|
|
s++;
|
|
if (!ISMAGIC(pc)) {
|
|
if (sc != pc)
|
|
return (0);
|
|
continue;
|
|
}
|
|
switch (*p++) {
|
|
case '[':
|
|
if (sc == 0 || (p = cclass(p, sc)) == NULL)
|
|
return (0);
|
|
break;
|
|
|
|
case '?':
|
|
if (sc == 0)
|
|
return (0);
|
|
break;
|
|
|
|
case '*':
|
|
if (p == pe)
|
|
return (1);
|
|
s--;
|
|
do {
|
|
if (do_gmatch(s, se, p, pe))
|
|
return (1);
|
|
} while (s++ < se);
|
|
return (0);
|
|
|
|
/**
|
|
* [*+?@!](pattern|pattern|..)
|
|
* This is also needed for ${..%..}, etc.
|
|
*/
|
|
case 0x80|'+': /* matches one or more times */
|
|
case 0x80|'*': /* matches zero or more times */
|
|
if (!(prest = pat_scan(p, pe, 0)))
|
|
return (0);
|
|
s--;
|
|
/* take care of zero matches */
|
|
if (p[-1] == (0x80 | '*') &&
|
|
do_gmatch(s, se, prest, pe))
|
|
return (1);
|
|
for (psub = p; ; psub = pnext) {
|
|
pnext = pat_scan(psub, pe, 1);
|
|
for (srest = s; srest <= se; srest++) {
|
|
if (do_gmatch(s, srest, psub, pnext - 2) &&
|
|
(do_gmatch(srest, se, prest, pe) ||
|
|
(s != srest && do_gmatch(srest,
|
|
se, p - 2, pe))))
|
|
return (1);
|
|
}
|
|
if (pnext == prest)
|
|
break;
|
|
}
|
|
return (0);
|
|
|
|
case 0x80|'?': /* matches zero or once */
|
|
case 0x80|'@': /* matches one of the patterns */
|
|
case 0x80|' ': /* simile for @ */
|
|
if (!(prest = pat_scan(p, pe, 0)))
|
|
return (0);
|
|
s--;
|
|
/* Take care of zero matches */
|
|
if (p[-1] == (0x80 | '?') &&
|
|
do_gmatch(s, se, prest, pe))
|
|
return (1);
|
|
for (psub = p; ; psub = pnext) {
|
|
pnext = pat_scan(psub, pe, 1);
|
|
srest = prest == pe ? se : s;
|
|
for (; srest <= se; srest++) {
|
|
if (do_gmatch(s, srest, psub, pnext - 2) &&
|
|
do_gmatch(srest, se, prest, pe))
|
|
return (1);
|
|
}
|
|
if (pnext == prest)
|
|
break;
|
|
}
|
|
return (0);
|
|
|
|
case 0x80|'!': /* matches none of the patterns */
|
|
if (!(prest = pat_scan(p, pe, 0)))
|
|
return (0);
|
|
s--;
|
|
for (srest = s; srest <= se; srest++) {
|
|
int matched = 0;
|
|
|
|
for (psub = p; ; psub = pnext) {
|
|
pnext = pat_scan(psub, pe, 1);
|
|
if (do_gmatch(s, srest, psub,
|
|
pnext - 2)) {
|
|
matched = 1;
|
|
break;
|
|
}
|
|
if (pnext == prest)
|
|
break;
|
|
}
|
|
if (!matched &&
|
|
do_gmatch(srest, se, prest, pe))
|
|
return (1);
|
|
}
|
|
return (0);
|
|
|
|
default:
|
|
if (sc != p[-1])
|
|
return (0);
|
|
break;
|
|
}
|
|
}
|
|
return (s == se);
|
|
}
|
|
|
|
static const unsigned char *
|
|
cclass(const unsigned char *p, int sub)
|
|
{
|
|
int c, d, not, found = 0;
|
|
const unsigned char *orig_p = p;
|
|
|
|
if ((not = (ISMAGIC(*p) && *++p == NOT)))
|
|
p++;
|
|
do {
|
|
c = *p++;
|
|
if (ISMAGIC(c)) {
|
|
c = *p++;
|
|
if ((c & 0x80) && !ISMAGIC(c)) {
|
|
c &= 0x7f;/* extended pattern matching: *+?@! */
|
|
/* XXX the ( char isn't handled as part of [] */
|
|
if (c == ' ') /* simile for @: plain (..) */
|
|
c = '(' /*)*/;
|
|
}
|
|
}
|
|
if (c == '\0')
|
|
/* No closing ] - act as if the opening [ was quoted */
|
|
return (sub == '[' ? orig_p : NULL);
|
|
if (ISMAGIC(p[0]) && p[1] == '-' &&
|
|
(!ISMAGIC(p[2]) || p[3] != ']')) {
|
|
p += 2; /* MAGIC- */
|
|
d = *p++;
|
|
if (ISMAGIC(d)) {
|
|
d = *p++;
|
|
if ((d & 0x80) && !ISMAGIC(d))
|
|
d &= 0x7f;
|
|
}
|
|
/* POSIX says this is an invalid expression */
|
|
if (c > d)
|
|
return (NULL);
|
|
} else
|
|
d = c;
|
|
if (c == sub || (c <= sub && sub <= d))
|
|
found = 1;
|
|
} while (!(ISMAGIC(p[0]) && p[1] == ']'));
|
|
|
|
return ((found != not) ? p+2 : NULL);
|
|
}
|
|
|
|
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
|
|
const unsigned char *
|
|
pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep)
|
|
{
|
|
int nest = 0;
|
|
|
|
for (; p < pe; p++) {
|
|
if (!ISMAGIC(*p))
|
|
continue;
|
|
if ((*++p == /*(*/ ')' && nest-- == 0) ||
|
|
(*p == '|' && match_sep && nest == 0))
|
|
return (p + 1);
|
|
if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f))
|
|
nest++;
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
xstrcmp(const void *p1, const void *p2)
|
|
{
|
|
return (strcmp(*(char * const *)p1, *(char * const *)p2));
|
|
}
|
|
|
|
/* Initialise a Getopt structure */
|
|
void
|
|
ksh_getopt_reset(Getopt *go, int flags)
|
|
{
|
|
go->optind = 1;
|
|
go->optarg = NULL;
|
|
go->p = 0;
|
|
go->flags = flags;
|
|
go->info = 0;
|
|
go->buf[1] = '\0';
|
|
}
|
|
|
|
|
|
/* getopt() used for shell built-in commands, the getopts command, and
|
|
* command line options.
|
|
* A leading ':' in options means don't print errors, instead return '?'
|
|
* or ':' and set go->optarg to the offending option character.
|
|
* If GF_ERROR is set (and option doesn't start with :), errors result in
|
|
* a call to bi_errorf().
|
|
*
|
|
* Non-standard features:
|
|
* - ';' is like ':' in options, except the argument is optional
|
|
* (if it isn't present, optarg is set to 0).
|
|
* Used for 'set -o'.
|
|
* - ',' is like ':' in options, except the argument always immediately
|
|
* follows the option character (optarg is set to the null string if
|
|
* the option is missing).
|
|
* Used for 'read -u2', 'print -u2' and fc -40.
|
|
* - '#' is like ':' in options, expect that the argument is optional
|
|
* and must start with a digit. If the argument doesn't start with a
|
|
* digit, it is assumed to be missing and normal option processing
|
|
* continues (optarg is set to 0 if the option is missing).
|
|
* Used for 'typeset -LZ4'.
|
|
* - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
|
|
* option starting with + is accepted, the GI_PLUS flag will be set
|
|
* in go->info.
|
|
*/
|
|
int
|
|
ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
|
|
{
|
|
char c;
|
|
const char *o;
|
|
|
|
if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
|
|
const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
|
|
|
|
go->p = 1;
|
|
if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
|
|
go->optind++;
|
|
go->p = 0;
|
|
go->info |= GI_MINUSMINUS;
|
|
return (-1);
|
|
}
|
|
if (arg == NULL ||
|
|
((flag != '-' ) && /* neither a - nor a + (if + allowed) */
|
|
(!(go->flags & GF_PLUSOPT) || flag != '+')) ||
|
|
(c = arg[1]) == '\0') {
|
|
go->p = 0;
|
|
return (-1);
|
|
}
|
|
go->optind++;
|
|
go->info &= ~(GI_MINUS|GI_PLUS);
|
|
go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
|
|
}
|
|
go->p++;
|
|
if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
|
|
!(o = cstrchr(optionsp, c))) {
|
|
if (optionsp[0] == ':') {
|
|
go->buf[0] = c;
|
|
go->optarg = go->buf;
|
|
} else {
|
|
warningf(true, "%s%s-%c: unknown option",
|
|
(go->flags & GF_NONAME) ? "" : argv[0],
|
|
(go->flags & GF_NONAME) ? "" : ": ", c);
|
|
if (go->flags & GF_ERROR)
|
|
bi_errorfz();
|
|
}
|
|
return ('?');
|
|
}
|
|
/* : means argument must be present, may be part of option argument
|
|
* or the next argument
|
|
* ; same as : but argument may be missing
|
|
* , means argument is part of option argument, and may be null.
|
|
*/
|
|
if (*++o == ':' || *o == ';') {
|
|
if (argv[go->optind - 1][go->p])
|
|
go->optarg = argv[go->optind - 1] + go->p;
|
|
else if (argv[go->optind])
|
|
go->optarg = argv[go->optind++];
|
|
else if (*o == ';')
|
|
go->optarg = NULL;
|
|
else {
|
|
if (optionsp[0] == ':') {
|
|
go->buf[0] = c;
|
|
go->optarg = go->buf;
|
|
return (':');
|
|
}
|
|
warningf(true, "%s%s-'%c' requires argument",
|
|
(go->flags & GF_NONAME) ? "" : argv[0],
|
|
(go->flags & GF_NONAME) ? "" : ": ", c);
|
|
if (go->flags & GF_ERROR)
|
|
bi_errorfz();
|
|
return ('?');
|
|
}
|
|
go->p = 0;
|
|
} else if (*o == ',') {
|
|
/* argument is attached to option character, even if null */
|
|
go->optarg = argv[go->optind - 1] + go->p;
|
|
go->p = 0;
|
|
} else if (*o == '#') {
|
|
/* argument is optional and may be attached or unattached
|
|
* but must start with a digit. optarg is set to 0 if the
|
|
* argument is missing.
|
|
*/
|
|
if (argv[go->optind - 1][go->p]) {
|
|
if (ksh_isdigit(argv[go->optind - 1][go->p])) {
|
|
go->optarg = argv[go->optind - 1] + go->p;
|
|
go->p = 0;
|
|
} else
|
|
go->optarg = NULL;
|
|
} else {
|
|
if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) {
|
|
go->optarg = argv[go->optind++];
|
|
go->p = 0;
|
|
} else
|
|
go->optarg = NULL;
|
|
}
|
|
}
|
|
return (c);
|
|
}
|
|
|
|
/* print variable/alias value using necessary quotes
|
|
* (POSIX says they should be suitable for re-entry...)
|
|
* No trailing newline is printed.
|
|
*/
|
|
void
|
|
print_value_quoted(const char *s)
|
|
{
|
|
const char *p;
|
|
int inquote = 0;
|
|
|
|
/* Test if any quotes are needed */
|
|
for (p = s; *p; p++)
|
|
if (ctype(*p, C_QUOTE))
|
|
break;
|
|
if (!*p) {
|
|
shf_puts(s, shl_stdout);
|
|
return;
|
|
}
|
|
for (p = s; *p; p++) {
|
|
if (*p == '\'') {
|
|
if (inquote)
|
|
shf_putc('\'', shl_stdout);
|
|
shf_putc('\\', shl_stdout);
|
|
inquote = 0;
|
|
} else if (!inquote) {
|
|
shf_putc('\'', shl_stdout);
|
|
inquote = 1;
|
|
}
|
|
shf_putc(*p, shl_stdout);
|
|
}
|
|
if (inquote)
|
|
shf_putc('\'', shl_stdout);
|
|
}
|
|
|
|
/* Print things in columns and rows - func() is called to format the ith
|
|
* element
|
|
*/
|
|
void
|
|
print_columns(struct shf *shf, int n,
|
|
char *(*func) (const void *, int, char *, int),
|
|
const void *arg, int max_width, int prefcol)
|
|
{
|
|
char *str = alloc(max_width + 1, ATEMP);
|
|
int i, r, c, rows, cols, nspace;
|
|
|
|
/* ensure x_cols is valid first */
|
|
if (x_cols < MIN_COLS)
|
|
change_winsz();
|
|
|
|
/* max_width + 1 for the space. Note that no space
|
|
* is printed after the last column to avoid problems
|
|
* with terminals that have auto-wrap.
|
|
*/
|
|
cols = x_cols / (max_width + 1);
|
|
/* if we can only print one column anyway, skip the goo */
|
|
if (cols < 2) {
|
|
for (i = 0; i < n; ++i)
|
|
shf_fprintf(shf, "%s \n",
|
|
(*func)(arg, i, str, max_width + 1));
|
|
goto out;
|
|
}
|
|
rows = (n + cols - 1) / cols;
|
|
if (prefcol && n && cols > rows) {
|
|
int tmp = rows;
|
|
|
|
rows = cols;
|
|
cols = tmp;
|
|
if (rows > n)
|
|
rows = n;
|
|
}
|
|
|
|
nspace = (x_cols - max_width * cols) / cols;
|
|
if (nspace <= 0)
|
|
nspace = 1;
|
|
for (r = 0; r < rows; r++) {
|
|
for (c = 0; c < cols; c++) {
|
|
i = c * rows + r;
|
|
if (i < n) {
|
|
shf_fprintf(shf, "%-*s",
|
|
max_width,
|
|
(*func)(arg, i, str, max_width + 1));
|
|
if (c + 1 < cols)
|
|
shf_fprintf(shf, "%*s", nspace, null);
|
|
}
|
|
}
|
|
shf_putchar('\n', shf);
|
|
}
|
|
out:
|
|
afree(str, ATEMP);
|
|
}
|
|
|
|
/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
|
|
void
|
|
strip_nuls(char *buf, int nbytes)
|
|
{
|
|
char *dst;
|
|
|
|
/* nbytes check because some systems (older FreeBSDs) have a buggy
|
|
* memchr()
|
|
*/
|
|
if (nbytes && (dst = memchr(buf, '\0', nbytes))) {
|
|
char *end = buf + nbytes;
|
|
char *p, *q;
|
|
|
|
for (p = dst; p < end; p = q) {
|
|
/* skip a block of nulls */
|
|
while (++p < end && *p == '\0')
|
|
;
|
|
/* find end of non-null block */
|
|
if (!(q = memchr(p, '\0', end - p)))
|
|
q = end;
|
|
memmove(dst, p, q - p);
|
|
dst += q - p;
|
|
}
|
|
*dst = '\0';
|
|
}
|
|
}
|
|
|
|
/* Like read(2), but if read fails due to non-blocking flag, resets flag
|
|
* and restarts read.
|
|
*/
|
|
int
|
|
blocking_read(int fd, char *buf, int nbytes)
|
|
{
|
|
int ret;
|
|
int tried_reset = 0;
|
|
|
|
while ((ret = read(fd, buf, nbytes)) < 0) {
|
|
if (!tried_reset && errno == EAGAIN) {
|
|
if (reset_nonblock(fd) > 0) {
|
|
tried_reset = 1;
|
|
continue;
|
|
}
|
|
errno = EAGAIN;
|
|
}
|
|
break;
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
/* Reset the non-blocking flag on the specified file descriptor.
|
|
* Returns -1 if there was an error, 0 if non-blocking wasn't set,
|
|
* 1 if it was.
|
|
*/
|
|
int
|
|
reset_nonblock(int fd)
|
|
{
|
|
int flags;
|
|
|
|
if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
|
|
return (-1);
|
|
if (!(flags & O_NONBLOCK))
|
|
return (0);
|
|
flags &= ~O_NONBLOCK;
|
|
if (fcntl(fd, F_SETFL, flags) < 0)
|
|
return (-1);
|
|
return (1);
|
|
}
|
|
|
|
|
|
/* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */
|
|
char *
|
|
ksh_get_wd(size_t *dlen)
|
|
{
|
|
char *ret, *b;
|
|
size_t len = 1;
|
|
|
|
if ((ret = getcwd((b = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)))
|
|
ret = aresize(b, len = (strlen(b) + 1), ATEMP);
|
|
else
|
|
afree(b, ATEMP);
|
|
|
|
if (dlen)
|
|
*dlen = len;
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Makes a filename into result using the following algorithm.
|
|
* - make result NULL
|
|
* - if file starts with '/', append file to result & set cdpathp to NULL
|
|
* - if file starts with ./ or ../ append cwd and file to result
|
|
* and set cdpathp to NULL
|
|
* - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
|
|
* then cwd is appended to result.
|
|
* - the first element of cdpathp is appended to result
|
|
* - file is appended to result
|
|
* - cdpathp is set to the start of the next element in cdpathp (or NULL
|
|
* if there are no more elements.
|
|
* The return value indicates whether a non-null element from cdpathp
|
|
* was appended to result.
|
|
*/
|
|
int
|
|
make_path(const char *cwd, const char *file,
|
|
char **cdpathp, /* & of : separated list */
|
|
XString *xsp,
|
|
int *phys_pathp)
|
|
{
|
|
int rval = 0;
|
|
bool use_cdpath = true;
|
|
char *plist;
|
|
int len, plen = 0;
|
|
char *xp = Xstring(*xsp, xp);
|
|
|
|
if (!file)
|
|
file = null;
|
|
|
|
if (file[0] == '/') {
|
|
*phys_pathp = 0;
|
|
use_cdpath = false;
|
|
} else {
|
|
if (file[0] == '.') {
|
|
char c = file[1];
|
|
|
|
if (c == '.')
|
|
c = file[2];
|
|
if (c == '/' || c == '\0')
|
|
use_cdpath = false;
|
|
}
|
|
|
|
plist = *cdpathp;
|
|
if (!plist)
|
|
use_cdpath = false;
|
|
else if (use_cdpath) {
|
|
char *pend;
|
|
|
|
for (pend = plist; *pend && *pend != ':'; pend++)
|
|
;
|
|
plen = pend - plist;
|
|
*cdpathp = *pend ? pend + 1 : NULL;
|
|
}
|
|
|
|
if ((!use_cdpath || !plen || plist[0] != '/') &&
|
|
(cwd && *cwd)) {
|
|
len = strlen(cwd);
|
|
XcheckN(*xsp, xp, len);
|
|
memcpy(xp, cwd, len);
|
|
xp += len;
|
|
if (cwd[len - 1] != '/')
|
|
Xput(*xsp, xp, '/');
|
|
}
|
|
*phys_pathp = Xlength(*xsp, xp);
|
|
if (use_cdpath && plen) {
|
|
XcheckN(*xsp, xp, plen);
|
|
memcpy(xp, plist, plen);
|
|
xp += plen;
|
|
if (plist[plen - 1] != '/')
|
|
Xput(*xsp, xp, '/');
|
|
rval = 1;
|
|
}
|
|
}
|
|
|
|
len = strlen(file) + 1;
|
|
XcheckN(*xsp, xp, len);
|
|
memcpy(xp, file, len);
|
|
|
|
if (!use_cdpath)
|
|
*cdpathp = NULL;
|
|
|
|
return (rval);
|
|
}
|
|
|
|
/*
|
|
* Simplify pathnames containing "." and ".." entries.
|
|
* ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
|
|
*/
|
|
void
|
|
simplify_path(char *pathl)
|
|
{
|
|
char *cur, *t;
|
|
bool isrooted;
|
|
char *very_start = pathl, *start;
|
|
|
|
if (!*pathl)
|
|
return;
|
|
|
|
if ((isrooted = pathl[0] == '/'))
|
|
very_start++;
|
|
|
|
/* Before After
|
|
* /foo/ /foo
|
|
* /foo/../../bar /bar
|
|
* /foo/./blah/.. /foo
|
|
* . .
|
|
* .. ..
|
|
* ./foo foo
|
|
* foo/../../../bar ../../bar
|
|
*/
|
|
|
|
for (cur = t = start = very_start; ; ) {
|
|
/* treat multiple '/'s as one '/' */
|
|
while (*t == '/')
|
|
t++;
|
|
|
|
if (*t == '\0') {
|
|
if (cur == pathl)
|
|
/* convert empty path to dot */
|
|
*cur++ = '.';
|
|
*cur = '\0';
|
|
break;
|
|
}
|
|
|
|
if (t[0] == '.') {
|
|
if (!t[1] || t[1] == '/') {
|
|
t += 1;
|
|
continue;
|
|
} else if (t[1] == '.' && (!t[2] || t[2] == '/')) {
|
|
if (!isrooted && cur == start) {
|
|
if (cur != very_start)
|
|
*cur++ = '/';
|
|
*cur++ = '.';
|
|
*cur++ = '.';
|
|
start = cur;
|
|
} else if (cur != start)
|
|
while (--cur > start && *cur != '/')
|
|
;
|
|
t += 2;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (cur != very_start)
|
|
*cur++ = '/';
|
|
|
|
/* find/copy next component of pathname */
|
|
while (*t && *t != '/')
|
|
*cur++ = *t++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
set_current_wd(char *pathl)
|
|
{
|
|
size_t len = 1;
|
|
char *p = pathl;
|
|
|
|
if (p == NULL) {
|
|
if ((p = ksh_get_wd(&len)) == NULL)
|
|
p = null;
|
|
} else
|
|
len = strlen(p) + 1;
|
|
|
|
if (len > current_wd_size) {
|
|
afree(current_wd, APERM);
|
|
current_wd = alloc(current_wd_size = len, APERM);
|
|
}
|
|
memcpy(current_wd, p, len);
|
|
if (p != pathl && p != null)
|
|
afree(p, ATEMP);
|
|
}
|
|
|
|
char *
|
|
get_phys_path(const char *pathl)
|
|
{
|
|
XString xs;
|
|
char *xp;
|
|
|
|
Xinit(xs, xp, strlen(pathl) + 1, ATEMP);
|
|
|
|
xp = do_phys_path(&xs, xp, pathl);
|
|
|
|
if (!xp)
|
|
return (NULL);
|
|
|
|
if (Xlength(xs, xp) == 0)
|
|
Xput(xs, xp, '/');
|
|
Xput(xs, xp, '\0');
|
|
|
|
return (Xclose(xs, xp));
|
|
}
|
|
|
|
static char *
|
|
do_phys_path(XString *xsp, char *xp, const char *pathl)
|
|
{
|
|
const char *p, *q;
|
|
int len, llen, savepos;
|
|
char *lbuf;
|
|
|
|
lbuf = alloc(PATH_MAX, ATEMP);
|
|
Xcheck(*xsp, xp);
|
|
for (p = pathl; p; p = q) {
|
|
while (*p == '/')
|
|
p++;
|
|
if (!*p)
|
|
break;
|
|
len = (q = cstrchr(p, '/')) ? q - p : (int)strlen(p);
|
|
if (len == 1 && p[0] == '.')
|
|
continue;
|
|
if (len == 2 && p[0] == '.' && p[1] == '.') {
|
|
while (xp > Xstring(*xsp, xp)) {
|
|
xp--;
|
|
if (*xp == '/')
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
savepos = Xsavepos(*xsp, xp);
|
|
Xput(*xsp, xp, '/');
|
|
XcheckN(*xsp, xp, len + 1);
|
|
memcpy(xp, p, len);
|
|
xp += len;
|
|
*xp = '\0';
|
|
|
|
llen = readlink(Xstring(*xsp, xp), lbuf, PATH_MAX - 1);
|
|
if (llen < 0) {
|
|
if (errno == EINVAL)
|
|
/* not a symbolic link */
|
|
continue;
|
|
xp = NULL;
|
|
goto out;
|
|
}
|
|
lbuf[llen] = '\0';
|
|
|
|
/* If absolute path, start from scratch.. */
|
|
xp = lbuf[0] == '/' ? Xstring(*xsp, xp) :
|
|
Xrestpos(*xsp, xp, savepos);
|
|
if ((xp = do_phys_path(xsp, xp, lbuf)) == NULL)
|
|
break;
|
|
}
|
|
out:
|
|
afree(lbuf, ATEMP);
|
|
return (xp);
|
|
}
|
|
|
|
#ifdef USE_CHVT
|
|
static void
|
|
chvt(const char *fn)
|
|
{
|
|
char dv[20];
|
|
struct stat sb;
|
|
int fd;
|
|
|
|
if (*fn == '-') {
|
|
memcpy(dv, "-/dev/null", sizeof("-/dev/null"));
|
|
fn = dv + 1;
|
|
} else {
|
|
if (stat(fn, &sb)) {
|
|
memcpy(dv, "/dev/ttyC", 9);
|
|
strlcpy(dv + 9, fn, 20 - 9);
|
|
if (stat(dv, &sb)) {
|
|
strlcpy(dv + 8, fn, 20 - 8);
|
|
if (stat(dv, &sb))
|
|
errorf("chvt: can't find tty %s", fn);
|
|
}
|
|
fn = dv;
|
|
}
|
|
if (!(sb.st_mode & S_IFCHR))
|
|
errorf("chvt: not a char device: %s", fn);
|
|
if ((sb.st_uid != 0) && chown(fn, 0, 0))
|
|
warningf(false, "chvt: cannot chown root %s", fn);
|
|
if (((sb.st_mode & 07777) != 0600) && chmod(fn, 0600))
|
|
warningf(false, "chvt: cannot chmod 0600 %s", fn);
|
|
#if HAVE_REVOKE
|
|
if (revoke(fn))
|
|
#endif
|
|
warningf(false, "chvt: cannot revoke %s, new shell is"
|
|
" potentially insecure", fn);
|
|
}
|
|
if ((fd = open(fn, O_RDWR)) == -1) {
|
|
sleep(1);
|
|
if ((fd = open(fn, O_RDWR)) == -1)
|
|
errorf("chvt: cannot open %s", fn);
|
|
}
|
|
switch (fork()) {
|
|
case -1:
|
|
errorf("chvt: %s failed", "fork");
|
|
case 0:
|
|
break;
|
|
default:
|
|
exit(0);
|
|
}
|
|
if (setsid() == -1)
|
|
errorf("chvt: %s failed", "setsid");
|
|
if (fn != dv + 1) {
|
|
if (ioctl(fd, TIOCSCTTY, NULL) == -1)
|
|
errorf("chvt: %s failed", "TIOCSCTTY");
|
|
if (tcflush(fd, TCIOFLUSH))
|
|
errorf("chvt: %s failed", "TCIOFLUSH");
|
|
}
|
|
ksh_dup2(fd, 0, false);
|
|
ksh_dup2(fd, 1, false);
|
|
ksh_dup2(fd, 2, false);
|
|
if (fd > 2)
|
|
close(fd);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
char longsizes_are_okay[sizeof(long) == sizeof(unsigned long) ? 1 : -1];
|
|
char arisize_is_okay[sizeof(mksh_ari_t) == 4 ? 1 : -1];
|
|
char uarisize_is_okay[sizeof(mksh_uari_t) == 4 ? 1 : -1];
|
|
|
|
char *
|
|
strchr(char *p, int ch)
|
|
{
|
|
for (;; ++p) {
|
|
if (*p == ch)
|
|
return (p);
|
|
if (!*p)
|
|
return (NULL);
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
char *
|
|
strstr(char *b, const char *l)
|
|
{
|
|
char first, c;
|
|
size_t n;
|
|
|
|
if ((first = *l++) == '\0')
|
|
return (b);
|
|
n = strlen(l);
|
|
strstr_look:
|
|
while ((c = *b++) != first)
|
|
if (c == '\0')
|
|
return (NULL);
|
|
if (strncmp(b, l, n))
|
|
goto strstr_look;
|
|
return (b - 1);
|
|
}
|
|
#endif
|
|
|
|
#ifndef MKSH_ASSUME_UTF8
|
|
#if !HAVE_STRCASESTR
|
|
const char *
|
|
stristr(const char *b, const char *l)
|
|
{
|
|
char first, c;
|
|
size_t n;
|
|
|
|
if ((first = *l++), ((first = ksh_tolower(first)) == '\0'))
|
|
return (b);
|
|
n = strlen(l);
|
|
stristr_look:
|
|
while ((c = *b++), ((c = ksh_tolower(c)) != first))
|
|
if (c == '\0')
|
|
return (NULL);
|
|
if (strncasecmp(b, l, n))
|
|
goto stristr_look;
|
|
return (b - 1);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MKSH_SMALL
|
|
char *
|
|
strndup_(const char *src, size_t len, Area *ap)
|
|
{
|
|
char *dst = NULL;
|
|
|
|
if (src != NULL) {
|
|
dst = alloc(++len, ap);
|
|
strlcpy(dst, src, len);
|
|
}
|
|
return (dst);
|
|
}
|
|
|
|
char *
|
|
strdup_(const char *src, Area *ap)
|
|
{
|
|
return (src == NULL ? NULL : strndup_(src, strlen(src), ap));
|
|
}
|
|
#endif
|
|
|
|
#if !HAVE_GETRUSAGE
|
|
#define INVTCK(r,t) do { \
|
|
r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \
|
|
r.tv_sec = (t) / CLK_TCK; \
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
int
|
|
getrusage(int what, struct rusage *ru)
|
|
{
|
|
struct tms tms;
|
|
clock_t u, s;
|
|
|
|
if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
|
|
return (-1);
|
|
|
|
switch (what) {
|
|
case RUSAGE_SELF:
|
|
u = tms.tms_utime;
|
|
s = tms.tms_stime;
|
|
break;
|
|
case RUSAGE_CHILDREN:
|
|
u = tms.tms_cutime;
|
|
s = tms.tms_cstime;
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
INVTCK(ru->ru_utime, u);
|
|
INVTCK(ru->ru_stime, s);
|
|
return (0);
|
|
}
|
|
#endif
|