Skip to content

Commit

Permalink
Switch b_alias() from optget() to getopt_long()
Browse files Browse the repository at this point in the history
Another step in switching from the DocOpt base option processing done by
the AST `optget()` function to the borg standard `getopt_long()`.

It also introduces a system-wide ksh config script to enable auto-loading
functions included with ksh by default. This allows the use of the
auto-loaded `_ksh_print_help` function. As well as the dirs/popd/pushd
set of functions that have been the sole functions included with ksh
forever but not automatically enabled.

Related #507
Fixes #835
  • Loading branch information
krader1961 committed Sep 3, 2019
1 parent 6ca1c5a commit d35d42d
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 38 deletions.
45 changes: 26 additions & 19 deletions src/cmd/ksh93/bltins/alias.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
***********************************************************************/
#include "config_ast.h" // IWYU pragma: keep

#include <getopt.h>
#include <stdlib.h>
#include <string.h>

#include "argnod.h"
Expand All @@ -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;
Expand All @@ -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 | --.
Expand All @@ -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);
}
27 changes: 27 additions & 0 deletions src/cmd/ksh93/data/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
23 changes: 23 additions & 0 deletions src/cmd/ksh93/data/config.sh
Original file line number Diff line number Diff line change
@@ -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
18 changes: 0 additions & 18 deletions src/cmd/ksh93/docs/alias.1

This file was deleted.

43 changes: 43 additions & 0 deletions src/cmd/ksh93/functions/_ksh_print_help
Original file line number Diff line number Diff line change
@@ -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"
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 3 additions & 1 deletion src/cmd/ksh93/include/builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down Expand Up @@ -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[];
Expand Down
3 changes: 3 additions & 0 deletions src/cmd/ksh93/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
1 change: 1 addition & 0 deletions src/cmd/ksh93/sh/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit d35d42d

Please sign in to comment.