add a “\builtin” builtin, make it forward assignments, fix some bugs

related to that:
• while AT&T ksh may do it, POSIX says nothing about allowing
  declaration commands only without vars and redirections, and
  “without vars” especially seems against which commands they are
• fix relationship between forwarders and real declaration commands
• clean up c_builtin vs shcomexec mess

Also, re-run “make repool” with a fixed src/scripts/stringpool.sh,v 1.3
This commit is contained in:
tg 2017-03-12 02:04:15 +00:00
parent 86773fcf21
commit b531baa7fd
5 changed files with 88 additions and 86 deletions

19
exec.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.189 2017/03/11 23:22:34 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.190 2017/03/12 02:04:12 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@ -982,8 +982,13 @@ scriptexec(struct op *tp, const char **ap)
int
c_builtin(const char **wp)
{
return (call_builtin(ktsearch(&builtins, *wp, hash(*wp)), wp,
Tbuiltin, false));
return (call_builtin(get_builtin(*wp), wp, Tbuiltin, false));
}
struct tbl *
get_builtin(const char *s)
{
return (s && *s ? ktsearch(&builtins, s, hash(s)) : NULL);
}
/*
@ -1089,6 +1094,14 @@ builtin(const char *name, int (*func) (const char **))
/* external utility overrides built-in utility, with flags */
flag |= LOW_BI;
break;
case '-':
/* is declaration utility if argv[1] is one (POSIX: command) */
flag |= DECL_FWDR;
break;
case '^':
/* is declaration utility (POSIX: export, readonly) */
flag |= DECL_UTIL;
break;
default:
goto flags_seen;
}

13
funcs.c
View File

