2009-03-22 18:53:50 +01:00
|
|
|
/* $OpenBSD: c_ksh.c,v 1.33 2009/02/07 14:03:24 kili Exp $ */
|
2010-04-09 20:59:30 +02:00
|
|
|
/* $OpenBSD: c_sh.c,v 1.41 2010/03/27 09:10:01 jmc Exp $ */
|
2009-03-22 18:58:58 +01:00
|
|
|
/* $OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $ */
|
2008-04-16 23:56:03 +02:00
|
|
|
/* $OpenBSD: c_ulimit.c,v 1.17 2008/03/21 12:51:19 millert Exp $ */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-05-16 18:59:42 +02:00
|
|
|
/*-
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
2009-05-16 18:59:42 +02:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
#include "sh.h"
|
|
|
|
|
2010-11-01 18:29:05 +01:00
|
|
|
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.164 2010/11/01 17:29:02 tg Exp $");
|
2009-06-08 22:34:40 +02:00
|
|
|
|
|
|
|
#if HAVE_KILLPG
|
|
|
|
/*
|
|
|
|
* use killpg if < -1 since -1 does special things
|
|
|
|
* for some non-killpg-endowed kills
|
|
|
|
*/
|
|
|
|
#define mksh_kill(p,s) ((p) < -1 ? killpg(-(p), (s)) : kill((p), (s)))
|
|
|
|
#else
|
|
|
|
/* cross fingers and hope kill is killpg-endowed */
|
|
|
|
#define mksh_kill kill
|
|
|
|
#endif
|
2007-08-20 00:06:27 +02:00
|
|
|
|
2009-07-25 23:31:27 +02:00
|
|
|
/* XXX conditions correct? */
|
2009-07-25 22:35:22 +02:00
|
|
|
#if !defined(RLIM_INFINITY) && !defined(MKSH_NO_LIMITS)
|
2009-06-08 22:52:28 +02:00
|
|
|
#define MKSH_NO_LIMITS
|
|
|
|
#endif
|
|
|
|
|
2009-06-10 20:11:27 +02:00
|
|
|
#ifdef MKSH_NO_LIMITS
|
|
|
|
#define c_ulimit c_label
|
|
|
|
#endif
|
|
|
|
|
2009-09-06 19:42:15 +02:00
|
|
|
extern uint8_t set_refflag;
|
|
|
|
|
2007-08-20 00:06:27 +02:00
|
|
|
/* A leading = means assignments before command are kept;
|
|
|
|
* a leading * means a POSIX special builtin;
|
|
|
|
* a leading + means a POSIX regular builtin
|
|
|
|
* (* and + should not be combined).
|
|
|
|
*/
|
|
|
|
const struct builtin mkshbuiltins[] = {
|
|
|
|
{"*=.", c_dot},
|
|
|
|
{"*=:", c_label},
|
|
|
|
{"[", c_test},
|
|
|
|
{"*=break", c_brkcont},
|
2010-08-28 20:50:58 +02:00
|
|
|
{T_gbuiltin, c_builtin},
|
2007-08-20 00:06:27 +02:00
|
|
|
{"*=continue", c_brkcont},
|
|
|
|
{"*=eval", c_eval},
|
|
|
|
{"*=exec", c_exec},
|
|
|
|
{"*=exit", c_exitreturn},
|
|
|
|
{"+false", c_label},
|
|
|
|
{"*=return", c_exitreturn},
|
2010-08-28 20:50:58 +02:00
|
|
|
{T_sgset, c_set},
|
2007-08-20 00:06:27 +02:00
|
|
|
{"*=shift", c_shift},
|
|
|
|
{"=times", c_times},
|
|
|
|
{"*=trap", c_trap},
|
|
|
|
{"+=wait", c_wait},
|
|
|
|
{"+read", c_read},
|
|
|
|
{"test", c_test},
|
|
|
|
{"+true", c_label},
|
|
|
|
{"ulimit", c_ulimit},
|
|
|
|
{"+umask", c_umask},
|
|
|
|
{"*=unset", c_unset},
|
2010-08-28 20:50:58 +02:00
|
|
|
{T_palias, c_alias}, /* no =: AT&T manual wrong */
|
2007-08-20 00:06:27 +02:00
|
|
|
{"+cd", c_cd},
|
2009-08-30 23:02:01 +02:00
|
|
|
{"chdir", c_cd}, /* dash compatibility hack */
|
2007-08-20 00:06:27 +02:00
|
|
|
{"+command", c_command},
|
|
|
|
{"echo", c_print},
|
|
|
|
{"*=export", c_typeset},
|
|
|
|
{"+fc", c_fc},
|
|
|
|
{"+getopts", c_getopts},
|
|
|
|
{"+jobs", c_jobs},
|
|
|
|
{"+kill", c_kill},
|
|
|
|
{"let", c_let},
|
|
|
|
{"print", c_print},
|
2009-07-30 21:11:12 +02:00
|
|
|
#ifdef MKSH_PRINTF_BUILTIN
|
|
|
|
{"printf", c_printf},
|
|
|
|
#endif
|
2007-08-20 00:06:27 +02:00
|
|
|
{"pwd", c_pwd},
|
|
|
|
{"*=readonly", c_typeset},
|
2009-09-26 05:40:03 +02:00
|
|
|
{T__typeset, c_typeset},
|
2010-08-28 20:50:58 +02:00
|
|
|
{T_punalias, c_unalias},
|
2007-08-20 00:06:27 +02:00
|
|
|
{"whence", c_whence},
|
2009-04-05 14:35:32 +02:00
|
|
|
#ifndef MKSH_UNEMPLOYED
|
2007-08-20 00:06:27 +02:00
|
|
|
{"+bg", c_fgbg},
|
|
|
|
{"+fg", c_fgbg},
|
2009-04-05 14:35:32 +02:00
|
|
|
#endif
|
2007-08-20 00:06:27 +02:00
|
|
|
{"bind", c_bind},
|
2010-08-28 19:21:46 +02:00
|
|
|
{"cat", c_cat},
|
2007-08-20 00:06:27 +02:00
|
|
|
#if HAVE_MKNOD
|
|
|
|
{"mknod", c_mknod},
|
2008-05-17 20:27:57 +02:00
|
|
|
#endif
|
|
|
|
{"realpath", c_realpath},
|
2007-08-20 00:06:27 +02:00
|
|
|
{"rename", c_rename},
|
|
|
|
{NULL, (int (*)(const char **))NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct kill_info {
|
|
|
|
int num_width;
|
|
|
|
int name_width;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct t_op {
|
2009-04-07 21:51:47 +02:00
|
|
|
char op_text[4];
|
|
|
|
Test_op op_num;
|
2007-08-20 00:06:27 +02:00
|
|
|
} u_ops[] = {
|
|
|
|
{"-a", TO_FILAXST },
|
|
|
|
{"-b", TO_FILBDEV },
|
|
|
|
{"-c", TO_FILCDEV },
|
|
|
|
{"-d", TO_FILID },
|
|
|
|
{"-e", TO_FILEXST },
|
|
|
|
{"-f", TO_FILREG },
|
|
|
|
{"-G", TO_FILGID },
|
|
|
|
{"-g", TO_FILSETG },
|
|
|
|
{"-h", TO_FILSYM },
|
|
|
|
{"-H", TO_FILCDF },
|
|
|
|
{"-k", TO_FILSTCK },
|
|
|
|
{"-L", TO_FILSYM },
|
|
|
|
{"-n", TO_STNZE },
|
|
|
|
{"-O", TO_FILUID },
|
|
|
|
{"-o", TO_OPTION },
|
|
|
|
{"-p", TO_FILFIFO },
|
|
|
|
{"-r", TO_FILRD },
|
|
|
|
{"-s", TO_FILGZ },
|
|
|
|
{"-S", TO_FILSOCK },
|
|
|
|
{"-t", TO_FILTT },
|
|
|
|
{"-u", TO_FILSETU },
|
|
|
|
{"-w", TO_FILWR },
|
|
|
|
{"-x", TO_FILEX },
|
|
|
|
{"-z", TO_STZER },
|
|
|
|
{"", TO_NONOP }
|
|
|
|
};
|
|
|
|
static const struct t_op b_ops[] = {
|
|
|
|
{"=", TO_STEQL },
|
|
|
|
{"==", TO_STEQL },
|
|
|
|
{"!=", TO_STNEQ },
|
|
|
|
{"<", TO_STLT },
|
|
|
|
{">", TO_STGT },
|
|
|
|
{"-eq", TO_INTEQ },
|
|
|
|
{"-ne", TO_INTNE },
|
|
|
|
{"-gt", TO_INTGT },
|
|
|
|
{"-ge", TO_INTGE },
|
|
|
|
{"-lt", TO_INTLT },
|
|
|
|
{"-le", TO_INTLE },
|
|
|
|
{"-ef", TO_FILEQ },
|
|
|
|
{"-nt", TO_FILNT },
|
|
|
|
{"-ot", TO_FILOT },
|
|
|
|
{"", TO_NONOP }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int test_eaccess(const char *, int);
|
2008-04-02 00:20:20 +02:00
|
|
|
static int test_oexpr(Test_env *, bool);
|
|
|
|
static int test_aexpr(Test_env *, bool);
|
|
|
|
static int test_nexpr(Test_env *, bool);
|
|
|
|
static int test_primary(Test_env *, bool);
|
2010-07-18 00:09:40 +02:00
|
|
|
static Test_op ptest_isa(Test_env *, Test_meta);
|
2008-04-02 00:20:20 +02:00
|
|
|
static const char *ptest_getopnd(Test_env *, Test_op, bool);
|
2007-08-20 00:06:27 +02:00
|
|
|
static void ptest_error(Test_env *, int, const char *);
|
2009-11-10 00:35:11 +01:00
|
|
|
static char *kill_fmt_entry(char *, int, int, const void *);
|
2009-05-20 12:10:02 +02:00
|
|
|
static void p_time(struct shf *, bool, long, int, int,
|
2009-12-12 23:27:10 +01:00
|
|
|
const char *, const char *)
|
|
|
|
MKSH_A_NONNULL((nonnull (6, 7)));
|
2009-08-30 15:22:39 +02:00
|
|
|
static char *do_realpath(const char *);
|
|
|
|
|
|
|
|
static char *
|
|
|
|
do_realpath(const char *upath)
|
|
|
|
{
|
|
|
|
char *xp, *ip, *tp, *ipath, *ldest = NULL;
|
|
|
|
XString xs;
|
|
|
|
ptrdiff_t pos;
|
|
|
|
size_t len;
|
|
|
|
int symlinks = 32; /* max. recursion depth */
|
|
|
|
int llen;
|
|
|
|
struct stat sb;
|
2010-04-27 23:39:09 +02:00
|
|
|
#ifdef NO_PATH_MAX
|
|
|
|
size_t ldestlen = 0;
|
|
|
|
#define pathlen sb.st_size
|
|
|
|
#define pathcnd (ldestlen < (pathlen + 1))
|
|
|
|
#else
|
|
|
|
#define pathlen PATH_MAX
|
|
|
|
#define pathcnd (!ldest)
|
|
|
|
#endif
|
2009-08-30 15:22:39 +02:00
|
|
|
|
|
|
|
if (upath[0] == '/') {
|
|
|
|
/* upath is an absolute pathname */
|
|
|
|
strdupx(ipath, upath, ATEMP);
|
|
|
|
} else {
|
|
|
|
/* upath is a relative pathname, prepend cwd */
|
|
|
|
if ((tp = ksh_get_wd(NULL)) == NULL || tp[0] != '/')
|
|
|
|
return (NULL);
|
2010-08-28 22:22:24 +02:00
|
|
|
ipath = shf_smprintf("%s%s%s", tp, "/", upath);
|
2009-08-30 15:22:39 +02:00
|
|
|
afree(tp, ATEMP);
|
|
|
|
}
|
|
|
|
|
2010-09-14 23:26:19 +02:00
|
|
|
/* ipath and upath are in memory at the same time -> unchecked */
|
2009-08-30 15:22:39 +02:00
|
|
|
Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
|
|
|
|
|
|
|
|
while (*ip) {
|
|
|
|
/* skip slashes in input */
|
|
|
|
while (*ip == '/')
|
|
|
|
++ip;
|
|
|
|
if (!*ip)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* get next pathname component from input */
|
|
|
|
tp = ip;
|
|
|
|
while (*ip && *ip != '/')
|
|
|
|
++ip;
|
|
|
|
len = ip - tp;
|
|
|
|
|
|
|
|
/* check input for "." and ".." */
|
|
|
|
if (tp[0] == '.') {
|
|
|
|
if (len == 1)
|
|
|
|
/* just continue with the next one */
|
|
|
|
continue;
|
|
|
|
else if (len == 2 && tp[1] == '.') {
|
|
|
|
/* strip off last pathname component */
|
|
|
|
while (xp > Xstring(xs, xp))
|
|
|
|
if (*--xp == '/')
|
|
|
|
break;
|
|
|
|
/* then continue with the next one */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* store output position away, then append slash to output */
|
|
|
|
pos = Xsavepos(xs, xp);
|
2010-04-27 23:39:09 +02:00
|
|
|
/* 1 for the '/' and len + 1 for tp and the NUL from below */
|
|
|
|
XcheckN(xs, xp, 1 + len + 1);
|
2009-08-30 15:22:39 +02:00
|
|
|
Xput(xs, xp, '/');
|
|
|
|
|
|
|
|
/* append next pathname component to output */
|
|
|
|
memcpy(xp, tp, len);
|
|
|
|
xp += len;
|
|
|
|
*xp = '\0';
|
|
|
|
|
|
|
|
/* lstat the current output, see if it's a symlink */
|
|
|
|
if (lstat(Xstring(xs, xp), &sb)) {
|
|
|
|
/* lstat failed */
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
/* because the pathname does not exist */
|
|
|
|
while (*ip == '/')
|
|
|
|
/* skip any trailing slashes */
|
|
|
|
++ip;
|
|
|
|
/* no more components left? */
|
|
|
|
if (!*ip)
|
|
|
|
/* we can still return successfully */
|
|
|
|
break;
|
|
|
|
/* more components left? fall through */
|
|
|
|
}
|
|
|
|
/* not ENOENT or not at the end of ipath */
|
|
|
|
goto notfound;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if we encountered a symlink? */
|
|
|
|
if (S_ISLNK(sb.st_mode)) {
|
|
|
|
/* reached maximum recursion depth? */
|
|
|
|
if (!symlinks--) {
|
|
|
|
/* yep, prevent infinite loops */
|
|
|
|
errno = ELOOP;
|
|
|
|
goto notfound;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get symlink(7) target */
|
2010-09-14 23:26:19 +02:00
|
|
|
if (pathcnd) {
|
|
|
|
#ifdef NO_PATH_MAX
|
|
|
|
if (notoktoadd(pathlen, 1)) {
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
goto notfound;
|
|
|
|
}
|
|
|
|
#endif
|
2010-04-27 23:39:09 +02:00
|
|
|
ldest = aresize(ldest, pathlen + 1, ATEMP);
|
2010-09-14 23:26:19 +02:00
|
|
|
}
|
2010-04-27 23:39:09 +02:00
|
|
|
llen = readlink(Xstring(xs, xp), ldest, pathlen);
|
|
|
|
if (llen < 0)
|
2009-08-30 15:22:39 +02:00
|
|
|
/* oops... */
|
|
|
|
goto notfound;
|
|
|
|
ldest[llen] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* restart if symlink target is an absolute path,
|
|
|
|
* otherwise continue with currently resolved prefix
|
|
|
|
*/
|
|
|
|
xp = (ldest[0] == '/') ? Xstring(xs, xp) :
|
|
|
|
Xrestpos(xs, xp, pos);
|
2010-08-24 16:42:04 +02:00
|
|
|
tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip);
|
2009-08-30 15:22:39 +02:00
|
|
|
afree(ipath, ATEMP);
|
|
|
|
ip = ipath = tp;
|
|
|
|
}
|
|
|
|
/* otherwise (no symlink) merely go on */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* either found the target and successfully resolved it,
|
|
|
|
* or found its parent directory and may create it
|
|
|
|
*/
|
|
|
|
if (Xlength(xs, xp) == 0)
|
|
|
|
/*
|
|
|
|
* if the resolved pathname is "", make it "/",
|
|
|
|
* otherwise do not add a trailing slash
|
|
|
|
*/
|
|
|
|
Xput(xs, xp, '/');
|
|
|
|
Xput(xs, xp, '\0');
|
|
|
|
|
2010-08-24 16:42:04 +02:00
|
|
|
/*
|
|
|
|
* if source path had a trailing slash, check if target path
|
|
|
|
* is not a non-directory existing file
|
|
|
|
*/
|
|
|
|
if (ip > ipath && ip[-1] == '/') {
|
|
|
|
if (stat(Xstring(xs, xp), &sb)) {
|
|
|
|
if (errno != ENOENT)
|
|
|
|
goto notfound;
|
|
|
|
} else if (!S_ISDIR(sb.st_mode)) {
|
|
|
|
errno = ENOTDIR;
|
|
|
|
goto notfound;
|
|
|
|
}
|
|
|
|
/* target now either does not exist or is a directory */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return target path */
|
2009-08-30 15:22:39 +02:00
|
|
|
if (ldest != NULL)
|
|
|
|
afree(ldest, ATEMP);
|
|
|
|
afree(ipath, ATEMP);
|
|
|
|
return (Xclose(xs, xp));
|
|
|
|
|
|
|
|
notfound:
|
|
|
|
llen = errno; /* save; free(3) might trash it */
|
|
|
|
if (ldest != NULL)
|
|
|
|
afree(ldest, ATEMP);
|
|
|
|
afree(ipath, ATEMP);
|
|
|
|
Xfree(xs, xp);
|
|
|
|
errno = llen;
|
|
|
|
return (NULL);
|
2010-04-27 23:39:09 +02:00
|
|
|
|
|
|
|
#undef pathlen
|
|
|
|
#undef pathcnd
|
2009-08-30 15:22:39 +02:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_cd(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int optc, rv, phys_path;
|
2008-04-01 23:50:58 +02:00
|
|
|
bool physical = Flag(FPHYSICAL) ? true : false;
|
2005-05-23 05:06:10 +02:00
|
|
|
int cdnode; /* was a node from cdpath added in? */
|
2008-04-01 23:50:58 +02:00
|
|
|
bool printpath = false; /* print where we cd'd? */
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *pwd_s, *oldpwd_s;
|
|
|
|
XString xs;
|
2010-07-18 00:09:40 +02:00
|
|
|
char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'L':
|
2008-04-01 23:50:58 +02:00
|
|
|
physical = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'P':
|
2008-04-01 23:50:58 +02:00
|
|
|
physical = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
if (Flag(FRESTRICTED)) {
|
|
|
|
bi_errorf("restricted shell - can't cd");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pwd_s = global("PWD");
|
|
|
|
oldpwd_s = global("OLDPWD");
|
|
|
|
|
|
|
|
if (!wp[0]) {
|
|
|
|
/* No arguments - go home */
|
|
|
|
if ((dir = str_val(global("HOME"))) == null) {
|
|
|
|
bi_errorf("no home directory (HOME not set)");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
} else if (!wp[1]) {
|
|
|
|
/* One argument: - or dir */
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(allocd, wp[0], ATEMP);
|
|
|
|
if (ksh_isdash((dir = allocd))) {
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(allocd, ATEMP);
|
|
|
|
allocd = NULL;
|
2005-05-23 05:06:10 +02:00
|
|
|
dir = str_val(oldpwd_s);
|
|
|
|
if (dir == null) {
|
|
|
|
bi_errorf("no OLDPWD");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-01 23:50:58 +02:00
|
|
|
printpath = true;
|
2008-05-17 20:47:03 +02:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
} else if (!wp[2]) {
|
|
|
|
/* Two arguments - substitute arg1 in PWD for arg2 */
|
2010-09-14 23:26:19 +02:00
|
|
|
size_t ilen, olen, nlen, elen;
|
2005-05-23 05:06:10 +02:00
|
|
|
char *cp;
|
|
|
|
|
|
|
|
if (!current_wd[0]) {
|
2010-08-28 22:22:24 +02:00
|
|
|
bi_errorf("can't determine current directory");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
/* substitute arg1 for arg2 in current path.
|
|
|
|
* if the first substitution fails because the cd fails
|
|
|
|
* we could try to find another substitution. For now
|
|
|
|
* we don't
|
|
|
|
*/
|
|
|
|
if ((cp = strstr(current_wd, wp[0])) == NULL) {
|
|
|
|
bi_errorf("bad substitution");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2010-09-14 23:26:19 +02:00
|
|
|
/*-
|
|
|
|
* ilen = part of current_wd before wp[0]
|
|
|
|
* elen = part of current_wd after wp[0]
|
|
|
|
* because current_wd and wp[1] need to be in memory at the
|
|
|
|
* same time beforehand the addition can stay unchecked
|
|
|
|
*/
|
2005-05-23 05:06:10 +02:00
|
|
|
ilen = cp - current_wd;
|
|
|
|
olen = strlen(wp[0]);
|
|
|
|
nlen = strlen(wp[1]);
|
|
|
|
elen = strlen(current_wd + ilen + olen) + 1;
|
2008-12-13 18:02:18 +01:00
|
|
|
dir = allocd = alloc(ilen + nlen + elen, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
memcpy(dir, current_wd, ilen);
|
|
|
|
memcpy(dir + ilen, wp[1], nlen);
|
|
|
|
memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
|
2008-04-01 23:50:58 +02:00
|
|
|
printpath = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
|
|
|
bi_errorf("too many arguments");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-10-27 18:00:02 +01:00
|
|
|
#ifdef NO_PATH_MAX
|
|
|
|
/* only a first guess; make_path will enlarge xs if necessary */
|
|
|
|
XinitN(xs, 1024, ATEMP);
|
|
|
|
#else
|
2008-03-28 14:46:53 +01:00
|
|
|
XinitN(xs, PATH_MAX, ATEMP);
|
2009-10-27 18:00:02 +01:00
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
cdpath = str_val(global("CDPATH"));
|
|
|
|
do {
|
|
|
|
cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
|
|
|
|
if (physical)
|
2010-07-18 00:09:40 +02:00
|
|
|
rv = chdir(tryp = Xstring(xs, xp) + phys_path);
|
2005-05-23 05:06:10 +02:00
|
|
|
else {
|
|
|
|
simplify_path(Xstring(xs, xp));
|
2010-07-18 00:09:40 +02:00
|
|
|
rv = chdir(tryp = Xstring(xs, xp));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-02 00:20:20 +02:00
|
|
|
} while (rv < 0 && cdpath != NULL);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-04-02 00:20:20 +02:00
|
|
|
if (rv < 0) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (cdnode)
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", dir, "bad directory");
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", tryp, strerror(errno));
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(allocd, ATEMP);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-08-30 15:30:08 +02:00
|
|
|
/* allocd (above) => dir, which is no longer used */
|
|
|
|
afree(allocd, ATEMP);
|
|
|
|
allocd = NULL;
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
/* Clear out tracked aliases with relative paths */
|
|
|
|
flushcom(0);
|
|
|
|
|
|
|
|
/* Set OLDPWD (note: unsetting OLDPWD does not disable this
|
2009-06-11 14:42:21 +02:00
|
|
|
* setting in AT&T ksh)
|
2005-05-23 05:06:10 +02:00
|
|
|
*/
|
|
|
|
if (current_wd[0])
|
|
|
|
/* Ignore failure (happens if readonly or integer) */
|
|
|
|
setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
|
|
|
|
|
|
|
|
if (Xstring(xs, xp)[0] != '/') {
|
|
|
|
pwd = NULL;
|
2009-08-30 15:30:08 +02:00
|
|
|
} else if (!physical || !(pwd = allocd = do_realpath(Xstring(xs, xp))))
|
2005-05-23 05:06:10 +02:00
|
|
|
pwd = Xstring(xs, xp);
|
|
|
|
|
|
|
|
/* Set PWD */
|
|
|
|
if (pwd) {
|
|
|
|
char *ptmp = pwd;
|
2009-08-30 15:30:08 +02:00
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
set_current_wd(ptmp);
|
|
|
|
/* Ignore failure (happens if readonly or integer) */
|
|
|
|
setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
|
|
|
|
} else {
|
|
|
|
set_current_wd(null);
|
|
|
|
pwd = Xstring(xs, xp);
|
|
|
|
/* XXX unset $PWD? */
|
|
|
|
}
|
|
|
|
if (printpath || cdnode)
|
|
|
|
shprintf("%s\n", pwd);
|
|
|
|
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(allocd, ATEMP);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_pwd(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
int optc;
|
2008-04-02 00:20:20 +02:00
|
|
|
bool physical = Flag(FPHYSICAL) ? true : false;
|
2008-05-17 20:47:03 +02:00
|
|
|
char *p, *allocd = NULL;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'L':
|
2008-04-01 23:50:58 +02:00
|
|
|
physical = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'P':
|
2008-04-01 23:50:58 +02:00
|
|
|
physical = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
if (wp[0]) {
|
|
|
|
bi_errorf("too many arguments");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-08-30 15:30:08 +02:00
|
|
|
p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
|
2008-05-17 20:47:03 +02:00
|
|
|
current_wd) : NULL;
|
2006-11-10 07:16:25 +01:00
|
|
|
if (p && access(p, R_OK) < 0)
|
2005-05-23 05:06:10 +02:00
|
|
|
p = NULL;
|
2008-05-17 20:47:03 +02:00
|
|
|
if (!p && !(p = allocd = ksh_get_wd(NULL))) {
|
2010-08-28 22:22:24 +02:00
|
|
|
bi_errorf("%s: %s", "can't determine current directory",
|
|
|
|
strerror(errno));
|
2008-05-17 20:47:03 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
shprintf("%s\n", p);
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(allocd, ATEMP);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-09-19 23:54:46 +02:00
|
|
|
static const char *s_ptr;
|
|
|
|
static int s_get(void);
|
|
|
|
static void s_put(int);
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_print(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
#define PO_NL BIT(0) /* print newline */
|
|
|
|
#define PO_EXPAND BIT(1) /* expand backslash sequences */
|
|
|
|
#define PO_PMINUSMINUS BIT(2) /* print a -- argument */
|
|
|
|
#define PO_HIST BIT(3) /* print to history instead of stdout */
|
|
|
|
#define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
|
2009-10-10 23:17:31 +02:00
|
|
|
int fd = 1, c;
|
2005-05-23 05:06:10 +02:00
|
|
|
int flags = PO_EXPAND|PO_NL;
|
2008-04-02 00:20:20 +02:00
|
|
|
const char *s, *emsg;
|
2005-05-23 05:06:10 +02:00
|
|
|
XString xs;
|
|
|
|
char *xp;
|
|
|
|
|
2009-10-10 23:17:31 +02:00
|
|
|
if (wp[0][0] == 'e') {
|
|
|
|
/* echo builtin */
|
|
|
|
wp++;
|
2010-01-28 16:18:51 +01:00
|
|
|
if (Flag(FPOSIX) || Flag(FSH)) {
|
2009-10-10 23:17:31 +02:00
|
|
|
/* Debian Policy 10.4 compliant "echo" builtin */
|
|
|
|
if (*wp && !strcmp(*wp, "-n")) {
|
|
|
|
/* we recognise "-n" only as the first arg */
|
|
|
|
flags = 0;
|
2009-03-22 18:52:48 +01:00
|
|
|
wp++;
|
2009-10-10 23:17:31 +02:00
|
|
|
} else
|
|
|
|
/* otherwise, we print everything as-is */
|
|
|
|
flags = PO_NL;
|
|
|
|
} else {
|
|
|
|
int nflags = flags;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* a compromise between sysV and BSD echo commands:
|
|
|
|
* escape sequences are enabled by default, and -n,
|
|
|
|
* -e and -E are recognised if they appear in argu-
|
|
|
|
* ments with no illegal options (ie, echo -nq will
|
|
|
|
* print -nq).
|
|
|
|
* Different from sysV echo since options are reco-
|
|
|
|
* gnised, different from BSD echo since escape se-
|
|
|
|
* quences are enabled by default.
|
|
|
|
*/
|
|
|
|
|
2009-03-22 18:52:48 +01:00
|
|
|
while ((s = *wp) && *s == '-' && s[1]) {
|
|
|
|
while (*++s)
|
|
|
|
if (*s == 'n')
|
|
|
|
nflags &= ~PO_NL;
|
|
|
|
else if (*s == 'e')
|
|
|
|
nflags |= PO_EXPAND;
|
|
|
|
else if (*s == 'E')
|
|
|
|
nflags &= ~PO_EXPAND;
|
|
|
|
else
|
|
|
|
/*
|
|
|
|
* bad option: don't use
|
|
|
|
* nflags, print argument
|
|
|
|
*/
|
|
|
|
break;
|
2009-10-10 23:17:31 +02:00
|
|
|
|
2009-03-22 18:52:48 +01:00
|
|
|
if (*s)
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
2009-03-22 18:52:48 +01:00
|
|
|
wp++;
|
|
|
|
flags = nflags;
|
|
|
|
}
|
2009-10-10 23:17:31 +02:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
|
|
|
int optc;
|
|
|
|
const char *opts = "Rnprsu,";
|
2009-10-10 23:17:31 +02:00
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'R': /* fake BSD echo command */
|
|
|
|
flags |= PO_PMINUSMINUS;
|
|
|
|
flags &= ~PO_EXPAND;
|
|
|
|
opts = "ne";
|
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
flags |= PO_EXPAND;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
flags &= ~PO_NL;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", "-p", emsg);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
flags &= ~PO_EXPAND;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
flags |= PO_HIST;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
if (!*(s = builtin_opt.optarg))
|
|
|
|
fd = 0;
|
|
|
|
else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s: %s", "-u", s, emsg);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-10-10 23:17:31 +02:00
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!(builtin_opt.info & GI_MINUSMINUS)) {
|
|
|
|
/* treat a lone - like -- */
|
|
|
|
if (wp[builtin_opt.optind] &&
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
ksh_isdash(wp[builtin_opt.optind]))
|
2005-05-23 05:06:10 +02:00
|
|
|
builtin_opt.optind++;
|
|
|
|
} else if (flags & PO_PMINUSMINUS)
|
|
|
|
builtin_opt.optind--;
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
}
|
|
|
|
|
|
|
|
Xinit(xs, xp, 128, ATEMP);
|
|
|
|
|
|
|
|
while (*wp != NULL) {
|
|
|
|
s = *wp;
|
|
|
|
while ((c = *s++) != '\0') {
|
|
|
|
Xcheck(xs, xp);
|
|
|
|
if ((flags & PO_EXPAND) && c == '\\') {
|
2009-09-19 23:54:46 +02:00
|
|
|
s_ptr = s;
|
|
|
|
c = unbksl(false, s_get, s_put);
|
|
|
|
s = s_ptr;
|
|
|
|
if (c == -1) {
|
2009-09-19 17:16:05 +02:00
|
|
|
/* rejected by generic function */
|
|
|
|
switch ((c = *s++)) {
|
|
|
|
case 'c':
|
|
|
|
flags &= ~PO_NL;
|
|
|
|
/* AT&T brain damage */
|
|
|
|
continue;
|
|
|
|
case '\0':
|
|
|
|
s--;
|
|
|
|
c = '\\';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Xput(xs, xp, '\\');
|
2008-02-24 16:48:43 +01:00
|
|
|
}
|
2009-09-19 23:54:46 +02:00
|
|
|
} else if ((unsigned int)c > 0xFF) {
|
2009-09-19 17:16:05 +02:00
|
|
|
/* generic function returned Unicode */
|
|
|
|
char ts[4];
|
|
|
|
|
|
|
|
c = utf_wctomb(ts, c - 0x100);
|
|
|
|
ts[c] = 0;
|
|
|
|
for (c = 0; ts[c]; ++c)
|
|
|
|
Xput(xs, xp, ts[c]);
|
|
|
|
continue;
|
2009-10-15 18:36:27 +02:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
Xput(xs, xp, c);
|
|
|
|
}
|
|
|
|
if (*++wp != NULL)
|
|
|
|
Xput(xs, xp, ' ');
|
|
|
|
}
|
|
|
|
if (flags & PO_NL)
|
|
|
|
Xput(xs, xp, '\n');
|
|
|
|
|
|
|
|
if (flags & PO_HIST) {
|
|
|
|
Xput(xs, xp, '\0');
|
2008-09-30 21:25:51 +02:00
|
|
|
histsave(&source->line, Xstring(xs, xp), true, false);
|
2005-05-23 05:06:10 +02:00
|
|
|
Xfree(xs, xp);
|
|
|
|
} else {
|
2009-10-10 23:17:31 +02:00
|
|
|
int len = Xlength(xs, xp);
|
2005-05-23 05:06:10 +02:00
|
|
|
int opipe = 0;
|
|
|
|
|
|
|
|
/* Ensure we aren't killed by a SIGPIPE while writing to
|
2009-06-10 20:12:51 +02:00
|
|
|
* a coprocess. AT&T ksh doesn't seem to do this (seems
|
2006-01-30 13:37:24 +01:00
|
|
|
* to just check that the co-process is alive which is
|
2005-05-23 05:06:10 +02:00
|
|
|
* not enough).
|
|
|
|
*/
|
|
|
|
if (coproc.write >= 0 && coproc.write == fd) {
|
|
|
|
flags |= PO_COPROC;
|
|
|
|
opipe = block_pipe();
|
|
|
|
}
|
|
|
|
for (s = Xstring(xs, xp); len > 0; ) {
|
2009-10-10 23:17:31 +02:00
|
|
|
if ((c = write(fd, s, len)) < 0) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (flags & PO_COPROC)
|
|
|
|
restore_pipe(opipe);
|
|
|
|
if (errno == EINTR) {
|
|
|
|
/* allow user to ^C out */
|
|
|
|
intrcheck();
|
|
|
|
if (flags & PO_COPROC)
|
|
|
|
opipe = block_pipe();
|
|
|
|
continue;
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-10-10 23:17:31 +02:00
|
|
|
s += c;
|
|
|
|
len -= c;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (flags & PO_COPROC)
|
|
|
|
restore_pipe(opipe);
|
|
|
|
}
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-09-19 23:54:46 +02:00
|
|
|
static int
|
|
|
|
s_get(void)
|
|
|
|
{
|
|
|
|
return (*s_ptr++);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-12-12 23:27:10 +01:00
|
|
|
s_put(int c MKSH_A_UNUSED)
|
2009-09-19 23:54:46 +02:00
|
|
|
{
|
|
|
|
--s_ptr;
|
|
|
|
}
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_whence(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
struct tbl *tp;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *id;
|
2008-04-01 23:50:58 +02:00
|
|
|
bool pflag = false, vflag = false, Vflag = false;
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv = 0, optc, fcflags;
|
|
|
|
bool iam_whence = wp[0][0] == 'w';
|
2005-05-23 05:06:10 +02:00
|
|
|
const char *opts = iam_whence ? "pv" : "pvV";
|
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'p':
|
2008-04-01 23:50:58 +02:00
|
|
|
pflag = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'v':
|
2008-04-01 23:50:58 +02:00
|
|
|
vflag = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'V':
|
2008-04-01 23:50:58 +02:00
|
|
|
Vflag = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
fcflags = FC_BI | FC_PATH | FC_FUNC;
|
|
|
|
if (!iam_whence) {
|
|
|
|
/* Note that -p on its own is deal with in comexec() */
|
|
|
|
if (pflag)
|
|
|
|
fcflags |= FC_DEFPATH;
|
|
|
|
/* Convert command options to whence options - note that
|
|
|
|
* command -pV uses a different path search than whence -v
|
2009-06-10 20:12:51 +02:00
|
|
|
* or whence -pv. This should be considered a feature.
|
2005-05-23 05:06:10 +02:00
|
|
|
*/
|
|
|
|
vflag = Vflag;
|
|
|
|
}
|
|
|
|
if (pflag)
|
|
|
|
fcflags &= ~(FC_BI | FC_FUNC);
|
|
|
|
|
2008-04-02 00:20:20 +02:00
|
|
|
while ((vflag || rv == 0) && (id = *wp++) != NULL) {
|
2009-08-28 22:30:59 +02:00
|
|
|
uint32_t h = 0;
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
tp = NULL;
|
|
|
|
if ((iam_whence || vflag) && !pflag)
|
2009-08-28 22:30:59 +02:00
|
|
|
tp = ktsearch(&keywords, id, h = hash(id));
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!tp && !pflag) {
|
2009-08-28 22:30:59 +02:00
|
|
|
tp = ktsearch(&aliases, id, h ? h : hash(id));
|
2005-05-23 05:06:10 +02:00
|
|
|
if (tp && !(tp->flag & ISSET))
|
|
|
|
tp = NULL;
|
|
|
|
}
|
|
|
|
if (!tp)
|
|
|
|
tp = findcom(id, fcflags);
|
|
|
|
if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
|
|
|
|
tp->type != CTALIAS))
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts(id, shl_stdout);
|
2010-08-28 20:50:58 +02:00
|
|
|
if (vflag)
|
|
|
|
switch (tp->type) {
|
|
|
|
case CKEYWD:
|
|
|
|
case CALIAS:
|
|
|
|
case CFUNC:
|
|
|
|
case CSHELL:
|
|
|
|
shf_puts(" is a", shl_stdout);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (tp->type) {
|
|
|
|
case CKEYWD:
|
|
|
|
if (vflag)
|
2010-08-28 20:50:58 +02:00
|
|
|
shf_puts(" reserved word", shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case CALIAS:
|
|
|
|
if (vflag)
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("n %s%s for ",
|
|
|
|
(tp->flag & EXPORT) ? "exported " : null,
|
|
|
|
T_alias);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!iam_whence && !vflag)
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s %s=", T_alias, id);
|
2005-05-23 05:06:10 +02:00
|
|
|
print_value_quoted(tp->val.s);
|
|
|
|
break;
|
|
|
|
case CFUNC:
|
|
|
|
if (vflag) {
|
|
|
|
if (tp->flag & EXPORT)
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts("n exported", shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (tp->flag & TRACE)
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts(" traced", shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!(tp->flag & ISSET)) {
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts(" undefined", shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (tp->u.fpath)
|
|
|
|
shprintf(" (autoload from %s)",
|
|
|
|
tp->u.fpath);
|
|
|
|
}
|
2010-08-28 22:22:24 +02:00
|
|
|
shf_puts(T__function, shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CSHELL:
|
|
|
|
if (vflag)
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s %s %s",
|
|
|
|
(tp->flag & SPEC_BI) ? " special" : null,
|
|
|
|
"shell", T_builtin);
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case CTALIAS:
|
|
|
|
case CEXEC:
|
|
|
|
if (tp->flag & ISSET) {
|
|
|
|
if (vflag) {
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts(" is ", shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (tp->type == CTALIAS)
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("a tracked %s%s for ",
|
2005-05-23 05:06:10 +02:00
|
|
|
(tp->flag & EXPORT) ?
|
2010-08-28 20:50:58 +02:00
|
|
|
"exported " : null,
|
|
|
|
T_alias);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts(tp->val.s, shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
|
|
|
if (vflag)
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf(" %s\n", "not found");
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = 1;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
shprintf("%s is *GOK*", id);
|
|
|
|
break;
|
|
|
|
}
|
2008-04-02 00:20:20 +02:00
|
|
|
if (vflag || !rv)
|
2007-07-22 15:34:52 +02:00
|
|
|
shf_putc('\n', shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Deal with command -vV - command -p dealt with in comexec() */
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_command(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2009-06-10 20:12:51 +02:00
|
|
|
/* Let c_whence do the work. Note that c_command() must be
|
2005-05-23 05:06:10 +02:00
|
|
|
* a distinct function from c_whence() (tested in comexec()).
|
|
|
|
*/
|
2009-06-08 22:06:50 +02:00
|
|
|
return (c_whence(wp));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* typeset, export, and readonly */
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_typeset(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2007-07-01 23:10:29 +02:00
|
|
|
struct block *l;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *vp, **p;
|
2008-04-01 23:39:45 +02:00
|
|
|
Tflag fset = 0, fclr = 0, flag;
|
|
|
|
int thing = 0, field, base, optc;
|
|
|
|
const char *opts;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *fieldstr, *basestr;
|
2008-04-01 23:39:45 +02:00
|
|
|
bool localv = false, func = false, pflag = false, istset = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
switch (**wp) {
|
|
|
|
case 'e': /* export */
|
|
|
|
fset |= EXPORT;
|
2008-04-01 23:39:45 +02:00
|
|
|
istset = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'r': /* readonly */
|
|
|
|
fset |= RDONLY;
|
2008-04-01 23:39:45 +02:00
|
|
|
istset = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 's': /* set */
|
|
|
|
/* called with 'typeset -' */
|
|
|
|
break;
|
|
|
|
case 't': /* typeset */
|
2008-04-01 23:39:45 +02:00
|
|
|
localv = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-04-01 23:39:45 +02:00
|
|
|
/* see comment below regarding possible opions */
|
2009-09-06 19:42:15 +02:00
|
|
|
opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
|
2008-04-01 23:39:45 +02:00
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
fieldstr = basestr = NULL;
|
|
|
|
builtin_opt.flags |= GF_PLUSOPT;
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh seems to have 0-9 as options which are multiplied
|
2005-05-23 05:06:10 +02:00
|
|
|
* to get a number that is used with -L, -R, -Z or -i (eg, -1R2
|
2009-06-10 20:12:51 +02:00
|
|
|
* sets right justify in a field of 12). This allows options
|
2005-05-23 05:06:10 +02:00
|
|
|
* to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
|
|
|
|
* does not allow the number to be specified as a separate argument
|
|
|
|
* Here, the number must follow the RLZi option, but is optional
|
|
|
|
* (see the # kludge in ksh_getopt()).
|
|
|
|
*/
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
|
2005-05-23 05:06:10 +02:00
|
|
|
flag = 0;
|
|
|
|
switch (optc) {
|
|
|
|
case 'L':
|
|
|
|
flag = LJUST;
|
|
|
|
fieldstr = builtin_opt.optarg;
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
flag = RJUST;
|
|
|
|
fieldstr = builtin_opt.optarg;
|
|
|
|
break;
|
|
|
|
case 'U':
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh uses u, but this conflicts with
|
2009-06-10 20:12:51 +02:00
|
|
|
* upper/lower case. If this option is changed,
|
2005-05-23 05:06:10 +02:00
|
|
|
* need to change the -U below as well
|
|
|
|
*/
|
|
|
|
flag = INT_U;
|
|
|
|
break;
|
|
|
|
case 'Z':
|
|
|
|
flag = ZEROFIL;
|
|
|
|
fieldstr = builtin_opt.optarg;
|
|
|
|
break;
|
2009-08-28 23:51:52 +02:00
|
|
|
case 'a':
|
|
|
|
/*
|
|
|
|
* this is supposed to set (-a) or unset (+a) the
|
|
|
|
* indexed array attribute; it does nothing on an
|
|
|
|
* existing regular string or indexed array though
|
|
|
|
*/
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
case 'f':
|
2008-04-01 23:39:45 +02:00
|
|
|
func = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
flag = INTEGER;
|
|
|
|
basestr = builtin_opt.optarg;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
flag = LCASEV;
|
|
|
|
break;
|
2009-09-06 19:42:15 +02:00
|
|
|
case 'n':
|
|
|
|
set_refflag = (builtin_opt.info & GI_PLUS) ? 2 : 1;
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
case 'p':
|
2008-04-01 23:39:45 +02:00
|
|
|
/* export, readonly: POSIX -p flag */
|
|
|
|
/* typeset: show values as well */
|
|
|
|
pflag = true;
|
|
|
|
if (istset)
|
|
|
|
continue;
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
case 'r':
|
|
|
|
flag = RDONLY;
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
flag = TRACE;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
flag = UCASEV_AL; /* upper case / autoload */
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
flag = EXPORT;
|
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (builtin_opt.info & GI_PLUS) {
|
|
|
|
fclr |= flag;
|
|
|
|
fset &= ~flag;
|
|
|
|
thing = '+';
|
|
|
|
} else {
|
|
|
|
fset |= flag;
|
|
|
|
fclr &= ~flag;
|
|
|
|
thing = '-';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
field = 0;
|
|
|
|
if (fieldstr && !bi_getn(fieldstr, &field))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
base = 0;
|
|
|
|
if (basestr && !bi_getn(basestr, &base))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
|
|
|
|
(wp[builtin_opt.optind][0] == '-' ||
|
|
|
|
wp[builtin_opt.optind][0] == '+') &&
|
|
|
|
wp[builtin_opt.optind][1] == '\0') {
|
|
|
|
thing = wp[builtin_opt.optind][0];
|
|
|
|
builtin_opt.optind++;
|
|
|
|
}
|
|
|
|
|
2009-09-06 19:42:15 +02:00
|
|
|
if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) || set_refflag)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
bi_errorf("only -t, -u and -x options may be used with -f");
|
2009-09-06 19:42:15 +02:00
|
|
|
set_refflag = 0;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (wp[builtin_opt.optind]) {
|
|
|
|
/* Take care of exclusions.
|
2009-09-06 19:42:15 +02:00
|
|
|
* At this point, flags in fset are cleared in fclr and vice
|
2009-06-10 20:12:51 +02:00
|
|
|
* versa. This property should be preserved.
|
2005-05-23 05:06:10 +02:00
|
|
|
*/
|
|
|
|
if (fset & LCASEV) /* LCASEV has priority over UCASEV_AL */
|
|
|
|
fset &= ~UCASEV_AL;
|
|
|
|
if (fset & LJUST) /* LJUST has priority over RJUST */
|
|
|
|
fset &= ~RJUST;
|
|
|
|
if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { /* -Z implies -ZR */
|
|
|
|
fset |= RJUST;
|
|
|
|
fclr &= ~RJUST;
|
|
|
|
}
|
|
|
|
/* Setting these attributes clears the others, unless they
|
|
|
|
* are also set in this command
|
|
|
|
*/
|
2009-09-06 19:42:15 +02:00
|
|
|
if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
|
|
|
|
INTEGER | INT_U | INT_L)) || set_refflag)
|
2005-05-23 05:06:10 +02:00
|
|
|
fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
|
|
|
|
LCASEV | INTEGER | INT_U | INT_L);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set variables and attributes */
|
|
|
|
if (wp[builtin_opt.optind]) {
|
2008-04-02 00:20:20 +02:00
|
|
|
int i, rv = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *f;
|
|
|
|
|
|
|
|
if (localv && !func)
|
|
|
|
fset |= LOCAL;
|
|
|
|
for (i = builtin_opt.optind; wp[i]; i++) {
|
|
|
|
if (func) {
|
|
|
|
f = findfunc(wp[i], hash(wp[i]),
|
2009-08-28 22:30:59 +02:00
|
|
|
(fset&UCASEV_AL) ? true : false);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!f) {
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh does ++rv: bogus */
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = 1;
|
2005-05-23 05:06:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (fset | fclr) {
|
|
|
|
f->flag |= fset;
|
|
|
|
f->flag &= ~fclr;
|
2010-08-28 22:22:24 +02:00
|
|
|
} else {
|
|
|
|
fpFUNCTf(shl_stdout, 0, f->flag & FKSH,
|
|
|
|
wp[i], f->val.t);
|
|
|
|
shf_putc('\n', shl_stdout);
|
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
} else if (!typeset(wp[i], fset, fclr, field, base)) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", wp[i], "not identifier");
|
2009-09-06 19:42:15 +02:00
|
|
|
set_refflag = 0;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
2009-09-06 19:42:15 +02:00
|
|
|
set_refflag = 0;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* list variables and attributes */
|
|
|
|
flag = fset | fclr; /* no difference at this point.. */
|
|
|
|
if (func) {
|
|
|
|
for (l = e->loc; l; l = l->next) {
|
2006-01-29 21:04:54 +01:00
|
|
|
for (p = ktsort(&l->funs); (vp = *p++); ) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (flag && (vp->flag & flag) == 0)
|
|
|
|
continue;
|
|
|
|
if (thing == '-')
|
2010-08-28 22:22:24 +02:00
|
|
|
fpFUNCTf(shl_stdout, 0,
|
|
|
|
vp->flag & FKSH,
|
2005-05-23 05:06:10 +02:00
|
|
|
vp->name, vp->val.t);
|
|
|
|
else
|
2010-08-28 22:22:24 +02:00
|
|
|
shf_puts(vp->name, shl_stdout);
|
|
|
|
shf_putc('\n', shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (l = e->loc; l; l = l->next) {
|
2006-01-29 21:04:54 +01:00
|
|
|
for (p = ktsort(&l->vars); (vp = *p++); ) {
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *tvp;
|
2008-04-02 00:20:20 +02:00
|
|
|
bool any_set = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
/*
|
|
|
|
* See if the parameter is set (for arrays, if any
|
|
|
|
* element is set).
|
|
|
|
*/
|
|
|
|
for (tvp = vp; tvp; tvp = tvp->u.array)
|
|
|
|
if (tvp->flag & ISSET) {
|
2008-04-02 00:20:20 +02:00
|
|
|
any_set = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check attributes - note that all array elements
|
|
|
|
* have (should have?) the same attributes, so checking
|
|
|
|
* the first is sufficient.
|
|
|
|
*
|
|
|
|
* Report an unset param only if the user has
|
|
|
|
* explicitly given it some attribute (like export);
|
|
|
|
* otherwise, after "echo $FOO", we would report FOO...
|
|
|
|
*/
|
|
|
|
if (!any_set && !(vp->flag & USERATTRIB))
|
|
|
|
continue;
|
|
|
|
if (flag && (vp->flag & flag) == 0)
|
|
|
|
continue;
|
|
|
|
for (; vp; vp = vp->u.array) {
|
|
|
|
/* Ignore array elements that aren't
|
|
|
|
* set unless there are no set elements,
|
|
|
|
* in which case the first is reported on */
|
|
|
|
if ((vp->flag&ARRAY) && any_set &&
|
|
|
|
!(vp->flag & ISSET))
|
|
|
|
continue;
|
|
|
|
/* no arguments */
|
|
|
|
if (thing == 0 && flag == 0) {
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh prints things
|
2005-05-23 05:06:10 +02:00
|
|
|
* like export, integer,
|
|
|
|
* leftadj, zerofill, etc.,
|
|
|
|
* but POSIX says must
|
|
|
|
* be suitable for re-entry...
|
|
|
|
*/
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts("typeset ", shl_stdout);
|
2009-09-06 19:42:15 +02:00
|
|
|
if (((vp->flag&(ARRAY|ASSOC))==ASSOC))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-n");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&INTEGER))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-i");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&EXPORT))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-x");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&RDONLY))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-r");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&TRACE))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-t");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&LJUST))
|
|
|
|
shprintf("-L%d ", vp->u2.field);
|
|
|
|
if ((vp->flag&RJUST))
|
|
|
|
shprintf("-R%d ", vp->u2.field);
|
|
|
|
if ((vp->flag&ZEROFIL))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-Z");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&LCASEV))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-l");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&UCASEV_AL))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-u");
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&INT_U))
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", "-U");
|
2008-04-01 23:39:45 +02:00
|
|
|
shf_puts(vp->name, shl_stdout);
|
|
|
|
if (pflag) {
|
|
|
|
char *s = str_val(vp);
|
|
|
|
|
|
|
|
shf_putc('=', shl_stdout);
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh can't have
|
2008-04-01 23:39:45 +02:00
|
|
|
* justified integers.. */
|
|
|
|
if ((vp->flag &
|
|
|
|
(INTEGER|LJUST|RJUST)) ==
|
|
|
|
INTEGER)
|
|
|
|
shf_puts(s, shl_stdout);
|
|
|
|
else
|
|
|
|
print_value_quoted(s);
|
|
|
|
}
|
|
|
|
shf_putc('\n', shl_stdout);
|
|
|
|
if (vp->flag & ARRAY)
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
|
|
|
if (pflag)
|
2008-04-01 23:39:45 +02:00
|
|
|
shf_puts(istset ?
|
|
|
|
"typeset " :
|
|
|
|
(flag & EXPORT) ?
|
|
|
|
"export " :
|
|
|
|
"readonly ",
|
|
|
|
shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((vp->flag&ARRAY) && any_set)
|
2007-10-18 22:32:33 +02:00
|
|
|
shprintf("%s[%lu]",
|
|
|
|
vp->name,
|
2009-09-06 19:42:15 +02:00
|
|
|
arrayindex(vp));
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts(vp->name, shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (thing == '-' && (vp->flag&ISSET)) {
|
|
|
|
char *s = str_val(vp);
|
|
|
|
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_putc('=', shl_stdout);
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh can't have
|
2005-05-23 05:06:10 +02:00
|
|
|
* justified integers.. */
|
|
|
|
if ((vp->flag &
|
|
|
|
(INTEGER|LJUST|RJUST)) ==
|
|
|
|
INTEGER)
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts(s, shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
|
|
|
print_value_quoted(s);
|
|
|
|
}
|
2007-07-22 15:34:52 +02:00
|
|
|
shf_putc('\n', shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
/* Only report first 'element' of an array with
|
2008-04-01 23:50:58 +02:00
|
|
|
* no set elements.
|
|
|
|
*/
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!any_set)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_alias(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
struct table *t = &aliases;
|
2008-04-01 23:50:58 +02:00
|
|
|
int rv = 0, prefix = 0;
|
|
|
|
bool rflag = false, tflag, Uflag = false, pflag = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
Tflag xflag = 0;
|
|
|
|
int optc;
|
|
|
|
|
|
|
|
builtin_opt.flags |= GF_PLUSOPT;
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != -1) {
|
2005-05-23 05:06:10 +02:00
|
|
|
prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
|
|
|
|
switch (optc) {
|
|
|
|
case 'd':
|
2009-07-25 22:26:33 +02:00
|
|
|
#ifdef MKSH_NOPWNAM
|
|
|
|
t = NULL; /* fix "alias -dt" */
|
2007-01-11 01:32:31 +01:00
|
|
|
#else
|
2005-05-23 05:06:10 +02:00
|
|
|
t = &homedirs;
|
2007-01-11 01:32:31 +01:00
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'p':
|
2008-04-01 23:50:58 +02:00
|
|
|
pflag = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'r':
|
2008-04-01 23:50:58 +02:00
|
|
|
rflag = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
t = &taliases;
|
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
/*
|
|
|
|
* kludge for tracked alias initialization
|
|
|
|
* (don't do a path search, just make an entry)
|
|
|
|
*/
|
2008-04-01 23:50:58 +02:00
|
|
|
Uflag = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
xflag = EXPORT;
|
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
2009-07-25 22:26:33 +02:00
|
|
|
#ifdef MKSH_NOPWNAM
|
|
|
|
if (t == NULL)
|
|
|
|
return (0);
|
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
|
|
|
|
(wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
|
|
|
|
prefix = wp[0][0];
|
|
|
|
wp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
tflag = t == &taliases;
|
|
|
|
|
|
|
|
/* "hash -r" means reset all the tracked aliases.. */
|
|
|
|
if (rflag) {
|
2007-03-04 01:13:17 +01:00
|
|
|
static const char *args[] = {
|
2010-08-28 20:50:58 +02:00
|
|
|
T_unalias, "-ta", NULL
|
2007-03-04 01:13:17 +01:00
|
|
|
};
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (!tflag || *wp) {
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s: -r flag can only be used with -t"
|
|
|
|
" and without arguments\n", T_alias);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
ksh_getopt_reset(&builtin_opt, GF_ERROR);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (c_unalias(args));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*wp == NULL) {
|
|
|
|
struct tbl *ap, **p;
|
|
|
|
|
2006-01-29 21:04:54 +01:00
|
|
|
for (p = ktsort(t); (ap = *p++) != NULL; )
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
|
|
|
|
if (pflag)
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", T_alias);
|
2005-05-23 05:06:10 +02:00
|
|
|
shf_puts(ap->name, shl_stdout);
|
|
|
|
if (prefix != '+') {
|
|
|
|
shf_putc('=', shl_stdout);
|
|
|
|
print_value_quoted(ap->val.s);
|
|
|
|
}
|
2007-07-22 15:34:52 +02:00
|
|
|
shf_putc('\n', shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; *wp != NULL; wp++) {
|
2008-04-02 00:20:20 +02:00
|
|
|
const char *alias = *wp, *val, *newval;
|
2007-03-04 01:13:17 +01:00
|
|
|
char *xalias = NULL;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *ap;
|
2009-08-28 20:54:01 +02:00
|
|
|
uint32_t h;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-07-12 18:56:40 +02:00
|
|
|
if ((val = cstrchr(alias, '='))) {
|
2008-10-28 15:32:43 +01:00
|
|
|
strndupx(xalias, alias, val++ - alias, ATEMP);
|
|
|
|
alias = xalias;
|
2008-07-12 18:56:40 +02:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
h = hash(alias);
|
|
|
|
if (val == NULL && !tflag && !xflag) {
|
2009-08-28 22:30:59 +02:00
|
|
|
ap = ktsearch(t, alias, h);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (ap != NULL && (ap->flag&ISSET)) {
|
|
|
|
if (pflag)
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s ", T_alias);
|
2005-05-23 05:06:10 +02:00
|
|
|
shf_puts(ap->name, shl_stdout);
|
|
|
|
if (prefix != '+') {
|
|
|
|
shf_putc('=', shl_stdout);
|
|
|
|
print_value_quoted(ap->val.s);
|
|
|
|
}
|
2007-07-22 15:34:52 +02:00
|
|
|
shf_putc('\n', shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
2010-08-28 20:50:58 +02:00
|
|
|
shprintf("%s %s %s\n", alias, T_alias,
|
|
|
|
"not found");
|
2005-05-23 05:06:10 +02:00
|
|
|
rv = 1;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2009-08-28 22:30:59 +02:00
|
|
|
ap = ktenter(t, alias, h);
|
2005-05-23 05:06:10 +02:00
|
|
|
ap->type = tflag ? CTALIAS : CALIAS;
|
|
|
|
/* Are we setting the value or just some flags? */
|
|
|
|
if ((val && !tflag) || (!val && tflag && !Uflag)) {
|
|
|
|
if (ap->flag&ALLOC) {
|
|
|
|
ap->flag &= ~(ALLOC|ISSET);
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(ap->val.s, APERM);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-06-11 14:42:21 +02:00
|
|
|
/* ignore values for -t (AT&T ksh does this) */
|
2008-07-12 18:56:40 +02:00
|
|
|
newval = tflag ? search(alias, path, X_OK, NULL) : val;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (newval) {
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(ap->val.s, newval, APERM);
|
2005-05-23 05:06:10 +02:00
|
|
|
ap->flag |= ALLOC|ISSET;
|
|
|
|
} else
|
|
|
|
ap->flag &= ~ISSET;
|
|
|
|
}
|
|
|
|
ap->flag |= DEFINED;
|
|
|
|
if (prefix == '+')
|
|
|
|
ap->flag &= ~xflag;
|
|
|
|
else
|
|
|
|
ap->flag |= xflag;
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(xalias, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_unalias(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
struct table *t = &aliases;
|
|
|
|
struct tbl *ap;
|
2008-04-01 23:50:58 +02:00
|
|
|
int optc, rv = 0;
|
|
|
|
bool all = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'a':
|
2008-04-01 23:50:58 +02:00
|
|
|
all = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'd':
|
2009-07-25 22:26:33 +02:00
|
|
|
#ifdef MKSH_NOPWNAM
|
|
|
|
t = NULL; /* fix "unalias -dt" */
|
2007-01-11 01:32:31 +01:00
|
|
|
#else
|
2005-05-23 05:06:10 +02:00
|
|
|
t = &homedirs;
|
2007-01-11 01:32:31 +01:00
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
t = &taliases;
|
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-07-25 22:26:33 +02:00
|
|
|
#ifdef MKSH_NOPWNAM
|
|
|
|
if (t == NULL)
|
|
|
|
return (0);
|
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
for (; *wp != NULL; wp++) {
|
2009-08-28 22:30:59 +02:00
|
|
|
ap = ktsearch(t, *wp, hash(*wp));
|
2005-05-23 05:06:10 +02:00
|
|
|
if (ap == NULL) {
|
|
|
|
rv = 1; /* POSIX */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ap->flag&ALLOC) {
|
|
|
|
ap->flag &= ~(ALLOC|ISSET);
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(ap->val.s, APERM);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
ap->flag &= ~(DEFINED|ISSET|EXPORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (all) {
|
|
|
|
struct tstate ts;
|
|
|
|
|
2006-01-29 21:04:54 +01:00
|
|
|
for (ktwalk(&ts, t); (ap = ktnext(&ts)); ) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (ap->flag&ALLOC) {
|
|
|
|
ap->flag &= ~(ALLOC|ISSET);
|
2008-11-12 01:54:52 +01:00
|
|
|
afree(ap->val.s, APERM);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
ap->flag &= ~(DEFINED|ISSET|EXPORT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_let(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
int rv = 1;
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t val;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-06-11 14:42:21 +02:00
|
|
|
if (wp[1] == NULL) /* AT&T ksh does this */
|
2005-05-23 05:06:10 +02:00
|
|
|
bi_errorf("no arguments");
|
|
|
|
else
|
|
|
|
for (wp++; *wp; wp++)
|
|
|
|
if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
|
|
|
|
rv = 2; /* distinguish error from zero result */
|
|
|
|
break;
|
|
|
|
} else
|
|
|
|
rv = val == 0;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_jobs(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-01 23:50:58 +02:00
|
|
|
int optc, flag = 0, nflag = 0, rv = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'l':
|
|
|
|
flag = 1;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
flag = 2;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
nflag = 1;
|
|
|
|
break;
|
|
|
|
case 'z': /* debugging: print zombies */
|
|
|
|
nflag = -1;
|
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
if (!*wp) {
|
|
|
|
if (j_jobs(NULL, flag, nflag))
|
|
|
|
rv = 1;
|
|
|
|
} else {
|
|
|
|
for (; *wp; wp++)
|
|
|
|
if (j_jobs(*wp, flag, nflag))
|
|
|
|
rv = 1;
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-04-05 14:35:32 +02:00
|
|
|
#ifndef MKSH_UNEMPLOYED
|
2005-05-23 05:06:10 +02:00
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_fgbg(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-01 23:50:58 +02:00
|
|
|
bool bg = strcmp(*wp, "bg") == 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
int rv = 0;
|
|
|
|
|
|
|
|
if (!Flag(FMONITOR)) {
|
|
|
|
bi_errorf("job control not enabled");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
wp += builtin_opt.optind;
|
|
|
|
if (*wp)
|
|
|
|
for (; *wp; wp++)
|
|
|
|
rv = j_resume(*wp, bg);
|
|
|
|
else
|
|
|
|
rv = j_resume("%%", bg);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (bg ? 0 : rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-04-05 14:35:32 +02:00
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* format a single kill item */
|
|
|
|
static char *
|
2009-11-10 00:35:11 +01:00
|
|
|
kill_fmt_entry(char *buf, int buflen, int i, const void *arg)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2006-11-12 15:58:16 +01:00
|
|
|
const struct kill_info *ki = (const struct kill_info *)arg;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
i++;
|
Clean up the signal mess, saves 172 Bytes:
* 'sigseen' in Build.sh goes away
* Signal name existence is checked in this order:
have our own¹ -> sys_signame[] -> _sys_signame[] -> build our own²
* Signal description existence is checked in this order:
sys_siglist[] -> _sys_siglist[] -> strsignal() -> NULL³
¹ Predefined list of items, for operating systems where we
cannot build² them, i.e. Plan 9 and Minix 3 (e.g. no $CPP -dD)
² The usual cpp(1) stuff
³ Changed later, see below
* Make $CPP test dependent on $NEED_MKSH_SIGNAME (others can
be added here, this is not absolute)
* Make signal name list generation² dependent on $NEED_MKSH_SIGNAME
* Fix check if the generation worked
* Guarantee that sigtraps[*].name and sigtraps[*].mess are valid
C strings; this makes the code shorter *and* removes a few pos-
sible nil pointer dereferences
* Embed autoconf'd usages of sys_sig* / strsignal / mksh_sigpairs
into inittraps()
* Check for each signal 0<=i<=NSIG that
name is not NULL or "" -> replace with ("%d", i)
mess is not NULL or "" -> replace with ("Signal %d", i)
name does not start (case-insensitive) with "SIG" -> name += 3
* In gettrap(), fix check if signal name starts, case-sensitive
or case-insensitive, depending on need, with "SIG" (bug from millert@)
Other changes:
* Build.sh: ac_test[n]() are documented
* Build.sh: ac_test[n]() can have negative prereqs as well now
* Build.sh: use <<-'EOF' consistently
* bump patchlevel to today
2007-01-12 01:25:40 +01:00
|
|
|
shf_snprintf(buf, buflen, "%*d %*s %s",
|
|
|
|
ki->num_width, i,
|
|
|
|
ki->name_width, sigtraps[i].name,
|
|
|
|
sigtraps[i].mess);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (buf);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_kill(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
Trap *t = NULL;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *p;
|
2008-04-01 23:50:58 +02:00
|
|
|
bool lflag = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
int i, n, rv, sig;
|
|
|
|
|
|
|
|
/* assume old style options if -digits or -UPPERCASE */
|
2006-11-10 08:52:04 +01:00
|
|
|
if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
|
|
|
|
ksh_isupper(p[1]))) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!(t = gettrap(p + 1, true))) {
|
|
|
|
bi_errorf("bad signal '%s'", p + 1);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
|
|
|
|
} else {
|
|
|
|
int optc;
|
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'l':
|
2008-04-01 23:50:58 +02:00
|
|
|
lflag = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
if (!(t = gettrap(builtin_opt.optarg, true))) {
|
|
|
|
bi_errorf("bad signal '%s'",
|
|
|
|
builtin_opt.optarg);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
i = builtin_opt.optind;
|
|
|
|
}
|
|
|
|
if ((lflag && t) || (!wp[i] && !lflag)) {
|
2006-11-10 00:19:52 +01:00
|
|
|
#ifndef MKSH_SMALL
|
2008-06-08 19:15:30 +02:00
|
|
|
shf_puts("usage:\tkill [-s signame | -signum | -signame]"
|
|
|
|
" { job | pid | pgrp } ...\n"
|
|
|
|
"\tkill -l [exit_status ...]\n", shl_out);
|
2006-11-10 00:19:52 +01:00
|
|
|
#endif
|
2007-07-22 16:01:50 +02:00
|
|
|
bi_errorfz();
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (lflag) {
|
|
|
|
if (wp[i]) {
|
|
|
|
for (; wp[i]; i++) {
|
|
|
|
if (!bi_getn(wp[i], &n))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (n > 128 && n < 128 + NSIG)
|
|
|
|
n -= 128;
|
Clean up the signal mess, saves 172 Bytes:
* 'sigseen' in Build.sh goes away
* Signal name existence is checked in this order:
have our own¹ -> sys_signame[] -> _sys_signame[] -> build our own²
* Signal description existence is checked in this order:
sys_siglist[] -> _sys_siglist[] -> strsignal() -> NULL³
¹ Predefined list of items, for operating systems where we
cannot build² them, i.e. Plan 9 and Minix 3 (e.g. no $CPP -dD)
² The usual cpp(1) stuff
³ Changed later, see below
* Make $CPP test dependent on $NEED_MKSH_SIGNAME (others can
be added here, this is not absolute)
* Make signal name list generation² dependent on $NEED_MKSH_SIGNAME
* Fix check if the generation worked
* Guarantee that sigtraps[*].name and sigtraps[*].mess are valid
C strings; this makes the code shorter *and* removes a few pos-
sible nil pointer dereferences
* Embed autoconf'd usages of sys_sig* / strsignal / mksh_sigpairs
into inittraps()
* Check for each signal 0<=i<=NSIG that
name is not NULL or "" -> replace with ("%d", i)
mess is not NULL or "" -> replace with ("Signal %d", i)
name does not start (case-insensitive) with "SIG" -> name += 3
* In gettrap(), fix check if signal name starts, case-sensitive
or case-insensitive, depending on need, with "SIG" (bug from millert@)
Other changes:
* Build.sh: ac_test[n]() are documented
* Build.sh: ac_test[n]() can have negative prereqs as well now
* Build.sh: use <<-'EOF' consistently
* bump patchlevel to today
2007-01-12 01:25:40 +01:00
|
|
|
if (n > 0 && n < NSIG)
|
2005-05-23 05:06:10 +02:00
|
|
|
shprintf("%s\n", sigtraps[n].name);
|
|
|
|
else
|
|
|
|
shprintf("%d\n", n);
|
|
|
|
}
|
|
|
|
} else {
|
2009-11-10 00:35:11 +01:00
|
|
|
int w, j, mess_cols, mess_octs;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct kill_info ki;
|
|
|
|
|
|
|
|
for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
|
|
|
|
ki.num_width++;
|
2009-11-10 00:35:11 +01:00
|
|
|
ki.name_width = mess_cols = mess_octs = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
for (j = 0; j < NSIG; j++) {
|
Clean up the signal mess, saves 172 Bytes:
* 'sigseen' in Build.sh goes away
* Signal name existence is checked in this order:
have our own¹ -> sys_signame[] -> _sys_signame[] -> build our own²
* Signal description existence is checked in this order:
sys_siglist[] -> _sys_siglist[] -> strsignal() -> NULL³
¹ Predefined list of items, for operating systems where we
cannot build² them, i.e. Plan 9 and Minix 3 (e.g. no $CPP -dD)
² The usual cpp(1) stuff
³ Changed later, see below
* Make $CPP test dependent on $NEED_MKSH_SIGNAME (others can
be added here, this is not absolute)
* Make signal name list generation² dependent on $NEED_MKSH_SIGNAME
* Fix check if the generation worked
* Guarantee that sigtraps[*].name and sigtraps[*].mess are valid
C strings; this makes the code shorter *and* removes a few pos-
sible nil pointer dereferences
* Embed autoconf'd usages of sys_sig* / strsignal / mksh_sigpairs
into inittraps()
* Check for each signal 0<=i<=NSIG that
name is not NULL or "" -> replace with ("%d", i)
mess is not NULL or "" -> replace with ("Signal %d", i)
name does not start (case-insensitive) with "SIG" -> name += 3
* In gettrap(), fix check if signal name starts, case-sensitive
or case-insensitive, depending on need, with "SIG" (bug from millert@)
Other changes:
* Build.sh: ac_test[n]() are documented
* Build.sh: ac_test[n]() can have negative prereqs as well now
* Build.sh: use <<-'EOF' consistently
* bump patchlevel to today
2007-01-12 01:25:40 +01:00
|
|
|
w = strlen(sigtraps[j].name);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (w > ki.name_width)
|
|
|
|
ki.name_width = w;
|
|
|
|
w = strlen(sigtraps[j].mess);
|
2009-11-10 00:35:11 +01:00
|
|
|
if (w > mess_octs)
|
|
|
|
mess_octs = w;
|
2009-11-28 15:28:03 +01:00
|
|
|
w = utf_mbswidth(sigtraps[j].mess);
|
2009-11-10 00:35:11 +01:00
|
|
|
if (w > mess_cols)
|
|
|
|
mess_cols = w;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
print_columns(shl_stdout, NSIG - 1,
|
Clean up the signal mess, saves 172 Bytes:
* 'sigseen' in Build.sh goes away
* Signal name existence is checked in this order:
have our own¹ -> sys_signame[] -> _sys_signame[] -> build our own²
* Signal description existence is checked in this order:
sys_siglist[] -> _sys_siglist[] -> strsignal() -> NULL³
¹ Predefined list of items, for operating systems where we
cannot build² them, i.e. Plan 9 and Minix 3 (e.g. no $CPP -dD)
² The usual cpp(1) stuff
³ Changed later, see below
* Make $CPP test dependent on $NEED_MKSH_SIGNAME (others can
be added here, this is not absolute)
* Make signal name list generation² dependent on $NEED_MKSH_SIGNAME
* Fix check if the generation worked
* Guarantee that sigtraps[*].name and sigtraps[*].mess are valid
C strings; this makes the code shorter *and* removes a few pos-
sible nil pointer dereferences
* Embed autoconf'd usages of sys_sig* / strsignal / mksh_sigpairs
into inittraps()
* Check for each signal 0<=i<=NSIG that
name is not NULL or "" -> replace with ("%d", i)
mess is not NULL or "" -> replace with ("Signal %d", i)
name does not start (case-insensitive) with "SIG" -> name += 3
* In gettrap(), fix check if signal name starts, case-sensitive
or case-insensitive, depending on need, with "SIG" (bug from millert@)
Other changes:
* Build.sh: ac_test[n]() are documented
* Build.sh: ac_test[n]() can have negative prereqs as well now
* Build.sh: use <<-'EOF' consistently
* bump patchlevel to today
2007-01-12 01:25:40 +01:00
|
|
|
kill_fmt_entry, (void *)&ki,
|
2009-11-10 00:35:11 +01:00
|
|
|
ki.num_width + 1 + ki.name_width + 1 + mess_octs,
|
|
|
|
ki.num_width + 1 + ki.name_width + 1 + mess_cols,
|
|
|
|
true);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
rv = 0;
|
|
|
|
sig = t ? t->signal : SIGTERM;
|
|
|
|
for (; (p = wp[i]); i++) {
|
|
|
|
if (*p == '%') {
|
|
|
|
if (j_kill(p, sig))
|
|
|
|
rv = 1;
|
|
|
|
} else if (!getn(p, &n)) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", p,
|
|
|
|
"arguments must be jobs or process IDs");
|
2005-05-23 05:06:10 +02:00
|
|
|
rv = 1;
|
|
|
|
} else {
|
2009-06-08 22:34:40 +02:00
|
|
|
if (mksh_kill(n, sig) < 0) {
|
2005-05-23 05:06:10 +02:00
|
|
|
bi_errorf("%s: %s", p, strerror(errno));
|
|
|
|
rv = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
getopts_reset(int val)
|
|
|
|
{
|
|
|
|
if (val >= 1) {
|
2005-07-04 14:27:28 +02:00
|
|
|
ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT);
|
2005-05-23 05:06:10 +02:00
|
|
|
user_opt.optind = user_opt.uoptind = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_getopts(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int argc, optc, rv;
|
|
|
|
const char *opts, *var;
|
|
|
|
char buf[3];
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *vq, *voptarg;
|
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
opts = *wp++;
|
|
|
|
if (!opts) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("missing %s argument", "options");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var = *wp++;
|
|
|
|
if (!var) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("missing %s argument", "name");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (!*var || *skip_varname(var, true)) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", var, "is not an identifier");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (e->loc->next == NULL) {
|
2010-08-28 20:50:58 +02:00
|
|
|
internal_warningf("%s: %s", "c_getopts", "no argv");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
/* Which arguments are we parsing... */
|
|
|
|
if (*wp == NULL)
|
|
|
|
wp = e->loc->next->argv;
|
|
|
|
else
|
|
|
|
*--wp = e->loc->next->argv[0];
|
|
|
|
|
|
|
|
/* Check that our saved state won't cause a core dump... */
|
|
|
|
for (argc = 0; wp[argc]; argc++)
|
|
|
|
;
|
|
|
|
if (user_opt.optind > argc ||
|
|
|
|
(user_opt.p != 0 &&
|
|
|
|
user_opt.p > strlen(wp[user_opt.optind - 1]))) {
|
|
|
|
bi_errorf("arguments changed since last call");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
user_opt.optarg = NULL;
|
|
|
|
optc = ksh_getopt(wp, &user_opt, opts);
|
|
|
|
|
|
|
|
if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
|
|
|
|
buf[0] = '+';
|
|
|
|
buf[1] = optc;
|
|
|
|
buf[2] = '\0';
|
|
|
|
} else {
|
2009-06-11 14:42:21 +02:00
|
|
|
/* POSIX says var is set to ? at end-of-options, AT&T ksh
|
2005-05-23 05:06:10 +02:00
|
|
|
* sets it to null - we go with POSIX...
|
|
|
|
*/
|
|
|
|
buf[0] = optc < 0 ? '?' : optc;
|
|
|
|
buf[1] = '\0';
|
|
|
|
}
|
|
|
|
|
2009-10-15 18:32:50 +02:00
|
|
|
/* AT&T ksh93 in fact does change OPTIND for unknown options too */
|
|
|
|
user_opt.uoptind = user_opt.optind;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
voptarg = global("OPTARG");
|
2009-06-11 14:42:21 +02:00
|
|
|
voptarg->flag &= ~RDONLY; /* AT&T ksh clears ro and int */
|
2005-05-23 05:06:10 +02:00
|
|
|
/* Paranoia: ensure no bizarre results. */
|
|
|
|
if (voptarg->flag & INTEGER)
|
|
|
|
typeset("OPTARG", 0, INTEGER, 0, 0);
|
|
|
|
if (user_opt.optarg == NULL)
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
unset(voptarg, 1);
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
|
|
|
/* This can't fail (have cleared readonly/integer) */
|
|
|
|
setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
|
|
|
|
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
vq = global(var);
|
|
|
|
/* Error message already printed (integer, readonly) */
|
|
|
|
if (!setstr(vq, buf, KSH_RETURN_ERROR))
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = 1;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (Flag(FEXPORT))
|
|
|
|
typeset(var, EXPORT, 0, 0, 0);
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (optc < 0 ? 1 : rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_bind(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-01 23:50:58 +02:00
|
|
|
int optc, rv = 0;
|
2009-09-23 20:04:58 +02:00
|
|
|
#ifndef MKSH_SMALL
|
|
|
|
bool macro = false;
|
|
|
|
#endif
|
|
|
|
bool list = false;
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
const char *cp;
|
|
|
|
char *up;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-09-23 20:04:58 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt,
|
|
|
|
#ifndef MKSH_SMALL
|
|
|
|
"lm"
|
|
|
|
#else
|
|
|
|
"l"
|
|
|
|
#endif
|
|
|
|
)) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'l':
|
2008-04-01 23:50:58 +02:00
|
|
|
list = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
2009-09-23 20:04:58 +02:00
|
|
|
#ifndef MKSH_SMALL
|
2005-05-23 05:06:10 +02:00
|
|
|
case 'm':
|
2008-04-01 23:50:58 +02:00
|
|
|
macro = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
2009-09-23 20:04:58 +02:00
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
if (*wp == NULL) /* list all */
|
2009-09-23 20:04:58 +02:00
|
|
|
rv = x_bind(NULL, NULL,
|
|
|
|
#ifndef MKSH_SMALL
|
|
|
|
false,
|
|
|
|
#endif
|
|
|
|
list);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
for (; *wp != NULL; wp++) {
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
if ((cp = cstrchr(*wp, '=')) == NULL)
|
|
|
|
up = NULL;
|
|
|
|
else {
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(up, *wp, ATEMP);
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
up[cp++ - *wp] = '\0';
|
|
|
|
}
|
2009-09-23 20:04:58 +02:00
|
|
|
if (x_bind(up ? up : *wp, cp,
|
|
|
|
#ifndef MKSH_SMALL
|
|
|
|
macro,
|
|
|
|
#endif
|
|
|
|
false))
|
2005-05-23 05:06:10 +02:00
|
|
|
rv = 1;
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(up, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-06-10 20:11:27 +02:00
|
|
|
/* :, false and true (and ulimit if MKSH_NO_LIMITS) */
|
2005-05-23 05:06:10 +02:00
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_label(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2009-06-08 22:06:50 +02:00
|
|
|
return (wp[0][0] == 'f' ? 1 : 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_shift(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
struct block *l = e->loc;
|
|
|
|
int n;
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t val;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *arg;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
arg = wp[builtin_opt.optind];
|
|
|
|
|
|
|
|
if (arg) {
|
|
|
|
evaluate(arg, &val, KSH_UNWIND_ERROR, false);
|
|
|
|
n = val;
|
|
|
|
} else
|
|
|
|
n = 1;
|
|
|
|
if (n < 0) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", arg, "bad number");
|
2005-05-23 05:06:10 +02:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
if (l->argc < n) {
|
|
|
|
bi_errorf("nothing to shift");
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
l->argv[n] = l->argv[0];
|
|
|
|
l->argv += n;
|
|
|
|
l->argc -= n;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_umask(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int i, optc;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *cp;
|
2008-04-02 00:20:20 +02:00
|
|
|
bool symbolic = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
mode_t old_umask;
|
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'S':
|
2008-04-01 23:50:58 +02:00
|
|
|
symbolic = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
cp = wp[builtin_opt.optind];
|
|
|
|
if (cp == NULL) {
|
2009-08-08 15:08:53 +02:00
|
|
|
old_umask = umask((mode_t)0);
|
2005-05-23 05:06:10 +02:00
|
|
|
umask(old_umask);
|
|
|
|
if (symbolic) {
|
2007-03-04 01:13:17 +01:00
|
|
|
char buf[18], *p;
|
2005-05-23 05:06:10 +02:00
|
|
|
int j;
|
|
|
|
|
|
|
|
old_umask = ~old_umask;
|
2007-03-04 01:13:17 +01:00
|
|
|
p = buf;
|
2005-05-23 05:06:10 +02:00
|
|
|
for (i = 0; i < 3; i++) {
|
2007-03-04 01:13:17 +01:00
|
|
|
*p++ = "ugo"[i];
|
|
|
|
*p++ = '=';
|
2005-05-23 05:06:10 +02:00
|
|
|
for (j = 0; j < 3; j++)
|
|
|
|
if (old_umask & (1 << (8 - (3*i + j))))
|
2007-03-04 01:13:17 +01:00
|
|
|
*p++ = "rwx"[j];
|
|
|
|
*p++ = ',';
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2007-03-04 01:13:17 +01:00
|
|
|
p[-1] = '\0';
|
2005-05-23 05:06:10 +02:00
|
|
|
shprintf("%s\n", buf);
|
|
|
|
} else
|
• more unsigned → unsigned int
• more int → bool
• more regression tests: check if the utf8-hack flag is really disabled
at non-interactive startup, enabled at interactive startup, if the
current locale is a UTF-8 one
• make the mksh-local multibyte handling functions globally accessible,
change their names, syntax and semantics a little (XXX more work needed)
• optimise
• utf_wctomb: src → dst, as we’re writing to that char array (pasto?)
• edit.c:x_e_getmbc(): if the second byte of a 2- or 3-byte multibyte
sequence is invalid utf-8, ungetc it (not possible for the 3rd byte yet)
• edit.c:x_zotc3(): easier (and faster) handling of UTF-8
• implement, document and test for base-1 numbers: they just get the
ASCII (8-bit) or Unicode (UTF-8) value of the octet(s) after the ‘1#’,
or do the same as print \x## or \u#### (depending on the utf8-hack flag),
plus support the PUA assignment of EF80‥EFFF for the MirBSD encoding “hack”
(print doesn’t, as it has \x## and \u#### to distinguish, but we cannot use
base-0 numbers which I had planned to use for raw octets first, as they are
used internally): http://thread.gmane.org/gmane.os.miros.general/7938
• as an application example, add a hexdumper to the regression tests ☺
2008-04-20 00:15:06 +02:00
|
|
|
shprintf("%#3.3o\n", (unsigned int)old_umask);
|
2005-05-23 05:06:10 +02:00
|
|
|
} else {
|
|
|
|
mode_t new_umask;
|
|
|
|
|
2006-11-10 08:52:04 +01:00
|
|
|
if (ksh_isdigit(*cp)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
|
|
|
|
new_umask = new_umask * 8 + (*cp - '0');
|
|
|
|
if (*cp) {
|
|
|
|
bi_errorf("bad number");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* symbolic format */
|
|
|
|
int positions, new_val;
|
|
|
|
char op;
|
|
|
|
|
2009-08-08 15:08:53 +02:00
|
|
|
old_umask = umask((mode_t)0);
|
2005-05-23 05:06:10 +02:00
|
|
|
umask(old_umask); /* in case of error */
|
|
|
|
old_umask = ~old_umask;
|
|
|
|
new_umask = old_umask;
|
|
|
|
positions = 0;
|
|
|
|
while (*cp) {
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
while (*cp && vstrchr("augo", *cp))
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (*cp++) {
|
|
|
|
case 'a':
|
|
|
|
positions |= 0111;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
positions |= 0100;
|
|
|
|
break;
|
|
|
|
case 'g':
|
|
|
|
positions |= 0010;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
positions |= 0001;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!positions)
|
|
|
|
positions = 0111; /* default is a */
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
if (!vstrchr("=+-", op = *cp))
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
cp++;
|
|
|
|
new_val = 0;
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
while (*cp && vstrchr("rwxugoXs", *cp))
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (*cp++) {
|
|
|
|
case 'r': new_val |= 04; break;
|
|
|
|
case 'w': new_val |= 02; break;
|
|
|
|
case 'x': new_val |= 01; break;
|
2009-06-10 20:12:51 +02:00
|
|
|
case 'u':
|
|
|
|
new_val |= old_umask >> 6;
|
|
|
|
break;
|
|
|
|
case 'g':
|
|
|
|
new_val |= old_umask >> 3;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
new_val |= old_umask >> 0;
|
|
|
|
break;
|
|
|
|
case 'X':
|
|
|
|
if (old_umask & 0111)
|
2005-05-23 05:06:10 +02:00
|
|
|
new_val |= 01;
|
2009-06-10 20:12:51 +02:00
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
/* ignored */
|
|
|
|
break;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
new_val = (new_val & 07) * positions;
|
|
|
|
switch (op) {
|
|
|
|
case '-':
|
|
|
|
new_umask &= ~new_val;
|
|
|
|
break;
|
|
|
|
case '=':
|
|
|
|
new_umask = new_val |
|
|
|
|
(new_umask & ~(positions * 07));
|
|
|
|
break;
|
|
|
|
case '+':
|
|
|
|
new_umask |= new_val;
|
|
|
|
}
|
|
|
|
if (*cp == ',') {
|
|
|
|
positions = 0;
|
|
|
|
cp++;
|
• remove strcasestr.c, use home-grown implementation¹, call it stricmp,
and have it return an API-correct const char *
• enhance and stylify comments
• a little KNF and simplifications
• #ifdef DEBUG: replace strchr and strstr with ucstrchr and ucstrstr
that take and return a non-const char *, and fix the violations
• new cstrchr, cstrstr (take and give const char *)
• new vstrchr, vstrstr (take const or not, give boolean value)
• new afreechk(x) = afreechv(x,x) = if (x1) afree(x2, ATEMP)
• new ksh_isdash(str) = (str != NULL) && !strcmp(str, "-")
• replace the only use of strrchr with inlined code to shrink
• minor man page fixes
• Minix 3 signames are autogenerated with gcc
• rename strlfun.c to strlcpy.c since we don't do strlcat(3) anyway,
only strlcpy(3), and shorten it
• dot.mkshrc: move MKSH=… down to the export line
to not disturb the PS1 visual impression ☺
• dot.mkshrc: Lstripcom(): optimise
• bump version
¹) side effect from creating API-correct cstrchr, cstrstr, etc.
uses goto so it must be better ☻
tested on mirbsd-current via both Makefile and Build.sh
2007-03-04 04:04:28 +01:00
|
|
|
} else if (!vstrchr("=+-", *cp))
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*cp) {
|
|
|
|
bi_errorf("bad mask");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
new_umask = ~new_umask;
|
|
|
|
}
|
|
|
|
umask(new_umask);
|
|
|
|
}
|
2008-02-24 16:57:20 +01:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_dot(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
const char *file, *cp, **argv;
|
|
|
|
int argc, i, errcode;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2008-02-24 16:57:20 +01:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-02-24 16:57:20 +01:00
|
|
|
if ((cp = wp[builtin_opt.optind]) == NULL) {
|
|
|
|
bi_errorf("missing argument");
|
|
|
|
return (1);
|
|
|
|
}
|
2008-04-02 00:20:20 +02:00
|
|
|
if ((file = search(cp, path, R_OK, &errcode)) == NULL) {
|
|
|
|
bi_errorf("%s: %s", cp,
|
|
|
|
errcode ? strerror(errcode) : "not found");
|
2008-02-24 16:57:20 +01:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set positional parameters? */
|
|
|
|
if (wp[builtin_opt.optind + 1]) {
|
|
|
|
argv = wp + builtin_opt.optind;
|
|
|
|
argv[0] = e->loc->argv[0]; /* preserve $0 */
|
|
|
|
for (argc = 0; argv[argc + 1]; argc++)
|
|
|
|
;
|
|
|
|
} else {
|
|
|
|
argc = 0;
|
|
|
|
argv = NULL;
|
|
|
|
}
|
2008-04-02 00:20:20 +02:00
|
|
|
if ((i = include(file, argc, argv, 0)) < 0) {
|
|
|
|
/* should not happen */
|
2005-05-23 05:06:10 +02:00
|
|
|
bi_errorf("%s: %s", cp, strerror(errno));
|
2008-02-24 16:57:20 +01:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-02-24 16:57:20 +01:00
|
|
|
return (i);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_wait(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv = 0, sig;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
wp += builtin_opt.optind;
|
|
|
|
if (*wp == NULL) {
|
|
|
|
while (waitfor(NULL, &sig) >= 0)
|
|
|
|
;
|
|
|
|
rv = sig;
|
|
|
|
} else {
|
|
|
|
for (; *wp; wp++)
|
|
|
|
rv = waitfor(*wp, &sig);
|
|
|
|
if (rv < 0)
|
|
|
|
rv = sig ? sig : 127; /* magic exit code: bad job-id */
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_read(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int c = 0, ecode = 0, fd = 0, optc;
|
|
|
|
bool expande = true, historyr = false, expanding;
|
|
|
|
const char *cp, *emsg;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct shf *shf;
|
2005-09-12 21:28:18 +02:00
|
|
|
XString cs, xs = { NULL, NULL, 0, NULL};
|
2005-05-23 05:06:10 +02:00
|
|
|
struct tbl *vp;
|
2008-04-02 00:20:20 +02:00
|
|
|
char *ccp, *xp = NULL, *wpalloc = NULL;
|
2005-05-23 05:06:10 +02:00
|
|
|
static char REPLY[] = "REPLY";
|
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'p':
|
|
|
|
if ((fd = coproc_getfd(R_OK, &emsg)) < 0) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", "-p", emsg);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'r':
|
2008-04-02 00:20:20 +02:00
|
|
|
expande = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 's':
|
2008-04-02 00:20:20 +02:00
|
|
|
historyr = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
if (!*(cp = builtin_opt.optarg))
|
|
|
|
fd = 0;
|
|
|
|
else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s: %s", "-u", cp, emsg);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '?':
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
if (*wp == NULL)
|
|
|
|
*--wp = REPLY;
|
|
|
|
|
|
|
|
/* Since we can't necessarily seek backwards on non-regular files,
|
|
|
|
* don't buffer them so we can't read too much.
|
|
|
|
*/
|
|
|
|
shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare);
|
|
|
|
|
2007-03-04 01:13:17 +01:00
|
|
|
if ((cp = cstrchr(*wp, '?')) != NULL) {
|
2008-10-28 15:32:43 +01:00
|
|
|
strdupx(wpalloc, *wp, ATEMP);
|
2007-03-04 01:13:17 +01:00
|
|
|
wpalloc[cp - *wp] = '\0';
|
|
|
|
*wp = wpalloc;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (isatty(fd)) {
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh says it prints prompt on fd if it's open
|
2005-05-23 05:06:10 +02:00
|
|
|
* for writing and is a tty, but it doesn't do it
|
|
|
|
* (it also doesn't check the interactive flag,
|
|
|
|
* as is indicated in the Kornshell book).
|
|
|
|
*/
|
2010-09-15 23:08:19 +02:00
|
|
|
shellf("%s", cp+1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we are reading from the co-process for the first time,
|
2009-06-10 20:12:51 +02:00
|
|
|
* make sure the other side of the pipe is closed first. This allows
|
2005-05-23 05:06:10 +02:00
|
|
|
* the detection of eof.
|
|
|
|
*
|
2009-06-11 14:42:21 +02:00
|
|
|
* This is not compatible with AT&T ksh... the fd is kept so another
|
2005-05-23 05:06:10 +02:00
|
|
|
* coproc can be started with same output, however, this means eof
|
2009-06-10 20:12:51 +02:00
|
|
|
* can't be detected... This is why it is closed here.
|
2005-05-23 05:06:10 +02:00
|
|
|
* If this call is removed, remove the eof check below, too.
|
|
|
|
* coproc_readw_close(fd);
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (historyr)
|
|
|
|
Xinit(xs, xp, 128, ATEMP);
|
2008-04-02 00:20:20 +02:00
|
|
|
expanding = false;
|
2007-03-04 01:13:17 +01:00
|
|
|
Xinit(cs, ccp, 128, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
for (; *wp != NULL; wp++) {
|
2007-03-04 01:13:17 +01:00
|
|
|
for (ccp = Xstring(cs, ccp); ; ) {
|
2005-05-23 05:06:10 +02:00
|
|
|
if (c == '\n' || c == EOF)
|
|
|
|
break;
|
|
|
|
while (1) {
|
|
|
|
c = shf_getc(shf);
|
|
|
|
if (c == '\0')
|
|
|
|
continue;
|
|
|
|
if (c == EOF && shf_error(shf) &&
|
|
|
|
shf_errno(shf) == EINTR) {
|
|
|
|
/* Was the offending signal one that
|
|
|
|
* would normally kill a process?
|
|
|
|
* If so, pretend the read was killed.
|
|
|
|
*/
|
|
|
|
ecode = fatal_trap_check();
|
|
|
|
|
|
|
|
/* non fatal (eg, CHLD), carry on */
|
|
|
|
if (!ecode) {
|
|
|
|
shf_clearerr(shf);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (historyr) {
|
|
|
|
Xcheck(xs, xp);
|
|
|
|
Xput(xs, xp, c);
|
|
|
|
}
|
2007-03-04 01:13:17 +01:00
|
|
|
Xcheck(cs, ccp);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (expanding) {
|
2008-04-02 00:20:20 +02:00
|
|
|
expanding = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (c == '\n') {
|
|
|
|
c = 0;
|
|
|
|
if (Flag(FTALKING_I) && isatty(fd)) {
|
|
|
|
/* set prompt in case this is
|
|
|
|
* called from .profile or $ENV
|
|
|
|
*/
|
|
|
|
set_prompt(PS2, NULL);
|
|
|
|
pprompt(prompt, 0);
|
|
|
|
}
|
|
|
|
} else if (c != EOF)
|
2007-03-04 01:13:17 +01:00
|
|
|
Xput(cs, ccp, c);
|
2005-05-23 05:06:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (expande && c == '\\') {
|
2008-04-02 00:20:20 +02:00
|
|
|
expanding = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (c == '\n' || c == EOF)
|
|
|
|
break;
|
|
|
|
if (ctype(c, C_IFS)) {
|
2007-03-04 01:13:17 +01:00
|
|
|
if (Xlength(cs, ccp) == 0 && ctype(c, C_IFSWS))
|
2005-05-23 05:06:10 +02:00
|
|
|
continue;
|
|
|
|
if (wp[1])
|
|
|
|
break;
|
|
|
|
}
|
2007-03-04 01:13:17 +01:00
|
|
|
Xput(cs, ccp, c);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
/* strip trailing IFS white space from last variable */
|
|
|
|
if (!wp[1])
|
2007-03-04 01:13:17 +01:00
|
|
|
while (Xlength(cs, ccp) && ctype(ccp[-1], C_IFS) &&
|
|
|
|
ctype(ccp[-1], C_IFSWS))
|
|
|
|
ccp--;
|
|
|
|
Xput(cs, ccp, '\0');
|
2005-05-23 05:06:10 +02:00
|
|
|
vp = global(*wp);
|
|
|
|
/* Must be done before setting export. */
|
|
|
|
if (vp->flag & RDONLY) {
|
|
|
|
shf_flush(shf);
|
2010-08-28 22:22:24 +02:00
|
|
|
bi_errorf("%s: %s", *wp, "is read only");
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(wpalloc, ATEMP);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (Flag(FEXPORT))
|
|
|
|
typeset(*wp, EXPORT, 0, 0, 0);
|
2007-03-04 01:13:17 +01:00
|
|
|
if (!setstr(vp, Xstring(cs, ccp), KSH_RETURN_ERROR)) {
|
|
|
|
shf_flush(shf);
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(wpalloc, ATEMP);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
shf_flush(shf);
|
|
|
|
if (historyr) {
|
|
|
|
Xput(xs, xp, '\0');
|
2008-09-30 21:25:51 +02:00
|
|
|
histsave(&source->line, Xstring(xs, xp), true, false);
|
2005-05-23 05:06:10 +02:00
|
|
|
Xfree(xs, xp);
|
|
|
|
}
|
|
|
|
/* if this is the co-process fd, close the file descriptor
|
|
|
|
* (can get eof if and only if all processes are have died, ie,
|
|
|
|
* coproc.njobs is 0 and the pipe is closed).
|
|
|
|
*/
|
|
|
|
if (c == EOF && !ecode)
|
|
|
|
coproc_read_close(fd);
|
|
|
|
|
2008-05-17 20:47:03 +02:00
|
|
|
afree(wpalloc, ATEMP);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (ecode ? ecode : c == EOF);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_eval(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2005-05-25 11:39:49 +02:00
|
|
|
struct source *s, *saves = source;
|
2009-08-08 15:08:53 +02:00
|
|
|
unsigned char savef;
|
2007-06-07 01:28:17 +02:00
|
|
|
int rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
s = pushs(SWORDS, ATEMP);
|
|
|
|
s->u.strv = wp + builtin_opt.optind;
|
2005-07-04 14:27:28 +02:00
|
|
|
|
2009-10-15 18:36:27 +02:00
|
|
|
/*-
|
|
|
|
* The following code handles the case where the command is
|
|
|
|
* empty due to failed command substitution, for example by
|
|
|
|
* eval "$(false)"
|
|
|
|
* This has historically returned 1 by AT&T ksh88. In this
|
|
|
|
* case, shell() will not set or change exstat because the
|
|
|
|
* compiled tree is empty, so it will use the value we pass
|
|
|
|
* from subst_exstat, which is cleared in execute(), so it
|
|
|
|
* should have been 0 if there were no substitutions.
|
2005-07-04 14:27:28 +02:00
|
|
|
*
|
2009-10-15 18:36:27 +02:00
|
|
|
* POSIX however says we don't do this, even though it is
|
|
|
|
* traditionally done. AT&T ksh93 agrees with POSIX, so we
|
|
|
|
* do. The following is an excerpt from SUSv4 [1003.2-2008]:
|
2009-06-10 20:12:51 +02:00
|
|
|
*
|
2009-10-15 18:36:27 +02:00
|
|
|
* 2.9.1: Simple Commands
|
2005-07-04 14:27:28 +02:00
|
|
|
* ... If there is a command name, execution shall
|
2009-10-15 18:36:27 +02:00
|
|
|
* continue as described in 2.9.1.1 [Command Search
|
|
|
|
* and Execution]. If there is no command name, but
|
|
|
|
* the command contained a command substitution, the
|
|
|
|
* command shall complete with the exit status of the
|
|
|
|
* last command substitution performed.
|
|
|
|
* 2.9.1.1: Command Search and Execution
|
|
|
|
* (1) a. If the command name matches the name of a
|
|
|
|
* special built-in utility, that special built-in
|
2005-07-04 14:27:28 +02:00
|
|
|
* utility shall be invoked.
|
2009-10-15 18:36:27 +02:00
|
|
|
* 2.14.5: eval
|
|
|
|
* If there are no arguments, or only null arguments,
|
|
|
|
* eval shall return a zero exit status; ...
|
2005-07-04 14:27:28 +02:00
|
|
|
*/
|
2009-10-15 18:36:27 +02:00
|
|
|
/* exstat = subst_exstat; */ /* AT&T ksh88 */
|
|
|
|
exstat = 0; /* SUSv4 */
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2005-05-25 11:39:49 +02:00
|
|
|
savef = Flag(FERREXIT);
|
|
|
|
Flag(FERREXIT) = 0;
|
|
|
|
rv = shell(s, false);
|
|
|
|
Flag(FERREXIT) = savef;
|
|
|
|
source = saves;
|
2007-09-09 20:06:42 +02:00
|
|
|
afree(s, ATEMP);
|
2005-05-25 11:39:49 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_trap(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
int i;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *s;
|
2005-05-23 05:06:10 +02:00
|
|
|
Trap *p;
|
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
wp += builtin_opt.optind;
|
|
|
|
|
|
|
|
if (*wp == NULL) {
|
2007-06-07 01:28:17 +02:00
|
|
|
for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
|
|
|
|
if (p->trap != NULL) {
|
2008-04-01 23:50:58 +02:00
|
|
|
shf_puts("trap -- ", shl_stdout);
|
2005-05-23 05:06:10 +02:00
|
|
|
print_value_quoted(p->trap);
|
|
|
|
shprintf(" %s\n", p->name);
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use case sensitive lookup for first arg so the
|
|
|
|
* command 'exit' isn't confused with the pseudo-signal
|
|
|
|
* 'EXIT'.
|
|
|
|
*/
|
|
|
|
s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL; /* get command */
|
|
|
|
if (s != NULL && s[0] == '-' && s[1] == '\0')
|
|
|
|
s = NULL;
|
|
|
|
|
|
|
|
/* set/clear traps */
|
|
|
|
while (*wp != NULL) {
|
|
|
|
p = gettrap(*wp++, true);
|
|
|
|
if (p == NULL) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("bad signal '%s'", wp[-1]);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
settrap(p, s);
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_exitreturn(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int n, how = LEXIT;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *arg;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
arg = wp[builtin_opt.optind];
|
|
|
|
|
|
|
|
if (arg) {
|
|
|
|
if (!getn(arg, &n)) {
|
|
|
|
exstat = 1;
|
2010-08-28 20:50:58 +02:00
|
|
|
warningf(true, "%s: %s", arg, "bad number");
|
2005-05-23 05:06:10 +02:00
|
|
|
} else
|
|
|
|
exstat = n;
|
2010-11-01 18:29:05 +01:00
|
|
|
} else if (trap_exstat != -1)
|
|
|
|
exstat = trap_exstat;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (wp[0][0] == 'r') { /* return */
|
|
|
|
struct env *ep;
|
|
|
|
|
|
|
|
/* need to tell if this is exit or return so trap exit will
|
|
|
|
* work right (POSIX)
|
|
|
|
*/
|
|
|
|
for (ep = e; ep; ep = ep->oenv)
|
|
|
|
if (STOP_RETURN(ep->type)) {
|
|
|
|
how = LRETURN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (how == LEXIT && !really_exit && j_stopped_running()) {
|
|
|
|
really_exit = 1;
|
|
|
|
how = LSHELL;
|
|
|
|
}
|
|
|
|
|
|
|
|
quitenv(NULL); /* get rid of any i/o redirections */
|
|
|
|
unwind(how);
|
2006-05-10 20:54:13 +02:00
|
|
|
/* NOTREACHED */
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_brkcont(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
int n, quit;
|
|
|
|
struct env *ep, *last_ep = NULL;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *arg;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
arg = wp[builtin_opt.optind];
|
|
|
|
|
|
|
|
if (!arg)
|
|
|
|
n = 1;
|
|
|
|
else if (!bi_getn(arg, &n))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
quit = n;
|
|
|
|
if (quit <= 0) {
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh does this for non-interactive shells only - weird */
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", arg, "bad value");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
|
|
|
|
for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
|
|
|
|
if (ep->type == E_LOOP) {
|
|
|
|
if (--quit == 0)
|
|
|
|
break;
|
|
|
|
ep->flags |= EF_BRKCONT_PASS;
|
|
|
|
last_ep = ep;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (quit) {
|
2009-06-11 14:42:21 +02:00
|
|
|
/* AT&T ksh doesn't print a message - just does what it
|
2009-06-10 20:12:51 +02:00
|
|
|
* can. We print a message 'cause it helps in debugging
|
2005-05-23 05:06:10 +02:00
|
|
|
* scripts, but don't generate an error (ie, keep going).
|
|
|
|
*/
|
|
|
|
if (n == quit) {
|
2010-08-28 22:22:24 +02:00
|
|
|
warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
/* POSIX says if n is too big, the last enclosing loop
|
2009-06-10 20:12:51 +02:00
|
|
|
* shall be used. Doesn't say to print an error but we
|
2005-05-23 05:06:10 +02:00
|
|
|
* do anyway 'cause the user messed up.
|
|
|
|
*/
|
2007-05-13 20:15:25 +02:00
|
|
|
if (last_ep)
|
|
|
|
last_ep->flags &= ~EF_BRKCONT_PASS;
|
2005-05-23 05:06:10 +02:00
|
|
|
warningf(true, "%s: can only %s %d level(s)",
|
|
|
|
wp[0], wp[0], n - quit);
|
|
|
|
}
|
|
|
|
|
|
|
|
unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
|
2006-05-10 20:54:13 +02:00
|
|
|
/* NOTREACHED */
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_set(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2009-12-05 18:43:50 +01:00
|
|
|
int argi;
|
|
|
|
bool setargs;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct block *l = e->loc;
|
2007-07-01 23:10:29 +02:00
|
|
|
const char **owp;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (wp[1] == NULL) {
|
2010-08-28 20:50:58 +02:00
|
|
|
static const char *args[] = { T_set, "-", NULL };
|
2009-06-08 22:06:50 +02:00
|
|
|
return (c_typeset(args));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
argi = parse_args(wp, OF_SET, &setargs);
|
|
|
|
if (argi < 0)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
/* set $# and $* */
|
|
|
|
if (setargs) {
|
2009-12-05 18:43:50 +01:00
|
|
|
wp += argi - 1;
|
|
|
|
owp = wp;
|
2005-05-23 05:06:10 +02:00
|
|
|
wp[0] = l->argv[0]; /* save $0 */
|
|
|
|
while (*++wp != NULL)
|
2008-12-13 18:02:18 +01:00
|
|
|
strdupx(*wp, *wp, &l->area);
|
2005-05-23 05:06:10 +02:00
|
|
|
l->argc = wp - owp - 1;
|
2010-09-14 23:26:19 +02:00
|
|
|
l->argv = alloc2(l->argc + 2, sizeof(char *), &l->area);
|
2005-05-23 05:06:10 +02:00
|
|
|
for (wp = l->argv; (*wp++ = *owp++) != NULL; )
|
|
|
|
;
|
|
|
|
}
|
2009-10-15 18:36:27 +02:00
|
|
|
/*-
|
2009-10-15 16:58:51 +02:00
|
|
|
* POSIX says set exit status is 0, but old scripts that use
|
2009-10-15 18:36:27 +02:00
|
|
|
* getopt(1) use the construct
|
|
|
|
* set -- $(getopt ab:c "$@")
|
2005-05-23 05:06:10 +02:00
|
|
|
* which assumes the exit value set will be that of the $()
|
|
|
|
* (subst_exstat is cleared in execute() so that it will be 0
|
|
|
|
* if there are no command substitutions).
|
2010-01-28 16:18:51 +01:00
|
|
|
* Switched ksh (!posix !sh) to POSIX in mksh R39b.
|
2005-05-23 05:06:10 +02:00
|
|
|
*/
|
2010-01-28 16:18:51 +01:00
|
|
|
return (Flag(FSH) ? subst_exstat : 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_unset(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2007-03-04 01:13:17 +01:00
|
|
|
const char *id;
|
2007-09-09 20:06:42 +02:00
|
|
|
int optc;
|
|
|
|
bool unset_var = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'f':
|
2007-09-09 20:06:42 +02:00
|
|
|
unset_var = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case 'v':
|
2007-09-09 20:06:42 +02:00
|
|
|
unset_var = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case '?':
|
2007-09-09 20:06:42 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
wp += builtin_opt.optind;
|
|
|
|
for (; (id = *wp) != NULL; wp++)
|
|
|
|
if (unset_var) { /* unset variable */
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
struct tbl *vp;
|
|
|
|
char *cp = NULL;
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
n = strlen(id);
|
|
|
|
if (n > 3 && id[n-3] == '[' && id[n-2] == '*' &&
|
|
|
|
id[n-1] == ']') {
|
|
|
|
strndupx(cp, id, n - 3, ATEMP);
|
|
|
|
id = cp;
|
|
|
|
optc = 3;
|
|
|
|
} else
|
|
|
|
optc = vstrchr(id, '[') ? 0 : 1;
|
|
|
|
|
|
|
|
vp = global(id);
|
|
|
|
afree(cp, ATEMP);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if ((vp->flag&RDONLY)) {
|
2010-08-28 22:22:24 +02:00
|
|
|
bi_errorf("%s: %s", vp->name, "is read only");
|
2007-09-09 20:06:42 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
Add “unset foo[*]” mksh extension, which allows to unset the *contents*
of foo[0] (but not its attributes), and the rest of the array, so that
later “set +A foo bar” will set foo[0]=bar but retain the attributes.
This is important, because, in the future, arrays will have different
attributes per element, instead of all the same (which, actually, is
not entirely true right now either, since “unset foo[0]” will not mo-
dify the attributes of a foo[1] existing at that point in time), where
foo[$newkey] will inherit from foo[0], but typeset foo will only affect
foo[0] no longer foo[*] in the future. (The rules about typeset=local
will still apply, as they affect creation of variables in a scope.)
2010-01-25 15:11:29 +01:00
|
|
|
unset(vp, optc);
|
2007-09-09 20:06:42 +02:00
|
|
|
} else /* unset function */
|
|
|
|
define(id, NULL);
|
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(struct shf *shf, bool posix, long tv_sec, int tv_usec, int width,
|
2005-05-23 05:06:10 +02:00
|
|
|
const char *prefix, const char *suffix)
|
|
|
|
{
|
2009-05-20 12:10:02 +02:00
|
|
|
tv_usec /= 10000;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (posix)
|
2009-05-20 12:10:02 +02:00
|
|
|
shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width,
|
|
|
|
tv_sec, tv_usec, suffix);
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2009-05-20 12:10:02 +02:00
|
|
|
shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width,
|
|
|
|
tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-12-12 23:27:10 +01:00
|
|
|
c_times(const char **wp MKSH_A_UNUSED)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
struct rusage usage;
|
|
|
|
|
2009-04-03 11:39:07 +02:00
|
|
|
getrusage(RUSAGE_SELF, &usage);
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_stdout, false, usage.ru_utime.tv_sec,
|
|
|
|
usage.ru_utime.tv_usec, 0, null, " ");
|
|
|
|
p_time(shl_stdout, false, usage.ru_stime.tv_sec,
|
|
|
|
usage.ru_stime.tv_usec, 0, null, "\n");
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-04-03 11:39:07 +02:00
|
|
|
getrusage(RUSAGE_CHILDREN, &usage);
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_stdout, false, usage.ru_utime.tv_sec,
|
|
|
|
usage.ru_utime.tv_usec, 0, null, " ");
|
|
|
|
p_time(shl_stdout, false, usage.ru_stime.tv_sec,
|
|
|
|
usage.ru_stime.tv_usec, 0, null, "\n");
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* time pipeline (really a statement, not a built-in command)
|
|
|
|
*/
|
|
|
|
int
|
2009-03-22 18:47:38 +01:00
|
|
|
timex(struct op *t, int f, volatile int *xerrok)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
#define TF_NOARGS BIT(0)
|
|
|
|
#define TF_NOREAL BIT(1) /* don't report real time */
|
2009-10-02 20:08:37 +02:00
|
|
|
#define TF_POSIX BIT(2) /* report in POSIX format */
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv = 0, tf = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct rusage ru0, ru1, cru0, cru1;
|
|
|
|
struct timeval usrtime, systime, tv0, tv1;
|
|
|
|
|
|
|
|
gettimeofday(&tv0, NULL);
|
|
|
|
getrusage(RUSAGE_SELF, &ru0);
|
|
|
|
getrusage(RUSAGE_CHILDREN, &cru0);
|
|
|
|
if (t->left) {
|
|
|
|
/*
|
|
|
|
* Two ways of getting cpu usage of a command: just use t0
|
|
|
|
* and t1 (which will get cpu usage from other jobs that
|
|
|
|
* finish while we are executing t->left), or get the
|
2009-06-11 14:42:21 +02:00
|
|
|
* cpu usage of t->left. AT&T ksh does the former, while
|
2005-05-23 05:06:10 +02:00
|
|
|
* pdksh tries to do the later (the j_usrtime hack doesn't
|
|
|
|
* really work as it only counts the last job).
|
|
|
|
*/
|
|
|
|
timerclear(&j_usrtime);
|
|
|
|
timerclear(&j_systime);
|
2009-03-22 18:47:38 +01:00
|
|
|
rv = execute(t->left, f | XTIME, xerrok);
|
2008-08-02 19:45:12 +02:00
|
|
|
if (t->left->type == TCOM)
|
|
|
|
tf |= t->left->str[0];
|
2005-05-23 05:06:10 +02:00
|
|
|
gettimeofday(&tv1, NULL);
|
|
|
|
getrusage(RUSAGE_SELF, &ru1);
|
|
|
|
getrusage(RUSAGE_CHILDREN, &cru1);
|
|
|
|
} else
|
|
|
|
tf = TF_NOARGS;
|
|
|
|
|
|
|
|
if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */
|
|
|
|
tf |= TF_NOREAL;
|
|
|
|
timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
|
|
|
|
timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
|
|
|
|
} else {
|
|
|
|
timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime);
|
|
|
|
timeradd(&usrtime, &j_usrtime, &usrtime);
|
|
|
|
timersub(&ru1.ru_stime, &ru0.ru_stime, &systime);
|
|
|
|
timeradd(&systime, &j_systime, &systime);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(tf & TF_NOREAL)) {
|
|
|
|
timersub(&tv1, &tv0, &tv1);
|
|
|
|
if (tf & TF_POSIX)
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
|
|
|
|
5, "real ", "\n");
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
|
|
|
|
5, null, " real ");
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if (tf & TF_POSIX)
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
|
|
|
|
5, "user ", "\n");
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
|
|
|
|
5, null, " user ");
|
2005-05-23 05:06:10 +02:00
|
|
|
if (tf & TF_POSIX)
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
|
|
|
|
5, "sys ", "\n");
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2009-05-20 12:10:02 +02:00
|
|
|
p_time(shl_out, false, systime.tv_sec, systime.tv_usec,
|
|
|
|
5, null, " system\n");
|
2005-05-23 05:06:10 +02:00
|
|
|
shf_flush(shl_out);
|
|
|
|
|
2006-08-24 22:32:53 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
timex_hook(struct op *t, char **volatile *app)
|
|
|
|
{
|
|
|
|
char **wp = *app;
|
2008-04-02 00:20:20 +02:00
|
|
|
int optc, i, j;
|
2005-05-23 05:06:10 +02:00
|
|
|
Getopt opt;
|
|
|
|
|
|
|
|
ksh_getopt_reset(&opt, 0);
|
|
|
|
opt.optind = 0; /* start at the start */
|
2007-03-04 01:13:17 +01:00
|
|
|
while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'p':
|
|
|
|
t->str[0] |= TF_POSIX;
|
|
|
|
break;
|
|
|
|
case '?':
|
2010-08-28 20:50:58 +02:00
|
|
|
errorf("time: -%s %s", opt.optarg,
|
|
|
|
"unknown option");
|
2005-05-23 05:06:10 +02:00
|
|
|
case ':':
|
2010-08-28 20:50:58 +02:00
|
|
|
errorf("time: -%s %s", opt.optarg,
|
|
|
|
"requires an argument");
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
/* Copy command words down over options. */
|
|
|
|
if (opt.optind != 0) {
|
|
|
|
for (i = 0; i < opt.optind; i++)
|
|
|
|
afree(wp[i], ATEMP);
|
|
|
|
for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
if (!wp[0])
|
|
|
|
t->str[0] |= TF_NOARGS;
|
|
|
|
*app = wp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* exec with no args - args case is taken care of in comexec() */
|
|
|
|
int
|
2009-12-12 23:27:10 +01:00
|
|
|
c_exec(const char **wp MKSH_A_UNUSED)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* make sure redirects stay in place */
|
|
|
|
if (e->savefd != NULL) {
|
|
|
|
for (i = 0; i < NUFILE; i++) {
|
|
|
|
if (e->savefd[i] > 0)
|
|
|
|
close(e->savefd[i]);
|
2010-01-28 16:18:51 +01:00
|
|
|
/*
|
|
|
|
* keep all file descriptors > 2 private for ksh,
|
|
|
|
* but not for POSIX or legacy/kludge sh
|
|
|
|
*/
|
|
|
|
if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
|
|
|
|
e->savefd[i])
|
2005-05-23 05:06:10 +02:00
|
|
|
fcntl(i, F_SETFD, FD_CLOEXEC);
|
|
|
|
}
|
|
|
|
e->savefd = NULL;
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2007-07-31 13:11:25 +02:00
|
|
|
#if HAVE_MKNOD
|
2007-08-20 00:06:27 +02:00
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_mknod(const char **wp)
|
2005-10-21 14:41:56 +02:00
|
|
|
{
|
|
|
|
int argc, optc, rv = 0;
|
|
|
|
bool ismkfifo = false;
|
2007-03-04 01:13:17 +01:00
|
|
|
const char **argv;
|
2005-10-21 14:41:56 +02:00
|
|
|
void *set = NULL;
|
|
|
|
mode_t mode = 0, oldmode = 0;
|
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, "m:")) != -1) {
|
2005-10-21 14:41:56 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'm':
|
|
|
|
set = setmode(builtin_opt.optarg);
|
|
|
|
if (set == NULL) {
|
|
|
|
bi_errorf("invalid file mode");
|
|
|
|
return (1);
|
|
|
|
}
|
2009-08-08 15:08:53 +02:00
|
|
|
mode = getmode(set, (mode_t)(DEFFILEMODE));
|
2005-10-21 14:41:56 +02:00
|
|
|
free(set);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto c_mknod_usage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
argv = &wp[builtin_opt.optind];
|
2009-05-16 19:33:10 +02:00
|
|
|
if (argv[0] == NULL)
|
2005-10-21 14:41:56 +02:00
|
|
|
goto c_mknod_usage;
|
|
|
|
for (argc = 0; argv[argc]; argc++)
|
|
|
|
;
|
|
|
|
if (argc == 2 && argv[1][0] == 'p')
|
|
|
|
ismkfifo = true;
|
|
|
|
else if (argc != 4 || (argv[1][0] != 'b' && argv[1][0] != 'c'))
|
|
|
|
goto c_mknod_usage;
|
|
|
|
|
|
|
|
if (set != NULL)
|
2009-08-08 15:08:53 +02:00
|
|
|
oldmode = umask((mode_t)0);
|
2005-10-21 14:41:56 +02:00
|
|
|
else
|
|
|
|
mode = DEFFILEMODE;
|
|
|
|
|
|
|
|
mode |= (argv[1][0] == 'b') ? S_IFBLK :
|
|
|
|
(argv[1][0] == 'c') ? S_IFCHR : 0;
|
|
|
|
|
|
|
|
if (!ismkfifo) {
|
2007-06-05 21:18:11 +02:00
|
|
|
unsigned long majnum, minnum;
|
2005-10-21 14:41:56 +02:00
|
|
|
dev_t dv;
|
|
|
|
char *c;
|
|
|
|
|
2007-06-05 21:18:11 +02:00
|
|
|
majnum = strtoul(argv[2], &c, 0);
|
2005-10-21 14:41:56 +02:00
|
|
|
if ((c == argv[2]) || (*c != '\0')) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
|
2005-10-21 14:41:56 +02:00
|
|
|
goto c_mknod_err;
|
|
|
|
}
|
2007-06-05 21:18:11 +02:00
|
|
|
minnum = strtoul(argv[3], &c, 0);
|
2005-10-21 14:41:56 +02:00
|
|
|
if ((c == argv[3]) || (*c != '\0')) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
|
2005-10-21 14:41:56 +02:00
|
|
|
goto c_mknod_err;
|
|
|
|
}
|
2007-06-05 21:18:11 +02:00
|
|
|
dv = makedev(majnum, minnum);
|
2010-07-18 00:09:40 +02:00
|
|
|
if ((unsigned long)(major(dv)) != majnum) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s %s too large: %lu", "device", "major", majnum);
|
2005-10-21 14:41:56 +02:00
|
|
|
goto c_mknod_err;
|
|
|
|
}
|
2010-07-18 00:09:40 +02:00
|
|
|
if ((unsigned long)(minor(dv)) != minnum) {
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
|
2005-10-21 14:41:56 +02:00
|
|
|
goto c_mknod_err;
|
|
|
|
}
|
|
|
|
if (mknod(argv[0], mode, dv))
|
|
|
|
goto c_mknod_failed;
|
|
|
|
} else if (mkfifo(argv[0], mode)) {
|
2006-08-01 15:43:28 +02:00
|
|
|
c_mknod_failed:
|
2010-09-05 21:51:35 +02:00
|
|
|
bi_errorf("%s: %s", argv[0], strerror(errno));
|
2006-08-01 15:43:28 +02:00
|
|
|
c_mknod_err:
|
2005-10-21 14:41:56 +02:00
|
|
|
rv = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (set)
|
|
|
|
umask(oldmode);
|
|
|
|
return (rv);
|
2006-08-01 15:43:28 +02:00
|
|
|
c_mknod_usage:
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor");
|
|
|
|
bi_errorf("%s: %s", "usage", "mknod [-m mode] name p");
|
2005-10-21 14:41:56 +02:00
|
|
|
return (1);
|
|
|
|
}
|
2006-11-09 23:18:10 +01:00
|
|
|
#endif
|
2005-10-21 14:41:56 +02:00
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
/* test(1) accepts the following grammar:
|
|
|
|
oexpr ::= aexpr | aexpr "-o" oexpr ;
|
|
|
|
aexpr ::= nexpr | nexpr "-a" aexpr ;
|
|
|
|
nexpr ::= primary | "!" nexpr ;
|
|
|
|
primary ::= unary-operator operand
|
|
|
|
| operand binary-operator operand
|
|
|
|
| operand
|
|
|
|
| "(" oexpr ")"
|
|
|
|
;
|
|
|
|
|
|
|
|
unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
|
|
|
|
"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
|
|
|
|
"-L"|"-h"|"-S"|"-H";
|
|
|
|
|
|
|
|
binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
|
|
|
|
"-nt"|"-ot"|"-ef"|
|
|
|
|
"<"|">" # rules used for [[ .. ]] expressions
|
|
|
|
;
|
|
|
|
operand ::= <any thing>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define T_ERR_EXIT 2 /* POSIX says > 1 for errors */
|
|
|
|
|
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_test(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int argc, res;
|
2005-05-23 05:06:10 +02:00
|
|
|
Test_env te;
|
|
|
|
|
|
|
|
te.flags = 0;
|
|
|
|
te.isa = ptest_isa;
|
|
|
|
te.getopnd = ptest_getopnd;
|
2007-05-13 20:49:00 +02:00
|
|
|
te.eval = test_eval;
|
2005-05-23 05:06:10 +02:00
|
|
|
te.error = ptest_error;
|
|
|
|
|
|
|
|
for (argc = 0; wp[argc]; argc++)
|
|
|
|
;
|
|
|
|
|
|
|
|
if (strcmp(wp[0], "[") == 0) {
|
|
|
|
if (strcmp(wp[--argc], "]") != 0) {
|
|
|
|
bi_errorf("missing ]");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (T_ERR_EXIT);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
te.pos.wp = wp + 1;
|
|
|
|
te.wp_end = wp + argc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the special cases from POSIX.2, section 4.62.4.
|
|
|
|
* Implementation of all the rules isn't necessary since
|
|
|
|
* our parser does the right thing for the omitted steps.
|
|
|
|
*/
|
|
|
|
if (argc <= 5) {
|
2007-03-04 01:13:17 +01:00
|
|
|
const char **owp = wp;
|
2005-05-23 05:06:10 +02:00
|
|
|
int invert = 0;
|
2009-04-07 21:51:47 +02:00
|
|
|
Test_op op;
|
2005-05-23 05:06:10 +02:00
|
|
|
const char *opnd1, *opnd2;
|
|
|
|
|
|
|
|
while (--argc >= 0) {
|
|
|
|
if ((*te.isa)(&te, TM_END))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (!0);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (argc == 3) {
|
|
|
|
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
|
2008-03-28 19:46:59 +01:00
|
|
|
if ((op = (*te.isa)(&te, TM_BINOP))) {
|
2005-05-23 05:06:10 +02:00
|
|
|
opnd2 = (*te.getopnd)(&te, op, 1);
|
|
|
|
res = (*te.eval)(&te, op, opnd1,
|
|
|
|
opnd2, 1);
|
|
|
|
if (te.flags & TEF_ERROR)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (T_ERR_EXIT);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (invert & 1)
|
|
|
|
res = !res;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (!res);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
/* back up to opnd1 */
|
|
|
|
te.pos.wp--;
|
|
|
|
}
|
|
|
|
if (argc == 1) {
|
|
|
|
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
|
2005-07-04 14:27:28 +02:00
|
|
|
if (strcmp(opnd1, "-t") == 0)
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
res = (*te.eval)(&te, TO_STNZE, opnd1,
|
|
|
|
NULL, 1);
|
|
|
|
if (invert & 1)
|
|
|
|
res = !res;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (!res);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
if ((*te.isa)(&te, TM_NOT)) {
|
|
|
|
invert++;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
te.pos.wp = owp + 1;
|
|
|
|
}
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (test_parse(&te));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic test routines.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Test_op
|
2005-10-08 21:31:00 +02:00
|
|
|
test_isop(Test_meta meta, const char *s)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
char sc1;
|
2005-10-08 21:31:00 +02:00
|
|
|
const struct t_op *tbl;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2005-10-08 21:31:00 +02:00
|
|
|
tbl = meta == TM_UNOP ? u_ops : b_ops;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (*s) {
|
|
|
|
sc1 = s[1];
|
2005-10-08 21:31:00 +02:00
|
|
|
for (; tbl->op_text[0]; tbl++)
|
|
|
|
if (sc1 == tbl->op_text[1] && !strcmp(s, tbl->op_text))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (tbl->op_num);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (TO_NONOP);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
|
2008-04-02 00:20:20 +02:00
|
|
|
bool do_eval)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int i, s;
|
2007-05-13 20:33:29 +02:00
|
|
|
size_t k;
|
2005-05-23 05:06:10 +02:00
|
|
|
struct stat b1, b2;
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t v1, v2;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (!do_eval)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2007-05-13 20:33:29 +02:00
|
|
|
switch ((int)op) {
|
2005-05-23 05:06:10 +02:00
|
|
|
/*
|
|
|
|
* Unary Operators
|
|
|
|
*/
|
|
|
|
case TO_STNZE: /* -n */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (*opnd1 != '\0');
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_STZER: /* -z */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (*opnd1 == '\0');
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_OPTION: /* -o */
|
2009-09-07 19:24:49 +02:00
|
|
|
if ((i = *opnd1) == '!' || i == '?')
|
2005-05-23 05:06:10 +02:00
|
|
|
opnd1++;
|
2007-05-13 20:33:29 +02:00
|
|
|
if ((k = option(opnd1)) == (size_t)-1)
|
2009-08-08 15:08:53 +02:00
|
|
|
return (0);
|
2009-09-07 19:24:49 +02:00
|
|
|
return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILRD: /* -r */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (test_eaccess(opnd1, R_OK) == 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILWR: /* -w */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (test_eaccess(opnd1, W_OK) == 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILEX: /* -x */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (test_eaccess(opnd1, X_OK) == 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILAXST: /* -a */
|
|
|
|
case TO_FILEXST: /* -e */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILREG: /* -r */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILID: /* -d */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILCDEV: /* -c */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILBDEV: /* -b */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILFIFO: /* -p */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILSYM: /* -h -L */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILSOCK: /* -S */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILCDF:/* -H HP context dependent files (directories) */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILSETU: /* -u */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 &&
|
|
|
|
(b1.st_mode & S_ISUID) == S_ISUID);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILSETG: /* -g */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 &&
|
|
|
|
(b1.st_mode & S_ISGID) == S_ISGID);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILSTCK: /* -k */
|
2006-08-24 22:32:53 +02:00
|
|
|
#ifdef S_ISVTX
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 &&
|
|
|
|
(b1.st_mode & S_ISVTX) == S_ISVTX);
|
2006-08-24 22:32:53 +02:00
|
|
|
#else
|
|
|
|
return (0);
|
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILGZ: /* -s */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && b1.st_size > 0L);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILTT: /* -t */
|
2007-05-13 20:33:29 +02:00
|
|
|
if (opnd1 && !bi_getn(opnd1, &i)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
te->flags |= TEF_ERROR;
|
2007-05-13 20:33:29 +02:00
|
|
|
i = 0;
|
2005-07-04 14:27:28 +02:00
|
|
|
} else
|
2007-05-13 20:33:29 +02:00
|
|
|
i = isatty(opnd1 ? i : 0);
|
|
|
|
return (i);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILUID: /* -O */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILGID: /* -G */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 && b1.st_gid == getegid());
|
2005-05-23 05:06:10 +02:00
|
|
|
/*
|
|
|
|
* Binary Operators
|
|
|
|
*/
|
|
|
|
case TO_STEQL: /* = */
|
|
|
|
if (te->flags & TEF_DBRACKET)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (gmatchx(opnd1, opnd2, false));
|
|
|
|
return (strcmp(opnd1, opnd2) == 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_STNEQ: /* != */
|
|
|
|
if (te->flags & TEF_DBRACKET)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (!gmatchx(opnd1, opnd2, false));
|
|
|
|
return (strcmp(opnd1, opnd2) != 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_STLT: /* < */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (strcmp(opnd1, opnd2) < 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_STGT: /* > */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (strcmp(opnd1, opnd2) > 0);
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_INTEQ: /* -eq */
|
|
|
|
case TO_INTNE: /* -ne */
|
|
|
|
case TO_INTGE: /* -ge */
|
|
|
|
case TO_INTGT: /* -gt */
|
|
|
|
case TO_INTLE: /* -le */
|
|
|
|
case TO_INTLT: /* -lt */
|
2008-04-02 00:20:20 +02:00
|
|
|
if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) ||
|
|
|
|
!evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) {
|
|
|
|
/* error already printed.. */
|
|
|
|
te->flags |= TEF_ERROR;
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-02 00:20:20 +02:00
|
|
|
switch ((int)op) {
|
|
|
|
case TO_INTEQ:
|
|
|
|
return (v1 == v2);
|
|
|
|
case TO_INTNE:
|
|
|
|
return (v1 != v2);
|
|
|
|
case TO_INTGE:
|
|
|
|
return (v1 >= v2);
|
|
|
|
case TO_INTGT:
|
|
|
|
return (v1 > v2);
|
|
|
|
case TO_INTLE:
|
|
|
|
return (v1 <= v2);
|
|
|
|
case TO_INTLT:
|
|
|
|
return (v1 < v2);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-02 00:20:20 +02:00
|
|
|
case TO_FILNT: /* -nt */
|
|
|
|
/* ksh88/ksh93 succeed if file2 can't be stated
|
|
|
|
* (subtly different from 'does not exist').
|
|
|
|
*/
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd1, &b1) == 0 &&
|
2008-04-02 00:20:20 +02:00
|
|
|
(((s = stat(opnd2, &b2)) == 0 &&
|
2009-06-08 22:06:50 +02:00
|
|
|
b1.st_mtime > b2.st_mtime) || s < 0));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILOT: /* -ot */
|
2008-04-02 00:20:20 +02:00
|
|
|
/* ksh88/ksh93 succeed if file1 can't be stated
|
|
|
|
* (subtly different from 'does not exist').
|
|
|
|
*/
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat(opnd2, &b2) == 0 &&
|
2008-04-02 00:20:20 +02:00
|
|
|
(((s = stat(opnd1, &b1)) == 0 &&
|
2009-06-08 22:06:50 +02:00
|
|
|
b1.st_mtime < b2.st_mtime) || s < 0));
|
2005-05-23 05:06:10 +02:00
|
|
|
case TO_FILEQ: /* -ef */
|
2009-06-08 22:06:50 +02:00
|
|
|
return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
|
|
|
|
b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
(*te->error)(te, 0, "internal error: unknown op");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2005-07-06 02:02:06 +02:00
|
|
|
/* On most/all unixen, access() says everything is executable for root... */
|
2005-05-23 05:06:10 +02:00
|
|
|
static int
|
|
|
|
test_eaccess(const char *pathl, int mode)
|
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-04-02 00:20:20 +02:00
|
|
|
if ((rv = access(pathl, mode)) == 0 && ksheuid == 0 && (mode & X_OK)) {
|
2005-05-23 05:06:10 +02:00
|
|
|
struct stat statb;
|
|
|
|
|
|
|
|
if (stat(pathl, &statb) < 0)
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = -1;
|
2005-05-23 05:06:10 +02:00
|
|
|
else if (S_ISDIR(statb.st_mode))
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = 0;
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ?
|
2005-05-23 05:06:10 +02:00
|
|
|
0 : -1;
|
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
test_parse(Test_env *te)
|
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = test_oexpr(te, 1);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
|
|
|
|
(*te->error)(te, 0, "unexpected operator/operand");
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return ((te->flags & TEF_ERROR) ? T_ERR_EXIT : !rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-04-02 00:20:20 +02:00
|
|
|
test_oexpr(Test_env *te, bool do_eval)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-04-02 00:20:20 +02:00
|
|
|
if ((rv = test_aexpr(te, do_eval)))
|
|
|
|
do_eval = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (test_oexpr(te, do_eval) || rv);
|
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-04-02 00:20:20 +02:00
|
|
|
test_aexpr(Test_env *te, bool do_eval)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2008-04-02 00:20:20 +02:00
|
|
|
if (!(rv = test_nexpr(te, do_eval)))
|
|
|
|
do_eval = false;
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (test_aexpr(te, do_eval) && rv);
|
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-04-02 00:20:20 +02:00
|
|
|
test_nexpr(Test_env *te, bool do_eval)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
|
2009-06-08 22:06:50 +02:00
|
|
|
return (!test_nexpr(te, do_eval));
|
|
|
|
return (test_primary(te, do_eval));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-04-02 00:20:20 +02:00
|
|
|
test_primary(Test_env *te, bool do_eval)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
const char *opnd1, *opnd2;
|
2008-04-02 00:20:20 +02:00
|
|
|
int rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
Test_op op;
|
|
|
|
|
|
|
|
if (te->flags & TEF_ERROR)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
if ((*te->isa)(te, TM_OPAREN)) {
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = test_oexpr(te, do_eval);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (te->flags & TEF_ERROR)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!(*te->isa)(te, TM_CPAREN)) {
|
2010-08-28 20:50:58 +02:00
|
|
|
(*te->error)(te, 0, "missing )");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-03-22 18:58:58 +01:00
|
|
|
/*
|
|
|
|
* Binary should have precedence over unary in this case
|
|
|
|
* so that something like test \( -f = -f \) is accepted
|
|
|
|
*/
|
|
|
|
if ((te->flags & TEF_DBRACKET) || (&te->pos.wp[1] < te->wp_end &&
|
2009-03-22 19:28:35 +01:00
|
|
|
!test_isop(TM_BINOP, te->pos.wp[1]))) {
|
2009-03-22 18:58:58 +01:00
|
|
|
if ((op = (*te->isa)(te, TM_UNOP))) {
|
|
|
|
/* unary expression */
|
|
|
|
opnd1 = (*te->getopnd)(te, op, do_eval);
|
|
|
|
if (!opnd1) {
|
|
|
|
(*te->error)(te, -1, "missing argument");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2009-03-22 18:58:58 +01:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return ((*te->eval)(te, op, opnd1, NULL, do_eval));
|
2009-03-22 18:58:58 +01:00
|
|
|
}
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
|
|
|
|
if (!opnd1) {
|
|
|
|
(*te->error)(te, 0, "expression expected");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-03-28 19:46:59 +01:00
|
|
|
if ((op = (*te->isa)(te, TM_BINOP))) {
|
2005-05-23 05:06:10 +02:00
|
|
|
/* binary expression */
|
|
|
|
opnd2 = (*te->getopnd)(te, op, do_eval);
|
|
|
|
if (!opnd2) {
|
|
|
|
(*te->error)(te, -1, "missing second argument");
|
2009-06-08 22:06:50 +02:00
|
|
|
return (0);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return ((*te->eval)(te, op, opnd1, opnd2, do_eval));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-06-08 22:06:50 +02:00
|
|
|
return ((*te->eval)(te, TO_STNZE, opnd1, NULL, do_eval));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Plain test (test and [ .. ]) specific routines.
|
|
|
|
*/
|
|
|
|
|
2010-07-18 00:09:40 +02:00
|
|
|
/*
|
|
|
|
* Test if the current token is a whatever. Accepts the current token if
|
2009-06-10 20:12:51 +02:00
|
|
|
* it is. Returns 0 if it is not, non-zero if it is (in the case of
|
2005-05-23 05:06:10 +02:00
|
|
|
* TM_UNOP and TM_BINOP, the returned value is a Test_op).
|
|
|
|
*/
|
2010-07-18 00:09:40 +02:00
|
|
|
static Test_op
|
2005-05-23 05:06:10 +02:00
|
|
|
ptest_isa(Test_env *te, Test_meta meta)
|
|
|
|
{
|
|
|
|
/* Order important - indexed by Test_meta values */
|
|
|
|
static const char *const tokens[] = {
|
|
|
|
"-o", "-a", "!", "(", ")"
|
|
|
|
};
|
2010-07-18 00:09:40 +02:00
|
|
|
Test_op rv;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (te->pos.wp >= te->wp_end)
|
2010-07-18 00:09:40 +02:00
|
|
|
return (meta == TM_END ? TO_NONNULL : TO_NONOP);
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
if (meta == TM_UNOP || meta == TM_BINOP)
|
2008-04-02 00:20:20 +02:00
|
|
|
rv = test_isop(meta, *te->pos.wp);
|
2005-05-23 05:06:10 +02:00
|
|
|
else if (meta == TM_END)
|
2010-07-18 00:09:40 +02:00
|
|
|
rv = TO_NONOP;
|
2005-05-23 05:06:10 +02:00
|
|
|
else
|
2010-07-18 00:09:40 +02:00
|
|
|
rv = !strcmp(*te->pos.wp, tokens[(int)meta]) ?
|
|
|
|
TO_NONNULL : TO_NONOP;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
/* Accept the token? */
|
2010-07-18 00:09:40 +02:00
|
|
|
if (rv != TO_NONOP)
|
2005-05-23 05:06:10 +02:00
|
|
|
te->pos.wp++;
|
|
|
|
|
2009-06-08 22:06:50 +02:00
|
|
|
return (rv);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
2009-12-12 23:27:10 +01:00
|
|
|
ptest_getopnd(Test_env *te, Test_op op, bool do_eval MKSH_A_UNUSED)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
|
|
|
if (te->pos.wp >= te->wp_end)
|
2009-06-08 22:06:50 +02:00
|
|
|
return (op == TO_FILTT ? "1" : NULL);
|
|
|
|
return (*te->pos.wp++);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-04-02 00:20:20 +02:00
|
|
|
ptest_error(Test_env *te, int ofs, const char *msg)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-02 00:20:20 +02:00
|
|
|
const char *op;
|
2005-05-23 05:06:10 +02:00
|
|
|
|
|
|
|
te->flags |= TEF_ERROR;
|
2008-04-02 00:20:20 +02:00
|
|
|
if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
|
2005-05-23 05:06:10 +02:00
|
|
|
bi_errorf("%s: %s", op, msg);
|
|
|
|
else
|
|
|
|
bi_errorf("%s", msg);
|
|
|
|
}
|
|
|
|
|
2009-06-08 22:52:28 +02:00
|
|
|
#ifndef MKSH_NO_LIMITS
|
2005-05-23 05:06:10 +02:00
|
|
|
#define SOFT 0x1
|
|
|
|
#define HARD 0x2
|
|
|
|
|
2008-04-16 23:56:03 +02:00
|
|
|
struct limits {
|
|
|
|
const char *name;
|
|
|
|
int resource; /* resource to get/set */
|
|
|
|
int factor; /* multiply by to get rlim_{cur,max} values */
|
|
|
|
char option;
|
|
|
|
};
|
|
|
|
|
2008-04-22 20:58:20 +02:00
|
|
|
static void print_ulimit(const struct limits *, int);
|
|
|
|
static int set_ulimit(const struct limits *, const char *, int);
|
|
|
|
|
2010-03-27 17:51:39 +01:00
|
|
|
/* Magic to divine the 'm' and 'v' limits */
|
|
|
|
|
|
|
|
#ifdef RLIMIT_AS
|
|
|
|
#if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
|
|
|
|
!defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
|
|
|
|
#define ULIMIT_V_IS_AS
|
|
|
|
#elif defined(RLIMIT_VMEM)
|
|
|
|
#if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
|
|
|
|
#define ULIMIT_V_IS_AS
|
|
|
|
#else
|
|
|
|
#define ULIMIT_V_IS_VMEM
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef RLIMIT_RSS
|
|
|
|
#ifdef ULIMIT_V_IS_VMEM
|
|
|
|
#define ULIMIT_M_IS_RSS
|
|
|
|
#elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
|
|
|
|
#define ULIMIT_M_IS_VMEM
|
|
|
|
#else
|
|
|
|
#define ULIMIT_M_IS_RSS
|
|
|
|
#endif
|
|
|
|
#if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)
|
|
|
|
#undef ULIMIT_M_IS_RSS
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
|
|
|
|
#define ULIMIT_V_IS_VMEM
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
|
|
|
|
(!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
|
|
|
|
#define ULIMIT_M_IS_VMEM
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
|
|
|
|
(RLIMIT_VMEM == RLIMIT_AS)
|
|
|
|
#undef ULIMIT_M_IS_VMEM
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
int
|
2007-03-04 01:13:17 +01:00
|
|
|
c_ulimit(const char **wp)
|
2005-05-23 05:06:10 +02:00
|
|
|
{
|
2008-04-16 23:56:03 +02:00
|
|
|
static const struct limits limits[] = {
|
|
|
|
/* do not use options -H, -S or -a or change the order */
|
|
|
|
#ifdef RLIMIT_CPU
|
|
|
|
{ "time(cpu-seconds)", RLIMIT_CPU, 1, 't' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_FSIZE
|
|
|
|
{ "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
|
|
|
|
#endif
|
2005-05-25 11:18:16 +02:00
|
|
|
#ifdef RLIMIT_CORE
|
2008-04-16 23:56:03 +02:00
|
|
|
{ "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
|
2005-05-25 11:18:16 +02:00
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_DATA
|
2008-04-16 23:56:03 +02:00
|
|
|
{ "data(KiB)", RLIMIT_DATA, 1024, 'd' },
|
2005-05-25 11:18:16 +02:00
|
|
|
#endif
|
2008-04-16 23:56:03 +02:00
|
|
|
#ifdef RLIMIT_STACK
|
|
|
|
{ "stack(KiB)", RLIMIT_STACK, 1024, 's' },
|
2005-05-23 05:06:10 +02:00
|
|
|
#endif
|
2005-05-23 17:24:43 +02:00
|
|
|
#ifdef RLIMIT_MEMLOCK
|
2008-04-16 23:56:03 +02:00
|
|
|
{ "lockedmem(KiB)", RLIMIT_MEMLOCK, 1024, 'l' },
|
2005-05-23 17:24:43 +02:00
|
|
|
#endif
|
2005-05-25 11:18:16 +02:00
|
|
|
#ifdef RLIMIT_NOFILE
|
2008-04-16 23:56:03 +02:00
|
|
|
{ "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
|
2005-05-25 11:18:16 +02:00
|
|
|
#endif
|
2005-05-23 17:24:43 +02:00
|
|
|
#ifdef RLIMIT_NPROC
|
2008-04-16 23:56:03 +02:00
|
|
|
{ "processes", RLIMIT_NPROC, 1, 'p' },
|
2005-05-26 01:31:08 +02:00
|
|
|
#endif
|
2005-05-23 05:06:10 +02:00
|
|
|
#ifdef RLIMIT_SWAP
|
2008-04-16 23:56:03 +02:00
|
|
|
{ "swap(KiB)", RLIMIT_SWAP, 1024, 'w' },
|
2005-05-23 05:06:10 +02:00
|
|
|
#endif
|
2008-04-16 23:56:03 +02:00
|
|
|
#ifdef RLIMIT_LOCKS
|
|
|
|
{ "flocks", RLIMIT_LOCKS, -1, 'L' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_TIME
|
|
|
|
{ "humantime(seconds)", RLIMIT_TIME, 1, 'T' },
|
2010-03-14 12:58:33 +01:00
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_NOVMON
|
|
|
|
{ "vnodemonitors", RLIMIT_NOVMON, 1, 'V' },
|
2010-03-27 17:51:39 +01:00
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_SIGPENDING
|
|
|
|
{ "sigpending", RLIMIT_SIGPENDING, 1, 'i' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_MSGQUEUE
|
|
|
|
{ "msgqueue(bytes)", RLIMIT_MSGQUEUE, 1, 'q' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_AIO_MEM
|
|
|
|
{ "AIOlockedmem(KiB)", RLIMIT_AIO_MEM, 1024, 'M' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_AIO_OPS
|
|
|
|
{ "AIOoperations", RLIMIT_AIO_OPS, 1, 'O' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_TCACHE
|
|
|
|
{ "cachedthreads", RLIMIT_TCACHE, 1, 'C' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_SBSIZE
|
|
|
|
{ "sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024, 'B' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_PTHREAD
|
|
|
|
{ "threadsperprocess", RLIMIT_PTHREAD, 1, 'P' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_NICE
|
|
|
|
{ "maxnice", RLIMIT_NICE, 1, 'e' },
|
|
|
|
#endif
|
|
|
|
#ifdef RLIMIT_RTPRIO
|
|
|
|
{ "maxrtprio", RLIMIT_RTPRIO, 1, 'r' },
|
|
|
|
#endif
|
|
|
|
#if defined(ULIMIT_M_IS_RSS)
|
|
|
|
{ "resident-set(KiB)", RLIMIT_RSS, 1024, 'm' },
|
|
|
|
#elif defined(ULIMIT_M_IS_VMEM)
|
|
|
|
{ "memory(KiB)", RLIMIT_VMEM, 1024, 'm' },
|
|
|
|
#endif
|
|
|
|
#if defined(ULIMIT_V_IS_VMEM)
|
|
|
|
{ "virtual-memory(KiB)", RLIMIT_VMEM, 1024, 'v' },
|
|
|
|
#elif defined(ULIMIT_V_IS_AS)
|
|
|
|
{ "address-space(KiB)", RLIMIT_AS, 1024, 'v' },
|
2008-04-16 23:56:03 +02:00
|
|
|
#endif
|
|
|
|
{ NULL, 0, 0, 0 }
|
2005-05-23 05:06:10 +02:00
|
|
|
};
|
2008-04-20 23:30:29 +02:00
|
|
|
static char opts[3 + NELEM(limits)];
|
2008-04-22 20:58:20 +02:00
|
|
|
int how = SOFT | HARD, optc, what = 'f';
|
|
|
|
bool all = false;
|
2008-04-02 00:20:20 +02:00
|
|
|
const struct limits *l;
|
|
|
|
|
2005-05-23 05:06:10 +02:00
|
|
|
if (!opts[0]) {
|
|
|
|
/* build options string on first call - yuck */
|
|
|
|
char *p = opts;
|
|
|
|
|
|
|
|
*p++ = 'H'; *p++ = 'S'; *p++ = 'a';
|
2008-04-20 23:30:29 +02:00
|
|
|
for (l = limits; l->name; l++)
|
2005-05-23 05:06:10 +02:00
|
|
|
*p++ = l->option;
|
|
|
|
*p = '\0';
|
|
|
|
}
|
2008-04-22 20:58:20 +02:00
|
|
|
|
2006-05-10 20:54:13 +02:00
|
|
|
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
|
2005-05-23 05:06:10 +02:00
|
|
|
switch (optc) {
|
|
|
|
case 'H':
|
|
|
|
how = HARD;
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
how = SOFT;
|
|
|
|
break;
|
|
|
|
case 'a':
|
2008-04-02 00:20:20 +02:00
|
|
|
all = true;
|
2005-05-23 05:06:10 +02:00
|
|
|
break;
|
|
|
|
case '?':
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", "usage",
|
|
|
|
"ulimit [-acdfHLlmnpSsTtvw] [value]");
|
2008-04-16 23:56:03 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
default:
|
2008-04-20 23:30:29 +02:00
|
|
|
what = optc;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2008-04-20 23:30:29 +02:00
|
|
|
for (l = limits; l->name && l->option != what; l++)
|
|
|
|
;
|
|
|
|
if (!l->name) {
|
|
|
|
internal_warningf("ulimit: %c", what);
|
2008-04-16 23:56:03 +02:00
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
|
|
|
|
2008-04-22 20:58:20 +02:00
|
|
|
if (wp[builtin_opt.optind]) {
|
|
|
|
if (all || wp[builtin_opt.optind + 1]) {
|
2008-04-20 23:30:29 +02:00
|
|
|
bi_errorf("too many arguments");
|
2008-04-16 23:56:03 +02:00
|
|
|
return (1);
|
2008-04-20 23:30:29 +02:00
|
|
|
}
|
2008-04-22 20:58:20 +02:00
|
|
|
return (set_ulimit(l, wp[builtin_opt.optind], how));
|
2008-04-20 23:30:29 +02:00
|
|
|
}
|
2008-04-22 20:58:20 +02:00
|
|
|
if (!all)
|
|
|
|
print_ulimit(l, how);
|
|
|
|
else for (l = limits; l->name; l++) {
|
|
|
|
shprintf("%-20s ", l->name);
|
|
|
|
print_ulimit(l, how);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
set_ulimit(const struct limits *l, const char *v, int how)
|
|
|
|
{
|
|
|
|
rlim_t val = (rlim_t)0;
|
|
|
|
struct rlimit limit;
|
|
|
|
|
|
|
|
if (strcmp(v, "unlimited") == 0)
|
|
|
|
val = (rlim_t)RLIM_INFINITY;
|
|
|
|
else {
|
2009-03-14 19:12:55 +01:00
|
|
|
mksh_ari_t rval;
|
2008-04-22 20:58:20 +02:00
|
|
|
|
|
|
|
if (!evaluate(v, &rval, KSH_RETURN_ERROR, false))
|
|
|
|
return (1);
|
|
|
|
/*
|
|
|
|
* Avoid problems caused by typos that evaluate misses due
|
|
|
|
* to evaluating unset parameters to 0...
|
|
|
|
* If this causes problems, will have to add parameter to
|
|
|
|
* evaluate() to control if unset params are 0 or an error.
|
|
|
|
*/
|
|
|
|
if (!rval && !ksh_isdigit(v[0])) {
|
|
|
|
bi_errorf("invalid %s limit: %s", l->name, v);
|
|
|
|
return (1);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-22 20:58:20 +02:00
|
|
|
val = (rlim_t)((rlim_t)rval * l->factor);
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-22 20:58:20 +02:00
|
|
|
|
2008-04-16 23:56:03 +02:00
|
|
|
if (getrlimit(l->resource, &limit) < 0) {
|
2010-08-28 22:22:24 +02:00
|
|
|
/* some can't be read, e.g. Linux RLIMIT_LOCKS */
|
2008-04-16 23:56:03 +02:00
|
|
|
limit.rlim_cur = RLIM_INFINITY;
|
|
|
|
limit.rlim_max = RLIM_INFINITY;
|
|
|
|
}
|
|
|
|
if (how & SOFT)
|
|
|
|
limit.rlim_cur = val;
|
|
|
|
if (how & HARD)
|
|
|
|
limit.rlim_max = val;
|
2008-04-22 20:58:20 +02:00
|
|
|
if (!setrlimit(l->resource, &limit))
|
|
|
|
return (0);
|
|
|
|
if (errno == EPERM)
|
|
|
|
bi_errorf("%s exceeds allowable %s limit", v, l->name);
|
|
|
|
else
|
|
|
|
bi_errorf("bad %s limit: %s", l->name, strerror(errno));
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_ulimit(const struct limits *l, int how)
|
|
|
|
{
|
|
|
|
rlim_t val = (rlim_t)0;
|
|
|
|
struct rlimit limit;
|
|
|
|
|
|
|
|
if (getrlimit(l->resource, &limit)) {
|
|
|
|
shf_puts("unknown\n", shl_stdout);
|
|
|
|
return;
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2008-04-22 20:58:20 +02:00
|
|
|
if (how & SOFT)
|
|
|
|
val = limit.rlim_cur;
|
|
|
|
else if (how & HARD)
|
|
|
|
val = limit.rlim_max;
|
2009-12-01 14:12:30 +01:00
|
|
|
if (val == (rlim_t)RLIM_INFINITY)
|
2008-04-22 20:58:20 +02:00
|
|
|
shf_puts("unlimited\n", shl_stdout);
|
|
|
|
else
|
|
|
|
shprintf("%ld\n", (long)(val / l->factor));
|
2005-05-23 05:06:10 +02:00
|
|
|
}
|
2009-04-03 11:45:23 +02:00
|
|
|
#endif
|
2007-08-20 00:06:27 +02:00
|
|
|
|
|
|
|
int
|
|
|
|
c_rename(const char **wp)
|
|
|
|
{
|
|
|
|
int rv = 1;
|
|
|
|
|
2010-09-05 21:51:35 +02:00
|
|
|
/* skip argv[0] */
|
|
|
|
++wp;
|
|
|
|
if (wp[0] && !strcmp(wp[0], "--"))
|
|
|
|
/* skip "--" (options separator) */
|
|
|
|
++wp;
|
|
|
|
|
|
|
|
/* check for exactly two arguments */
|
|
|
|
if (wp[0] == NULL /* first argument */ ||
|
|
|
|
wp[1] == NULL /* second argument */ ||
|
|
|
|
wp[2] != NULL /* no further args please */)
|
2007-08-20 01:12:23 +02:00
|
|
|
bi_errorf(T_synerr);
|
2010-09-05 21:51:35 +02:00
|
|
|
else if ((rv = rename(wp[0], wp[1])) != 0) {
|
2007-08-20 00:06:27 +02:00
|
|
|
rv = errno;
|
2010-08-28 20:50:58 +02:00
|
|
|
bi_errorf("%s: %s", "failed", strerror(rv));
|
2007-08-20 00:06:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (rv);
|
|
|
|
}
|
2008-05-17 20:27:57 +02:00
|
|
|
|
|
|
|
int
|
|
|
|
c_realpath(const char **wp)
|
|
|
|
{
|
|
|
|
int rv = 1;
|
2009-08-30 15:22:39 +02:00
|
|
|
char *buf;
|
2008-05-17 20:27:57 +02:00
|
|
|
|
2010-09-05 21:51:35 +02:00
|
|
|
/* skip argv[0] */
|
|
|
|
++wp;
|
|
|
|
if (wp[0] && !strcmp(wp[0], "--"))
|
|
|
|
/* skip "--" (options separator) */
|
|
|
|
++wp;
|
2008-05-17 20:27:57 +02:00
|
|
|
|
2010-09-05 21:51:35 +02:00
|
|
|
/* check for exactly one argument */
|
|
|
|
if (wp[0] == NULL || wp[1] != NULL)
|
2008-05-17 20:27:57 +02:00
|
|
|
bi_errorf(T_synerr);
|
2010-09-05 21:51:35 +02:00
|
|
|
else if ((buf = do_realpath(wp[0])) == NULL) {
|
2009-08-30 15:22:39 +02:00
|
|
|
rv = errno;
|
2010-09-05 21:51:35 +02:00
|
|
|
bi_errorf("%s: %s", wp[0], strerror(rv));
|
2009-08-30 15:22:39 +02:00
|
|
|
if ((unsigned int)rv > 255)
|
|
|
|
rv = 255;
|
|
|
|
} else {
|
|
|
|
shprintf("%s\n", buf);
|
2008-05-17 20:27:57 +02:00
|
|
|
afree(buf, ATEMP);
|
2010-09-05 21:51:35 +02:00
|
|
|
rv = 0;
|
2008-05-17 20:27:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return (rv);
|
|
|
|
}
|
2010-08-28 19:21:46 +02:00
|
|
|
|
|
|
|
int
|
|
|
|
c_cat(const char **wp)
|
|
|
|
{
|
|
|
|
int fd = STDIN_FILENO, rv = 0;
|
|
|
|
ssize_t n, w;
|
|
|
|
const char *fn = "<stdin>";
|
|
|
|
char *buf, *cp;
|
|
|
|
#define MKSH_CAT_BUFSIZ 4096
|
|
|
|
|
|
|
|
/* XXX uses malloc instead of lalloc (for alignment/speed) */
|
|
|
|
if ((buf = malloc(MKSH_CAT_BUFSIZ)) == NULL) {
|
2010-08-28 22:22:24 +02:00
|
|
|
bi_errorf("can't allocate %lu data bytes",
|
2010-08-28 19:21:46 +02:00
|
|
|
(unsigned long)MKSH_CAT_BUFSIZ);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
2010-09-05 21:51:35 +02:00
|
|
|
/* skip argv[0] */
|
|
|
|
++wp;
|
|
|
|
if (wp[0] && !strcmp(wp[0], "--"))
|
|
|
|
/* skip "--" (options separator) */
|
|
|
|
++wp;
|
2010-08-28 19:21:46 +02:00
|
|
|
|
|
|
|
do {
|
|
|
|
if (*wp) {
|
|
|
|
fn = *wp++;
|
|
|
|
if (fn[0] == '-' && fn[1] == '\0')
|
|
|
|
fd = STDIN_FILENO;
|
|
|
|
else if ((fd = open(fn, O_RDONLY)) < 0) {
|
|
|
|
rv = errno;
|
|
|
|
bi_errorf("%s: %s", fn, strerror(rv));
|
|
|
|
rv = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (1) {
|
|
|
|
n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
|
|
|
|
if (n == -1) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
/* interrupted, try again */
|
|
|
|
continue;
|
|
|
|
/* an error occured during reading */
|
|
|
|
rv = errno;
|
|
|
|
bi_errorf("%s: %s", fn, strerror(rv));
|
|
|
|
rv = 1;
|
|
|
|
break;
|
|
|
|
} else if (n == 0)
|
|
|
|
/* end of file reached */
|
|
|
|
break;
|
|
|
|
while (n) {
|
|
|
|
w = write(STDOUT_FILENO, cp, n);
|
|
|
|
if (w == -1) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
/* interrupted, try again */
|
|
|
|
continue;
|
|
|
|
/* an error occured during writing */
|
|
|
|
rv = errno;
|
|
|
|
bi_errorf("%s: %s", "<stdout>",
|
|
|
|
strerror(rv));
|
|
|
|
rv = 1;
|
|
|
|
if (fd != STDIN_FILENO)
|
|
|
|
close(fd);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
n -= w;
|
|
|
|
cp += w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fd != STDIN_FILENO)
|
|
|
|
close(fd);
|
|
|
|
} while (*wp);
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(buf);
|
|
|
|
return (rv);
|
|
|
|
}
|