diff --git a/src/cmd/ksh93/bltins/alias.c b/src/cmd/ksh93/bltins/alias.c index a334976de9b5..44b8b2931b8c 100644 --- a/src/cmd/ksh93/bltins/alias.c +++ b/src/cmd/ksh93/bltins/alias.c @@ -19,6 +19,8 @@ ***********************************************************************/ #include "config_ast.h" // IWYU pragma: keep +#include +#include #include #include "argnod.h" @@ -27,31 +29,38 @@ #include "defs.h" #include "error.h" #include "name.h" -#include "option.h" #include "shcmd.h" #include "variables.h" +static const char *short_options = ":ptx"; +static const struct option long_options[] = {{"help", 0, NULL, 1}, // all builtins supports --help + {NULL, 0, NULL, 0}}; + int b_alias(int argc, char *argv[], Shbltin_t *context) { UNUSED(argc); - nvflag_t nvflags = NV_NOARRAY | NV_NOSCOPE | NV_ASSIGN; + int opt; Dt_t *troot; - int n; struct tdata tdata; + Shell_t *shp = context->shp; + char *cmd = argv[0]; + nvflag_t nvflags = NV_NOARRAY | NV_NOSCOPE | NV_ASSIGN; memset(&tdata, 0, sizeof(tdata)); - tdata.sh = context->shp; + tdata.sh = shp; troot = tdata.sh->alias_tree; - if (*argv[0] == 'h') nvflags = NV_TAGGED; - if (sh_isoption(tdata.sh, SH_BASH)) tdata.prefix = argv[0]; + if (sh_isoption(tdata.sh, SH_BASH)) tdata.prefix = cmd; if (!argv[1]) return setall(argv, nvflags, troot, &tdata); - opt_info.offset = 0; - opt_info.index = 1; - *opt_info.option = 0; tdata.argnum = 0; tdata.aflag = *argv[1]; - while ((n = optget(argv, sh_optalias))) { - switch (n) { //!OCLINT(MissingDefaultStatement) + + optind = 0; + while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { + switch (opt) { + case 1: { + builtin_print_help(shp, cmd); + return 0; + } case 'p': { tdata.prefix = argv[0]; break; @@ -65,21 +74,18 @@ int b_alias(int argc, char *argv[], Shbltin_t *context) { break; } case ':': { - errormsg(SH_DICT, 2, "%s", opt_info.arg); - break; + builtin_missing_argument(shp, cmd, argv[optind - 1]); + return 2; } case '?': { - errormsg(SH_DICT, ERROR_usage(0), "%s", opt_info.arg); + builtin_unknown_option(shp, cmd, argv[optind - 1]); return 2; } + default: { abort(); } } } - if (error_info.errors) { - errormsg(SH_DICT, ERROR_usage(2), "%s", optusage(NULL)); - __builtin_unreachable(); - } - argv += (opt_info.index - 1); + argv += (optind - 1); if (!nv_isflag(nvflags, NV_TAGGED)) return setall(argv, nvflags, troot, &tdata); // Hacks to handle hash -r | --. @@ -99,6 +105,7 @@ int b_alias(int argc, char *argv[], Shbltin_t *context) { } } } + troot = tdata.sh->track_tree; return setall(argv, nvflags, troot, &tdata); } diff --git a/src/cmd/ksh93/data/builtins.c b/src/cmd/ksh93/data/builtins.c index af9c73d78179..70bd9b226e2b 100644 --- a/src/cmd/ksh93/data/builtins.c +++ b/src/cmd/ksh93/data/builtins.c @@ -29,6 +29,8 @@ #include "builtins.h" #include "jobs.h" +#include "sfio.h" + #define bltin(x) (b_##x) // In the last beta release that came out from AT&T, all the builtins for standard commands @@ -228,3 +230,28 @@ const char e_nofork[] = "cannot fork"; const char e_nosignal[] = "%s: unknown signal name"; const char e_condition[] = "condition(s) required"; const char e_cneedsarg[] = "-c requires argument"; + +// Error message for missing flag argument. +#define BUILTIN_ERR_MISSING "\n%s: Expected argument for flag '%s'\n" +// Error message for unrecognized flag. +#define BUILTIN_ERR_UNKNOWN "\n%s: Unknown flag '%s'\n" + +// Invoke a helper function or command to print a subset of the man page for the command. Such as +// from doing "cmd --help". +void builtin_print_help(Shell_t *shp, const char *cmd) { + const char *argv[3] = {"_ksh_print_help", cmd, NULL}; + sh_eval(shp, sh_sfeval(argv), 0); + return; +} + +// Error reporting for encounter with unknown option when parsing command arguments. +void builtin_unknown_option(Shell_t *shp, const char *cmd, const char *opt) { + builtin_print_help(shp, cmd); + sfprintf(sfstderr, BUILTIN_ERR_UNKNOWN, cmd, opt); +} + +// Error reporting for encounter with missing argument when parsing command arguments. +void builtin_missing_argument(Shell_t *shp, const char *cmd, const char *opt) { + builtin_print_help(shp, cmd); + sfprintf(sfstderr, BUILTIN_ERR_MISSING, cmd, opt); +} diff --git a/src/cmd/ksh93/data/config.sh b/src/cmd/ksh93/data/config.sh new file mode 100644 index 000000000000..c9a7f1004420 --- /dev/null +++ b/src/cmd/ksh93/data/config.sh @@ -0,0 +1,23 @@ +# +# This script is sourced first when the shell starts. It's purpose is to perform basic setup of the +# shell state. For example, where to find autoloaded functions that ship with the shell and arrange +# for them to be loaded on demand. It should not do anything not suitable for every single new ksh +# process. In particular it should be as fast as possible. +# + +# Arrange for standard autoloaded functions to be available. The test for whether or not FPATH is +# already set isn't technically necessary since empty path components are guaranteed not to be +# equivalent to `.` (the CWD). But I prefer to be paranoid since doing so is cheap. +__fpath="${.sh.install_prefix}/share/ksh/functions" +if [[ -z $FPATH ]] +then + FPATH="$__fpath" +else + FPATH="$__fpath:$FPATH" +fi + +for f in "$__fpath"/* +do + typeset -fu $(basename $f) +done +unset __fpath diff --git a/src/cmd/ksh93/docs/alias.1 b/src/cmd/ksh93/docs/alias.1 deleted file mode 100644 index 99aedc3f4de5..000000000000 --- a/src/cmd/ksh93/docs/alias.1 +++ /dev/null @@ -1,18 +0,0 @@ -[-1c? -@(#)$Id: alias (AT&T Research) 1999-07-07 $ -] -[+NAME?alias - define or display aliases] -[+DESCRIPTION?\balias\b creates or redefines alias definitions or writes the existing alias definitions to standard output. An alias definitions provides a string value that will replace a command name when the command is read. Alias names can contain any printable character which is not special to the shell. If an alias value ends in a space or tab, then the word following the command name the alias replaces is also checked to see whether it is an alias.] -[+?If no \aname\as are specified then the names and values of all aliases are written to standard output. Otherwise, for each \aname\a that is specified, and \b=\b\avalue\a is not specified, the current value of the alias corresponding to \aname\a is written to standard output. If \b=\b\avalue\a is specified, the alias \aname\a will be created or redefined.] -[+?\balias\b is built-in to the shell as a declaration command so that field splitting and pathname expansion are not performed on the arguments. Tilde expansion occurs on \avalue\a. An alias definition only affects scripts read by the current shell environment. It does not effect scripts run by this shell.] -[p?Causes the output to be in the form of alias commands that can be used as input to the shell to recreate the current aliases.] -[t?Used for tracked aliases. These are aliases that connect a command name to the pathname of the command and are reset when the \bPATH\b variable is unset. The tracked aliases feature is now obsolete.] -[x?Ignored, this option is obsolete.] - -[name[=value]...] - -[+EXIT STATUS?]{ -[+0?Successful completion.] -[+>0?One or more \aname\a operands did not have an alias definition, or an error occurred.] -} -[+SEE ALSO?\bsh\b(1), \bunalias\b(1)] diff --git a/src/cmd/ksh93/functions/_ksh_print_help b/src/cmd/ksh93/functions/_ksh_print_help new file mode 100644 index 000000000000..40b6afead84a --- /dev/null +++ b/src/cmd/ksh93/functions/_ksh_print_help @@ -0,0 +1,43 @@ +# +# Given a ksh man page emit the text of the SYNOPSIS and FLAGS sections and nothing else. This is +# primarily meant to be used by builtins (either special or normal commands) to emit a subset of its +# man page when invoked with `cmd --help` or the argument parsing for the command detects an error. +# Such as via the `builtin_print_help()` function in the ksh source. +# +function _ksh_print_help { + local cmd=$1 + local fname="${.sh.install_prefix}/share/ksh/man/man1/${cmd}.1" + + [[ -f $fname ]] || return 1 # can't find the man page; give up + + # Find the `ul` command to translate x\cHx sequences, etc., into bold chars and such. Pagers + # like `less` do this but better to not rely on them. If `ul` is not available just pipe the + # nroff output unmodified. + local ul=$(whence ul) + [[ -z $ul ]] && ul=cat + + # Try to determine an optimal line length for the output. + local cols=80 + if [[ -t 0 && -t 1 ]] + then + stty size | read _ cols + fi + cols=$(( cols - 4 )) # leave a little space on the right side of the screen + + # Convert the nroff source for the command into something that looks good on the terminal. + # Specifically, by elimating all the text outside of the SYNOPSIS and FLAGS sections while + # attempting to preserve some of the highlighting understood by the terminal. + nroff -c -man -rLL=${cols}n "$fname" | + sed -n \ + -e '/^S.*Y.*N.*O.*P.*S.*I.*S$/,/^[^ ]/ { + /^S/p + /^[^ ]/b + p + }' \ + -e '/^F.*L.*A.*G.*S$/,/^[^ ]/ { + /^F/p + /^[^ ]/b + p + }' | + uniq | "$ul" +} diff --git a/src/cmd/ksh93/fun/dirs b/src/cmd/ksh93/functions/dirs similarity index 100% rename from src/cmd/ksh93/fun/dirs rename to src/cmd/ksh93/functions/dirs diff --git a/src/cmd/ksh93/fun/popd b/src/cmd/ksh93/functions/popd similarity index 100% rename from src/cmd/ksh93/fun/popd rename to src/cmd/ksh93/functions/popd diff --git a/src/cmd/ksh93/fun/pushd b/src/cmd/ksh93/functions/pushd similarity index 100% rename from src/cmd/ksh93/fun/pushd rename to src/cmd/ksh93/functions/pushd diff --git a/src/cmd/ksh93/include/builtins.h b/src/cmd/ksh93/include/builtins.h index b40d07ba5586..db69ce00d330 100644 --- a/src/cmd/ksh93/include/builtins.h +++ b/src/cmd/ksh93/include/builtins.h @@ -68,6 +68,9 @@ struct tdata { extern int setall(char **, nvflag_t, Dt_t *, struct tdata *); extern void print_scan(Sfio_t *file, nvflag_t flag, Dt_t *root, bool omit_attrs, struct tdata *tp); +extern void builtin_print_help(Shell_t *shp, const char *cmd); +extern void builtin_unknown_option(Shell_t *shp, const char *cmd, const char *opt); +extern void builtin_missing_argument(Shell_t *shp, const char *cmd, const char *opt); // Entry points for shell special builtins. extern int b_alias(int, char *[], Shbltin_t *); @@ -160,7 +163,6 @@ extern const char e_defined[]; // For option parsing. extern const char sh_set[]; -extern const char sh_optalias[]; extern const char sh_optbreak[]; extern const char sh_optbuiltin[]; extern const char sh_optcd[]; diff --git a/src/cmd/ksh93/meson.build b/src/cmd/ksh93/meson.build index 7bfefeb0327f..a9f09d4fbfce 100644 --- a/src/cmd/ksh93/meson.build +++ b/src/cmd/ksh93/meson.build @@ -39,6 +39,9 @@ shcomp_exe = executable('shcomp', ['sh/shcomp.c'], c_args: shared_c_args, dependencies: [libm_dep, libexecinfo_dep, libdl_dep, libsocket_dep, libnsl_dep], install: true) +install_subdir('functions', install_dir: 'share/ksh') +install_data(['data/config.sh'], install_dir: 'share/ksh') + test_dir = join_paths(meson.current_source_dir(), 'tests') test_driver = join_paths(test_dir, 'util', 'run_test.sh') diff --git a/src/cmd/ksh93/sh/main.c b/src/cmd/ksh93/sh/main.c index 6dd8aed7fcf3..657b92097e49 100644 --- a/src/cmd/ksh93/sh/main.c +++ b/src/cmd/ksh93/sh/main.c @@ -162,6 +162,7 @@ int sh_main(int ac, char *av[], Shinit_f userinit) { sh_onoption(shp, SH_MONITOR); } job_init(shp, sh_isoption(shp, SH_LOGIN_SHELL)); + sh_source(shp, iop, INSTALL_PREFIX "/share/ksh/config.sh"); if (sh_isoption(shp, SH_LOGIN_SHELL)) { // System profile. sh_source(shp, iop, e_sysprofile);