1
0
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:
Peter Stephenson 2017-09-11 10:12:17 +01:00
parent c9df6bc8d4
commit 2ef3dff65a
6 changed files with 153 additions and 35 deletions

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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