@ -38,7 +38,7 @@
#endif
#endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.325 2017/03/11 23:22:35 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.326 2017/03/12 02:04:13 tg Exp $");
#if HAVE_KILLPG
/*
@ -92,6 +92,7 @@ c_false(const char **wp MKSH_A_UNUSED)
/*
* A leading = means assignments before command are kept.
* A leading * means a POSIX special builtin.
* A leading ^ means declaration utility, - forwarder.
*/
const struct builtin mkshbuiltins[] = {
{Tsgdot, c_dot},
@ -100,19 +101,19 @@ const struct builtin mkshbuiltins[] = {
/* no =: AT&T manual wrong */
{Talias, c_alias},
{"*=break", c_brkcont},
{T_builtin, c_builtin},
{T__builtin, c_builtin},
{Tbuiltin, c_builtin},
{Tbcat, c_cat},
{Tcd, c_cd},
/* dash compatibility hack */
{"chdir", c_cd},
{Tcommand, c_command},
{T_command, c_command},
{"*=continue", c_brkcont},
{"echo", c_print},
{"*=eval", c_eval},
{"*=exec", c_exec},
{"*=exit", c_exitreturn},
{Tsgexport, c_typeset},
{Tdsgexport, c_typeset},
{Tfalse, c_false},
{"fc", c_fc},
{Tgetopts, c_getopts},
@ -124,7 +125,7 @@ const struct builtin mkshbuiltins[] = {
{"print", c_print},
{"pwd", c_pwd},
{Tread, c_read},
{Tsgreadonly, c_typeset},
{Tdsgreadonly, c_typeset},
{"!realpath", c_realpath},
{"~rename", c_rename},
{"*=return", c_exitreturn},
@ -138,7 +139,7 @@ const struct builtin mkshbuiltins[] = {
{"*=times", c_times},
{"*=trap", c_trap},
{Ttrue, c_true},
{Tgtypeset, c_typeset},
{Tdgtypeset, c_typeset},
{"ulimit", c_ulimit},
{"umask", c_umask},
{Tunalias, c_unalias},

16
lex.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.229 2017/02/18 02:33:12 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.230 2017/03/12 02:04:14 tg Exp $");
/*
* states while lexing word
@ -1014,15 +1014,12 @@ yylex(int cf)
while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
*dp++ = *sp++;
if (c != EOS)
/* word is not unquoted */
/* word is not unquoted, or space ran out */
dp = ident;
/* make sure the ident array stays NUL padded */
memset(dp, 0, (ident + IDENT) - dp + 1);
if (!(cf & (KEYWORD | ALIAS)))
return (LWORD);
if (*ident != '\0') {
if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) {
struct tbl *p;
uint32_t h = hash(ident);
@ -1077,9 +1074,12 @@ yylex(int cf)
goto Again;
}
}
} else if (cf & ALIAS) {
} else if (*ident == '\0') {
/* retain typeset et al. even when quoted */
if (assign_command((dp = wdstrip(yylval.cp, 0)), true))
struct tbl *tt = get_builtin((dp = wdstrip(yylval.cp, 0)));
uint32_t flag = tt ? tt->flag : 0;
if (flag & (DECL_UTIL | DECL_FWDR))
strlcpy(ident, dp, sizeof(ident));
afree(dp, ATEMP);
}

90
sh.h
View File

@ -175,9 +175,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.793 2017/02/18 02:33:14 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.794 2017/03/12 02:04:14 tg Exp $");
#endif
#define MKSH_VERSION "R54 2017/02/18"
#define MKSH_VERSION "R54 2017/03/11"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@ -847,8 +847,8 @@ EXTERN char null[] E_INIT("");
#ifndef HAVE_STRING_POOLING /* helpers for pooled strings */
EXTERN const char T4spaces[] E_INIT(" ");
#define T1space (T4spaces + 3)
EXTERN const char Tcolsp[] E_INIT(": ");
#define T1space (Treal_sp2 + 5)
#define Tcolsp (Tf_sD_ + 2)
EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
#define TC_IFSWS (TC_LEX1 + 7)
EXTERN const char TFCEDIT_dollaru[] E_INIT("${FCEDIT:-/bin/ed} $_");
@ -857,15 +857,15 @@ EXTERN const char Tsgdot[] E_INIT("*=.");
EXTERN const char Taugo[] E_INIT("augo");
EXTERN const char Tbracket[] E_INIT("[");
#define Tdot (Tsgdot + 2)
EXTERN const char Talias[] E_INIT("alias");
EXTERN const char Tbadsubst[] E_INIT("bad substitution");
#define Talias (Tunalias + 2)
#define Tbadsubst (Tfg_badsubst + 10)
EXTERN const char Tbg[] E_INIT("bg");
EXTERN const char Tbad_bsize[] E_INIT("bad shf/buf/bsize");
#define Tbsize (Tbad_bsize + 12)
EXTERN const char Tbad_sig_ss[] E_INIT("%s: bad signal '%s'");
#define Tbad_sig_s (Tbad_sig_ss + 4)
EXTERN const char Tgbuiltin[] E_INIT("=builtin");
#define Tbuiltin (Tgbuiltin + 1)
EXTERN const char T__builtin[] E_INIT("-\\builtin");
#define Tbuiltin (T__builtin + 2)
EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes");
EXTERN const char Tcant_cd[] E_INIT("restricted shell - can't cd");
EXTERN const char Tcant_find[] E_INIT("can't find");
@ -874,26 +874,27 @@ EXTERN const char Tcant_open[] E_INIT("can't open");
EXTERN const char Tbcat[] E_INIT("!cat");
#define Tcat (Tbcat + 1)
#define Tcd (Tcant_cd + 25)
EXTERN const char Tcommand[] E_INIT("command");
#define T_command (T_funny_command + 9)
#define Tcommand (T_funny_command + 10)
EXTERN const char Tcreate[] E_INIT("create");
EXTERN const char TELIF_unexpected[] E_INIT("TELIF unexpected");
EXTERN const char TEXECSHELL[] E_INIT("EXECSHELL");
EXTERN const char Tsgexport[] E_INIT("*=export");
#define Texport (Tsgexport + 2)
EXTERN const char Tdsgexport[] E_INIT("^*=export");
#define Texport (Tdsgexport + 3)
#ifdef __OS2__
EXTERN const char Textproc[] E_INIT("extproc");
#endif
EXTERN const char Tfalse[] E_INIT("false");
EXTERN const char Tfg[] E_INIT("fg");
EXTERN const char Tfg_badsubst[] E_INIT("fileglob: bad substitution");
EXTERN const char Tfile[] E_INIT("file");
#define Tfile (Tfile_fd + 20)
EXTERN const char Tfile_fd[] E_INIT("function definition file");
EXTERN const char TFPATH[] E_INIT("FPATH");
EXTERN const char T_function[] E_INIT(" function");
#define Tfunction (T_function + 1)
EXTERN const char T_funny_command[] E_INIT("funny $() command");
EXTERN const char T_funny_command[] E_INIT("funny $()-command");
EXTERN const char Tgetopts[] E_INIT("getopts");
EXTERN const char Thistory[] E_INIT("history");
#define Thistory (Tnot_in_history + 7)
EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
EXTERN const char Tjobs[] E_INIT("jobs");
EXTERN const char Tjob_not_started[] E_INIT("job not started");
@ -909,20 +910,20 @@ EXTERN const char Tnot_found_s[] E_INIT("%s not found");
#define TOLDPWD (Tno_OLDPWD + 3)
#define Topen (Tcant_open + 6)
#define TPATH (TFPATH + 1)
EXTERN const char Tpv[] E_INIT("pv");
#define Tpv (TpVv + 1)
EXTERN const char TpVv[] E_INIT("Vpv");
#define TPWD (Tno_OLDPWD + 6)
EXTERN const char Tread[] E_INIT("read");
EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
#define Treadonly (Tsgreadonly + 2)
#define Tread (Tshf_read + 4)
EXTERN const char Tdsgreadonly[] E_INIT("^*=readonly");
#define Treadonly (Tdsgreadonly + 3)
EXTERN const char Tredirection_dup[] E_INIT("can't finish (dup) redirection");
#define Tredirection (Tredirection_dup + 19)
EXTERN const char Treal_sp1[] E_INIT("real ");
#define Treal_sp1 (Treal_sp2 + 1)
EXTERN const char Treal_sp2[] E_INIT(" real ");
EXTERN const char Treq_arg[] E_INIT("requires an argument");
EXTERN const char Tselect[] E_INIT("select");
EXTERN const char Tsgset[] E_INIT("*=set");
#define Tset (Tsgset + 2)
#define Tset (Tf_parm + 18)
#define Tsh (Tmksh + 2)
#define TSHELL (TEXECSHELL + 4)
EXTERN const char Tshf_read[] E_INIT("shf_read");
@ -935,27 +936,27 @@ EXTERN const char Ttoo_many_args[] E_INIT("too many arguments");
EXTERN const char Ttrue[] E_INIT("true");
EXTERN const char Ttty_fd_dupof[] E_INIT("dup of tty fd");
#define Ttty_fd (Ttty_fd_dupof + 7)
EXTERN const char Tgtypeset[] E_INIT("=typeset");
#define Ttypeset (Tgtypeset + 1)
EXTERN const char Tdgtypeset[] E_INIT("^=typeset");
#define Ttypeset (Tdgtypeset + 2)
#define Tugo (Taugo + 1)
EXTERN const char Tunalias[] E_INIT("unalias");
#define Tunexpected (TELIF_unexpected + 6)
EXTERN const char Tunknown_option[] E_INIT("unknown option");
EXTERN const char Tuser_sp1[] E_INIT("user ");
#define Tuser_sp1 (Tuser_sp2 + 1)
EXTERN const char Tuser_sp2[] E_INIT(" user ");
#define Twrite (Tshf_write + 4)
EXTERN const char Tf__S[] E_INIT(" %S");
EXTERN const char Tf__d[] E_INIT(" %d");
#define Tf__d (Tf_sd + 2)
EXTERN const char Tf__ss[] E_INIT(" %s%s");
EXTERN const char Tf__sN[] E_INIT(" %s\n");
#define Tf__sN (Tf_s_s_sN + 5)
EXTERN const char Tf_sSs[] E_INIT("%s/%s");
EXTERN const char Tf_T[] E_INIT("%T");
#define Tf_T (Tf_s_T + 3)
EXTERN const char Tf_dN[] E_INIT("%d\n");
EXTERN const char Tf_s_[] E_INIT("%s ");
EXTERN const char Tf_s_T[] E_INIT("%s %T");
EXTERN const char Tf_s_s_sN[] E_INIT("%s %s %s\n");
EXTERN const char Tf_s_s[] E_INIT("%s %s");
EXTERN const char Tf_s_sD_s[] E_INIT("%s %s: %s");
#define Tf_s_s (Tf_sD_s_s + 4)
#define Tf_s_sD_s (Tf_cant + 6)
EXTERN const char Tf_optfoo[] E_INIT("%s%s-%c: %s");
EXTERN const char Tf_sD_[] E_INIT("%s: ");
EXTERN const char Tf_szs[] E_INIT("%s: %zd %s");
@ -968,18 +969,18 @@ EXTERN const char Tf_nonnum[] E_INIT("non-numeric %s %s '%s'");
#endif
EXTERN const char Tf_S_[] E_INIT("%S ");
#define Tf_S (Tf__S + 1)
EXTERN const char Tf_lu[] E_INIT("%lu");
#define Tf_lu (Tf_toolarge + 17)
EXTERN const char Tf_toolarge[] E_INIT("%s %s too large: %lu");
EXTERN const char Tf_ldfailed[] E_INIT("%s %s(%d, %ld) failed: %s");
#define Tf_ss (Tf__ss + 1)
#define Tf_ss (Tf_sss + 2)
EXTERN const char Tf_sss[] E_INIT("%s%s%s");
EXTERN const char Tf_sD_s_sD_s[] E_INIT("%s: %s %s: %s");
EXTERN const char Tf_toomany[] E_INIT("too many %ss\n");
EXTERN const char Tf_sd[] E_INIT("%s %d");
#define Tf_s (Tf__ss + 3)
#define Tf_s (Tf_temp + 28)
EXTERN const char Tft_end[] E_INIT("%;");
EXTERN const char Tft_R[] E_INIT("%R");
#define Tf_d (Tf__d + 1)
#define Tf_d (Tf_sd + 3)
EXTERN const char Tf_sD_s_qs[] E_INIT("%s: %s '%s'");
EXTERN const char Tf_ro[] E_INIT("read-only: %s");
EXTERN const char Tf_flags[] E_INIT("%s: flags 0x%X");
@ -988,8 +989,8 @@ EXTERN const char Tf_ssfaileds[] E_INIT("%s: %s failed: %s");
EXTERN const char Tf_sD_sD_s[] E_INIT("%s: %s: %s");
EXTERN const char Tf__c_[] E_INIT("-%c ");
EXTERN const char Tf_sD_s_s[] E_INIT("%s: %s %s");
#define Tf_sN (Tf__sN + 1)
#define Tf_sD_s (Tf_s_sD_s + 3)
#define Tf_sN (Tf_s_s_sN + 6)
#define Tf_sD_s (Tf_temp + 24)
EXTERN const char T_devtty[] E_INIT("/dev/tty");
#else /* helpers for string pooling */
#define T4spaces " "
@ -1010,7 +1011,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tbsize "bsize"
#define Tbad_sig_ss "%s: bad signal '%s'"
#define Tbad_sig_s "bad signal '%s'"
#define Tgbuiltin "=builtin"
#define T__builtin "-\\builtin"
#define Tbuiltin "builtin"
#define Toomem "can't allocate %zu data bytes"
#define Tcant_cd "restricted shell - can't cd"
@ -1020,11 +1021,12 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Tbcat "!cat"
#define Tcat "cat"
#define Tcd "cd"
#define T_command "-command"
#define Tcommand "command"
#define Tcreate "create"
#define TELIF_unexpected "TELIF unexpected"
#define TEXECSHELL "EXECSHELL"
#define Tsgexport "*=export"
#define Tdsgexport "^*=export"
#define Texport "export"
#ifdef __OS2__
#define Textproc "extproc"
@ -1037,7 +1039,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define TFPATH "FPATH"
#define T_function " function"
#define Tfunction "function"
#define T_funny_command "funny $() command"
#define T_funny_command "funny $()-command"
#define Tgetopts "getopts"
#define Thistory "history"
#define Tintovfl "integer overflow %zu %c %zu prevented"
@ -1059,7 +1061,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define TpVv "Vpv"
#define TPWD "PWD"
#define Tread "read"
#define Tsgreadonly "*=readonly"
#define Tdsgreadonly "^*=readonly"
#define Treadonly "readonly"
#define Tredirection_dup "can't finish (dup) redirection"
#define Tredirection "redirection"
@ -1081,7 +1083,7 @@ EXTERN const char T_devtty[] E_INIT("/dev/tty");
#define Ttrue "true"
#define Ttty_fd_dupof "dup of tty fd"
#define Ttty_fd "tty fd"
#define Tgtypeset "=typeset"
#define Tdgtypeset "^=typeset"
#define Ttypeset "typeset"
#define Tugo "ugo"
#define Tunalias "unalias"
@ -1322,7 +1324,7 @@ EXTERN sigset_t sm_default, sm_sigchld;
/* name of called builtin function (used by error functions) */
EXTERN const char *builtin_argv0;
/* is called builtin SPEC_BI? (also KEEPASN, odd use though) */
/* is called builtin a POSIX special builtin? (error functions only) */
EXTERN bool builtin_spec;
/* current working directory */
@ -1492,6 +1494,8 @@ EXTERN bool last_lookup_was_array;
#define SPEC_BI BIT(12) /* a POSIX special builtin */
#define LOWER_BI BIT(13) /* (with LOW_BI) override even w/o flags */
#define LOW_BI BIT(14) /* external utility overrides built-in one */
#define DECL_UTIL BIT(15) /* is declaration utility */
#define DECL_FWDR BIT(16) /* is declaration utility forwarder */
/*
* Attributes that can be set by the user (used to decide if an unset
@ -2011,7 +2015,8 @@ int glob_str(char *, XPtrV *, bool);
char *do_tilde(char *);
/* exec.c */
int execute(struct op * volatile, volatile int, volatile int * volatile);
int shcomexec(const char **);
int c_builtin(const char **);
struct tbl *get_builtin(const char *);
struct tbl *findfunc(const char *, uint32_t, bool);
int define(const char *, struct op *);
const char *builtin(const char *, int (*)(const char **));
@ -2078,8 +2083,6 @@ int c_times(const char **);
int timex(struct op *, int, volatile int *);
void timex_hook(struct op *, char ** volatile *);
int c_exec(const char **);
/* dummy function (just need pointer value), special case in comexec() */
#define c_builtin shcomexec
int c_test(const char **);
#if HAVE_MKNOD
int c_mknod(const char **);
@ -2287,7 +2290,6 @@ char *shf_smprintf(const char *, ...)
ssize_t shf_vfprintf(struct shf *, const char *, va_list)
MKSH_A_FORMAT(__printf__, 2, 0);
/* syn.c */
int assign_command(const char *, bool) MKSH_A_PURE;
void initkeywords(void);
struct op *compile(Source *, bool);
bool parse_usec(const char *, struct timeval *);

36
syn.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.116 2017/03/11 22:49:56 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.117 2017/03/12 02:04:15 tg Exp $");
struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */
@ -289,11 +289,11 @@ get_command(int cf)
t->lineno = source->line;
goto get_command_start;
while (/* CONSTCOND */ 1) {
bool check_assign_cmd;
bool check_decl_utility;
if (XPsize(args) == 0) {
get_command_start:
check_assign_cmd = true;
check_decl_utility = true;
cf = sALIAS | CMDASN;
} else if (t->u.evalflags)
cf = CMDWORD | CMDASN;
@ -311,16 +311,15 @@ get_command(int cf)
case LWORD:
ACCEPT;
/*
* the iopn == 0 and XPsize(vars) == 0 are
* dubious but AT&T ksh acts this way
*/
if (iopn == 0 && XPsize(vars) == 0 &&
check_assign_cmd) {
if (assign_command(ident, false))
if (check_decl_utility) {
struct tbl *tt = get_builtin(ident);
uint32_t flag;
flag = tt ? tt->flag : 0;
if (flag & DECL_UTIL)
t->u.evalflags = DOVACHECK;
else if (strcmp(ident, Tcommand) != 0)
check_assign_cmd = false;
if (!(flag & DECL_FWDR))
check_decl_utility = false;
}
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
is_wdvarassign(yylval.cp))
@ -937,19 +936,6 @@ compile(Source *s, bool skiputf8bom)
return (outtree);
}
/* lexical analysis for declaration utilities */
int
assign_command(const char *s, bool docommand)
{
if (!*s)
return (0);
return ((strcmp(s, Talias) == 0) ||
(strcmp(s, Texport) == 0) ||
(strcmp(s, Treadonly) == 0) ||
(docommand && (strcmp(s, Tcommand) == 0)) ||
(strcmp(s, Ttypeset) == 0));
}
/* Check if we are in the middle of reading an alias */
static int
inalias(struct source *s)