mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-09-28 06:49:49 +02:00
41668: New --emulate option on invocation.
This sets the shell emulation mode similarly to ARGV0=... which doesn't work from other shells. Note that this gives more comprehensive emulation than running emulate within the shell.
This commit is contained in:
parent
c9df6bc8d4
commit
2ef3dff65a
@ -1,3 +1,10 @@
|
||||
2017-09-11 Peter Stephenson <p.stephenson@samsung.com>
|
||||
|
||||
* 41668: Doc/Zsh/invoke.yo, Src/builtin.c, Src/init.c,
|
||||
Src/zsh.h, Test/B07emulate.ztst: Add option --emulate at
|
||||
invocation to start in correct emulation, as the ARGV0 trick
|
||||
doesn't work from other shells.
|
||||
|
||||
2017-09-10 Peter Stephenson <p.w.stephenson@ntlworld.com>
|
||||
|
||||
* 41622: Src/exec., Test/A01grammar.ztst: exec -a arguments
|
||||
|
@ -46,6 +46,20 @@ ifzman(zmanref(zshoptions))\
|
||||
ifnzman(noderef(Options))\
|
||||
.
|
||||
|
||||
The long option `tt(--emulate)' followed (in a separate word) by an
|
||||
emulation mode may be passed to the shell.
|
||||
The emulation modes are those described for the tt(emulate) builtin,
|
||||
see
|
||||
ifzman(zmanref(zshbuiltins))\
|
||||
ifnzman(noderef(Shell Builtin Commands)).
|
||||
The `tt(--emulate)' option must precede any other options (which might
|
||||
otherwise be overridden), but following options are honoured, so
|
||||
may be used to modify the requested emulation mode. Note that certain
|
||||
extra steps are taken to ensure a smooth emulation when this option
|
||||
is used compared with the tt(emulate) command within the shell: for
|
||||
example, variables that conflict with POSIX usage such as tt(path) are
|
||||
not defined within the shell.
|
||||
|
||||
Options may be specified by name using the tt(-o) option. tt(-o) acts like
|
||||
a single-letter option, but takes a following string as the option name.
|
||||
For example,
|
||||
|
@ -5905,7 +5905,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func))
|
||||
savehackchar = keyboardhackchar;
|
||||
emulate(shname, opt_R, &new_emulation, new_opts);
|
||||
optlist = newlinklist();
|
||||
if (parseopts(nam, &argv, new_opts, &cmd, optlist)) {
|
||||
if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) {
|
||||
ret = 1;
|
||||
goto restore;
|
||||
}
|
||||
|
132
Src/init.c
132
Src/init.c
@ -244,33 +244,24 @@ static int restricted;
|
||||
|
||||
/**/
|
||||
static void
|
||||
parseargs(char **argv, char **runscript, char **cmdptr)
|
||||
parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr)
|
||||
{
|
||||
char **x;
|
||||
LinkList paramlist;
|
||||
int flags = PARSEARGS_TOPLEVEL;
|
||||
if (**argv == '-')
|
||||
flags |= PARSEARGS_LOGIN;
|
||||
|
||||
argzero = posixzero = *argv++;
|
||||
SHIN = 0;
|
||||
|
||||
/* There's a bit of trickery with opts[INTERACTIVE] here. It starts *
|
||||
* at a value of 2 (instead of 1) or 0. If it is explicitly set on *
|
||||
* the command line, it goes to 1 or 0. If input is coming from *
|
||||
* somewhere that normally makes the shell non-interactive, we do *
|
||||
* "opts[INTERACTIVE] &= 1", so that only a *default* on state will *
|
||||
* be changed. At the end of the function, a value of 2 gets *
|
||||
* changed to 1. */
|
||||
opts[INTERACTIVE] = isatty(0) ? 2 : 0;
|
||||
/*
|
||||
* MONITOR is similar: we initialise it to 2, and if it's
|
||||
* still 2 at the end, we set it to the value of INTERACTIVE.
|
||||
* parseopts sets up some options after we deal with emulation in
|
||||
* order to be consistent --- the code in parseopts_setemulate() is
|
||||
* matched by code at the end of the present function.
|
||||
*/
|
||||
opts[MONITOR] = 2; /* may be unset in init_io() */
|
||||
opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */
|
||||
opts[USEZLE] = 1; /* see below, related to SHINSTDIN */
|
||||
opts[SHINSTDIN] = 0;
|
||||
opts[SINGLECOMMAND] = 0;
|
||||
|
||||
if (parseopts(NULL, &argv, opts, cmdptr, NULL))
|
||||
if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags))
|
||||
exit(1);
|
||||
|
||||
/*
|
||||
@ -333,10 +324,46 @@ parseopts_insert(LinkList optlist, char *base, int optno)
|
||||
addlinknode(optlist, ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets the global emulation plus the options we traditionally
|
||||
* set immediately after that. This is just for historical consistency
|
||||
* --- I don't think those options actually need to be set here.
|
||||
*/
|
||||
static void parseopts_setemulate(char *nam, int flags)
|
||||
{
|
||||
emulate(nam, 1, &emulation, opts); /* initialises most options */
|
||||
opts[LOGINSHELL] = ((flags & PARSEARGS_LOGIN) != 0);
|
||||
opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
|
||||
|
||||
/* There's a bit of trickery with opts[INTERACTIVE] here. It starts *
|
||||
* at a value of 2 (instead of 1) or 0. If it is explicitly set on *
|
||||
* the command line, it goes to 1 or 0. If input is coming from *
|
||||
* somewhere that normally makes the shell non-interactive, we do *
|
||||
* "opts[INTERACTIVE] &= 1", so that only a *default* on state will *
|
||||
* be changed. At the end of the function, a value of 2 gets *
|
||||
* changed to 1. */
|
||||
opts[INTERACTIVE] = isatty(0) ? 2 : 0;
|
||||
/*
|
||||
* MONITOR is similar: we initialise it to 2, and if it's
|
||||
* still 2 at the end, we set it to the value of INTERACTIVE.
|
||||
*/
|
||||
opts[MONITOR] = 2; /* may be unset in init_io() */
|
||||
opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */
|
||||
opts[USEZLE] = 1; /* see below, related to SHINSTDIN */
|
||||
opts[SHINSTDIN] = 0;
|
||||
opts[SINGLECOMMAND] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse shell options.
|
||||
* If nam is not NULL, this is called from a command; don't
|
||||
* exit on failure.
|
||||
*
|
||||
* If (flags & PARSEARGS_TOPLEVEL):
|
||||
* - we are doing shell initilisation
|
||||
* - nam is the name under which the shell was started
|
||||
* - set up emulation and standard options based on that.
|
||||
* Otherwise:
|
||||
* - nam is a command name
|
||||
* - don't exit on failure.
|
||||
*
|
||||
* If optlist is not NULL, it used to form a list of pointers
|
||||
* into new_opts indicating which options have been changed.
|
||||
@ -345,23 +372,26 @@ parseopts_insert(LinkList optlist, char *base, int optno)
|
||||
/**/
|
||||
mod_export int
|
||||
parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
|
||||
LinkList optlist)
|
||||
LinkList optlist, int flags)
|
||||
{
|
||||
int optionbreak = 0;
|
||||
int action, optno;
|
||||
char **argv = *argvp;
|
||||
int toplevel = ((flags & PARSEARGS_TOPLEVEL) != 0u);
|
||||
int emulate_required = toplevel;
|
||||
char *top_emulation = nam;
|
||||
|
||||
*cmdp = 0;
|
||||
#define WARN_OPTION(F, S) \
|
||||
do { \
|
||||
if (nam) \
|
||||
if (!toplevel) \
|
||||
zwarnnam(nam, F, S); \
|
||||
else \
|
||||
zerr(F, S); \
|
||||
} while (0)
|
||||
#define LAST_OPTION(N) \
|
||||
do { \
|
||||
if (nam) { \
|
||||
if (!toplevel) { \
|
||||
if (*argv) \
|
||||
argv++; \
|
||||
goto doneargv; \
|
||||
@ -381,7 +411,7 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
|
||||
argv++;
|
||||
goto doneoptions;
|
||||
}
|
||||
if (nam || *argv != args+1 || **argv != '-')
|
||||
if (!toplevel || *argv != args+1 || **argv != '-')
|
||||
goto badoptionstring;
|
||||
/* GNU-style long options */
|
||||
++*argv;
|
||||
@ -394,6 +424,19 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
|
||||
printhelp();
|
||||
LAST_OPTION(0);
|
||||
}
|
||||
if (!strcmp(*argv, "emulate")) {
|
||||
++argv;
|
||||
if (!*argv) {
|
||||
zerr("--emulate: argument required");
|
||||
exit(1);
|
||||
}
|
||||
if (!emulate_required) {
|
||||
zerr("--emulate: must precede other options");
|
||||
exit(1);
|
||||
}
|
||||
top_emulation = *argv;
|
||||
break;
|
||||
}
|
||||
/* `-' characters are allowed in long options */
|
||||
for(args = *argv; *args; args++)
|
||||
if(*args == '-')
|
||||
@ -402,9 +445,17 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
|
||||
}
|
||||
|
||||
if (unset(SHOPTIONLETTERS) && **argv == 'b') {
|
||||
if (emulate_required) {
|
||||
parseopts_setemulate(top_emulation, flags);
|
||||
emulate_required = 0;
|
||||
}
|
||||
/* -b ends options at the end of this argument */
|
||||
optionbreak = 1;
|
||||
} else if (**argv == 'c') {
|
||||
if (emulate_required) {
|
||||
parseopts_setemulate(top_emulation, flags);
|
||||
emulate_required = 0;
|
||||
}
|
||||
/* -c command */
|
||||
*cmdp = *argv;
|
||||
new_opts[INTERACTIVE] &= 1;
|
||||
@ -417,15 +468,20 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
|
||||
return 1;
|
||||
}
|
||||
longoptions:
|
||||
if (emulate_required) {
|
||||
parseopts_setemulate(top_emulation, flags);
|
||||
emulate_required = 0;
|
||||
}
|
||||
if (!(optno = optlookup(*argv))) {
|
||||
WARN_OPTION("no such option: %s", *argv);
|
||||
return 1;
|
||||
} else if (optno == RESTRICTED && !nam) {
|
||||
} else if (optno == RESTRICTED && toplevel) {
|
||||
restricted = action;
|
||||
} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
|
||||
} else if ((optno == EMACSMODE || optno == VIMODE) && !toplevel) {
|
||||
WARN_OPTION("can't change option: %s", *argv);
|
||||
} else {
|
||||
if (dosetopt(optno, action, !nam, new_opts) && nam) {
|
||||
if (dosetopt(optno, action, toplevel, new_opts) &&
|
||||
!toplevel) {
|
||||
WARN_OPTION("can't change option: %s", *argv);
|
||||
} else if (optlist) {
|
||||
parseopts_insert(optlist, new_opts, optno);
|
||||
@ -442,15 +498,21 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (emulate_required) {
|
||||
parseopts_setemulate(top_emulation, flags);
|
||||
emulate_required = 0;
|
||||
}
|
||||
if (!(optno = optlookupc(**argv))) {
|
||||
WARN_OPTION("bad option: -%c", **argv);
|
||||
return 1;
|
||||
} else if (optno == RESTRICTED && !nam) {
|
||||
} else if (optno == RESTRICTED && toplevel) {
|
||||
restricted = action;
|
||||
} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
|
||||
} else if ((optno == EMACSMODE || optno == VIMODE) &&
|
||||
!toplevel) {
|
||||
WARN_OPTION("can't change option: %s", *argv);
|
||||
} else {
|
||||
if (dosetopt(optno, action, !nam, new_opts) && nam) {
|
||||
if (dosetopt(optno, action, toplevel, new_opts) &&
|
||||
!toplevel) {
|
||||
WARN_OPTION("can't change option: -%c", **argv);
|
||||
} else if (optlist) {
|
||||
parseopts_insert(optlist, new_opts, optno);
|
||||
@ -470,6 +532,10 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
|
||||
}
|
||||
doneargv:
|
||||
*argvp = argv;
|
||||
if (emulate_required) {
|
||||
parseopts_setemulate(top_emulation, flags);
|
||||
emulate_required = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1660,11 +1726,9 @@ zsh_main(UNUSED(int argc), char **argv)
|
||||
fdtable[0] = fdtable[1] = fdtable[2] = FDT_EXTERNAL;
|
||||
|
||||
createoptiontable();
|
||||
emulate(zsh_name, 1, &emulation, opts); /* initialises most options */
|
||||
opts[LOGINSHELL] = (**argv == '-');
|
||||
opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid());
|
||||
/* sets ZLE, INTERACTIVE, SHINSTDIN and SINGLECOMMAND */
|
||||
parseargs(argv, &runscript, &cmd);
|
||||
/* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE,
|
||||
* SHINSTDIN and SINGLECOMMAND */
|
||||
parseargs(zsh_name, argv, &runscript, &cmd);
|
||||
|
||||
SHTTY = -1;
|
||||
init_io(cmd);
|
||||
|
@ -1361,6 +1361,14 @@ struct options {
|
||||
int argscount, argsalloc;
|
||||
};
|
||||
|
||||
/* Flags to parseargs() */
|
||||
|
||||
enum {
|
||||
PARSEARGS_TOPLEVEL = 0x1, /* Call to initialise shell */
|
||||
PARSEARGS_LOGIN = 0x2 /* Shell is login shell */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Handler arguments are: builtin name, null-terminated argument
|
||||
* list excluding command name, option structure, the funcid element from the
|
||||
|
@ -251,3 +251,28 @@
|
||||
emulate sh -c '[[ a == a ]]'
|
||||
0:regression test for POSIX_ALIASES reserved words
|
||||
F:Some reserved tokens are handled in alias expansion
|
||||
|
||||
for mode in ksh bash zsh; do
|
||||
$ZTST_testdir/../Src/zsh --emulate $mode -f -c 'emulate'
|
||||
done
|
||||
0:--emulate option
|
||||
>ksh
|
||||
>sh
|
||||
>zsh
|
||||
|
||||
$ZTST_testdir/../Src/zsh -f --emulate sh
|
||||
1:--emulate must be first
|
||||
*?*: --emulate: must precede other options
|
||||
|
||||
$ZTST_testdir/../Src/zsh --emulate
|
||||
1:--emulate needs an argument
|
||||
*?*: --emulate: argument required
|
||||
|
||||
for opt in shwordsplit noshwordsplit; do
|
||||
$ZTST_testdir/../Src/zsh --emulate sh -f -o $opt -c '
|
||||
[[ -o shwordsplit ]] && echo yes || echo no
|
||||
'
|
||||
done
|
||||
0:--emulate followed by other options
|
||||
>yes
|
||||
>no
|
||||
|
Loading…
Reference in New Issue
Block a user