diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-17 02:51:35 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-17 02:51:35 +0200 |
| commit | 5ba6ad7733ca566627afb7c76184d27417b28228 (patch) | |
| tree | 2651f539638b65823235b762d566efe3cf82d1c8 | |
| parent | 5806d2a3db12c3b306b148734b0c48598db60f5b (diff) | |
hush: optional alias support
function old new delta
parse_stream 2540 2873 +333
i_getch 85 410 +325
builtin_alias - 216 +216
builtin_unalias - 185 +185
.rodata 105806 105985 +179
word_matches_alias - 82 +82
find_alias_slot - 77 +77
end_of_alias_name - 69 +69
builtin_type 128 179 +51
i_free_alias_buffer - 33 +33
enable_all_aliases - 29 +29
bltins1 396 420 +24
o_reset_to_empty_unquoted - 21 +21
run_pipe 1554 1566 +12
i_peek 57 55 -2
parse_redirect 351 346 -5
redirect_opt_num 63 53 -10
encode_then_append_var_plusminus 552 532 -20
done_word 796 771 -25
i_getch_interactive 308 - -308
------------------------------------------------------------------------------
(add/remove: 8/1 grow/shrink: 6/5 up/down: 1636/-370) Total: 1266 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | shell/hush.c | 432 |
1 files changed, 388 insertions, 44 deletions
diff --git a/shell/hush.c b/shell/hush.c index 040b96d4a..aa6d3759c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -194,6 +194,13 @@ //config: help //config: Enable case ... esac statement. +400 bytes. //config: +//config:config HUSH_ALIAS +//config: bool "Support aliases" +//config: default y +//config: depends on SHELL_HUSH +//config: help +//config: Enable aliases. +//config: //config:config HUSH_FUNCTIONS //config: bool "Support funcname() { commands; } syntax" //config: default y @@ -591,6 +598,11 @@ typedef struct HFILE { typedef struct in_str { const char *p; +#if ENABLE_HUSH_ALIAS + /* During alias expansion, p is saved in saved_ibuf, and replaced by alias expansion */ + const char *saved_ibuf; + char *albuf; /* malloced */ +#endif int peek_buf[2]; int last_char; HFILE *file; @@ -802,6 +814,14 @@ struct function { }; #endif +#if ENABLE_HUSH_ALIAS +struct alias { + struct alias *next; + char *str; + smallint dont_recurse; +}; +#endif + /* set -/+o OPT support. (TODO: make it optional) * bash supports the following opts: * allexport off @@ -983,6 +1003,9 @@ struct globals { # endif struct function *top_func; #endif +#if ENABLE_HUSH_ALIAS + struct alias *top_alias; +#endif /* Signal and trap handling */ #if ENABLE_HUSH_FAST unsigned count_SIGCHLD; @@ -1085,6 +1108,10 @@ static int builtin_jobs(char **argv) FAST_FUNC; #if ENABLE_HUSH_GETOPTS static int builtin_getopts(char **argv) FAST_FUNC; #endif +#if ENABLE_HUSH_ALIAS +static int builtin_alias(char **argv) FAST_FUNC; +static int builtin_unalias(char **argv) FAST_FUNC; +#endif #if ENABLE_HUSH_HELP static int builtin_help(char **argv) FAST_FUNC; #endif @@ -1162,6 +1189,9 @@ struct built_in_command { static const struct built_in_command bltins1[] ALIGN_PTR = { BLTIN("." , builtin_source , "Run commands in file"), BLTIN(":" , builtin_true , NULL), +#if ENABLE_HUSH_ALIAS + BLTIN("alias" , builtin_alias , "Define or display aliases"), +#endif #if ENABLE_HUSH_JOB BLTIN("bg" , builtin_fg_bg , "Resume job in background"), #endif @@ -1213,7 +1243,7 @@ static const struct built_in_command bltins1[] ALIGN_PTR = { BLTIN("return" , builtin_return , "Return from function"), #endif #if ENABLE_HUSH_SET - BLTIN("set" , builtin_set , "Set positional parameters"), + BLTIN("set" , builtin_set , "Set options or positional parameters"), #endif BLTIN("shift" , builtin_shift , "Shift positional parameters"), #if BASH_SOURCE @@ -1223,7 +1253,7 @@ static const struct built_in_command bltins1[] ALIGN_PTR = { BLTIN("times" , builtin_times , NULL), #endif #if ENABLE_HUSH_TRAP - BLTIN("trap" , builtin_trap , "Trap signals"), + BLTIN("trap" , builtin_trap , "Define or display signal handlers"), #endif BLTIN("true" , builtin_true , NULL), #if ENABLE_HUSH_TYPE @@ -1235,6 +1265,9 @@ static const struct built_in_command bltins1[] ALIGN_PTR = { #if ENABLE_HUSH_UMASK BLTIN("umask" , builtin_umask , "Set file creation mask"), #endif +#if ENABLE_HUSH_ALIAS + BLTIN("unalias" , builtin_unalias , "Delete aliases"), +#endif #if ENABLE_HUSH_UNSET BLTIN("unset" , builtin_unset , "Unset variables"), #endif @@ -1675,6 +1708,21 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) return newfd; } +#if ENABLE_HUSH_ALIAS +static void enable_all_aliases(void) +{ + if (G_interactive_fd) { + struct alias *alias = G.top_alias; + while (alias) { + alias->dont_recurse = 0; + alias = alias->next; + } + } +} +#else +#define enable_all_aliases() ((void)0) +#endif + /* * Manipulating HFILEs */ @@ -2867,17 +2915,56 @@ static ALWAYS_INLINE int i_getch_interactive(struct in_str *i) } #endif /* !INTERACTIVE */ +#if ENABLE_HUSH_ALIAS +static ALWAYS_INLINE int i_has_alias_buffer(struct in_str *i) +{ + return i->saved_ibuf && i->p && *i->p; +} +static void i_prepend_to_alias_buffer(struct in_str *i, char *prepend, char ch) +{ + if (i->saved_ibuf) { + size_t ofs = i->p - i->albuf; + char *old = i->albuf; + i->albuf = xasprintf("%s%c%s", prepend, ch, old); + i->p = i->albuf + ofs; + free(old); + return; + } + i->saved_ibuf = i->p; + i->p = i->albuf = xasprintf("%s%c", prepend, ch); +} +static void i_free_alias_buffer(struct in_str *i) +{ + if (i->saved_ibuf) { + /* We are here if there was an alias expansion and it has ended just now */ + free(i->albuf); + i->p = i->saved_ibuf; + i->saved_ibuf = NULL; + } +} +#else +# define i_has_alias_buffer(i) 0 +# define i_free_alias_buffer(i) ((void)0) +#endif + static int i_getch(struct in_str *i) { int ch; + IF_HUSH_ALIAS(again:) if (i->p) { - /* string-based in_str, or line editing buffer */ + /* string-based in_str, or line editing buffer, or alias buffer */ ch = (unsigned char)*i->p; if (ch != '\0') { i->p++; goto out; } +#if ENABLE_HUSH_ALIAS + if (i->saved_ibuf) { + i_free_alias_buffer(i); + goto again; + } +#endif /* If string-based in_str, end-of-string is EOF */ if (!i->file) { debug_printf("i_getch: got EOF from string\n"); @@ -2912,57 +2999,48 @@ static int i_getch(struct in_str *i) return ch; } +/* Called often, has optimizations to make it faster: + * = May return NUL instead of EOF. + * It's ok because use cases are "we got '&', peek nexh ch and see whether it's '&'" + * = Must not be called after '\n' (it would cause unexpected line editing prompt). + */ static int i_peek(struct in_str *i) { int ch; - if (!i->file) { - /* string-based in_str */ - /* Doesn't report EOF on NUL. None of the callers care. */ + if (i->p) { + /* string-based in_str, or line editing buffer, or alias buffer */ return (unsigned char)*i->p; } - /* FILE-based in_str */ + /* Now we know it is a file-based in_str. */ -#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE - /* This can be stdin, check line editing char[] buffer */ - if (i->p && *i->p != '\0') - return (unsigned char)*i->p; -#endif /* peek_buf[] is an int array, not char. Can contain EOF. */ ch = i->peek_buf[0]; - if (ch != 0) - return ch; + if (ch == 0) { + /* We did not read it yet, get it now */ + do ch = hfgetc(i->file); while (ch == '\0'); + i->peek_buf[0] = ch; + } - /* Need to get a new char */ - ch = i_getch_interactive(i); debug_printf("file_peek: got '%c' %d\n", ch, ch); - - /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */ -#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE - if (i->p) { - i->p -= 1; - return ch; - } -#endif - i->peek_buf[0] = ch; - /*i->peek_buf[1] = 0; - already is */ return ch; } -/* Only ever called if i_peek() was called, and did not return EOF. - * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, - * not end-of-line. Therefore we never need to read a new editing line here. +/* Called if i_peek() was called, and saw an ordinary char + * (not EOF, not NUL, not end-of-line). + * Therefore we never need to read a new editing line here. */ static int i_peek2(struct in_str *i) { int ch; - /* There are two cases when i->p[] buffer exists. + /* There are three cases when i->p[] buffer exists. + * (0) alias expansion in progress. * (1) it's a string in_str. * (2) It's a file, and we have a saved line editing buffer. - * In both cases, we know that i->p[0] exists and not NUL, and - * the peek2 result is in i->p[1]. + * In all cases, we know that i->p[0] exists and not NUL. + * The peek2 result is in i->p[1]. */ if (i->p) return (unsigned char)i->p[1]; @@ -4423,6 +4501,7 @@ static int done_word(struct parse_context *ctx) ctx->is_assignment = MAYBE_ASSIGNMENT; } debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]); + command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data)); debug_print_strings("word appended to argv", command->argv); @@ -4488,7 +4567,7 @@ static int parse_redir_right_fd(o_string *as_string, struct in_str *input) } d = 0; ok = 0; - while (ch != EOF && isdigit(ch)) { + while (/*ch != EOF &&*/ isdigit(ch)) { d = d*10 + (ch-'0'); ok = 1; ch = i_getch(input); @@ -5563,6 +5642,111 @@ static int encode_string(o_string *as_string, goto again; } +#if ENABLE_HUSH_ALIAS +static char* end_of_alias_name(const char *name) +{ + while (*name) { + if (!isalnum(*name) +// Uncommented chars are allowed in alias names. +// Commented out with // are disallowed in bash: space, "$&'();<=>\`| +// Commented out with //bb are allowed in bash, but disallowed in hush: !#*-/?[]{}~ +// (do you really want alias named '?' to be allowed?) +// && *name != ' ' // 20 +//bb && *name != '!' // 21 +// && *name != '"' // 22 +//bb && *name != '#' // 23 +// && *name != '$' // 24 + && *name != '%' // 25 +// && *name != '&' // 26 +// && *name != '\'' // 27 +// && *name != '(' // 28 +// && *name != ')' // 29 +//bb && *name != '*' // 2a + && *name != '+' // 2b + && *name != ',' // 2c +//bb && *name != '-' // 2d bash _can_ set it: "alias -- -=STR" (and it lists it as "alias -- -='STR'" in "alias" output!) + && *name != '.' // 2e seen Fedora defining alias "l." +//bb && *name != '/' // 2f + && *name != ':' // 3a +// && *name != ';' // 3b +// && *name != '<' // 3c +// && *name != '=' // 3d +// && *name != '>' // 3e +//bb && *name != '?' // 3f + && *name != '@' // 40 +//bb && *name != '[' // 5b +// && *name != '\\' // 5c +//bb && *name != ']' // 5d + && *name != '^' // 5e + && *name != '_' // 5f +// && *name != '`' // 60 +//bb && *name != '{' // 7b +// && *name != '|' // 7c +//bb && *name != '}' // 7d +//bb && *name != '~' // 7e + ) { + break; /* disallowed char, stop */ + } + name++; + } + return (char*)name; +} +#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) +#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) + +static struct alias **find_alias_slot(const char *name, const char *eq) +{ + unsigned len; + struct alias *alias; + struct alias **aliaspp; + + len = eq - name; + aliaspp = &G.top_alias; + while ((alias = *aliaspp) != NULL) { + //bb_error_msg("alias->str'%s' name'%.*s'", alias->str, len, name); + if (strncmp(name, alias->str, len) == 0 + && alias->str[len] == '=' + ) { + //bb_error_msg("match!"); + break; + } + aliaspp = &alias->next; + } + return aliaspp; +} + +static ALWAYS_INLINE const struct alias *find_alias(const char *name) +{ + //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); + return *find_alias_slot(name, strchr(name, '\0')); +} + +static const struct alias *word_matches_alias(struct parse_context *ctx) +{ + if (ctx->ctx_res_w != RES_CASE_BODY +/* && !ctx.command->argv - caller checked this */ + && !ctx->word.has_quoted_part + && ctx->word.data[0] != '\0' /* optimization */ + ) { + const char *word = ctx->word.data; + const char *end = end_of_alias_name(word); + if (*end == '\0') { + struct alias *alias; + + //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); + alias = *find_alias_slot(word, end); + if (alias && !alias->dont_recurse) { + alias->dont_recurse = 1; + o_reset_to_empty_unquoted(&ctx->word); + return alias; + } + } + } + return NULL; +} + +#endif /* ENABLE_HUSH_ALIAS */ + /* * Scan input until EOF or end_trigger char. * Return a list of pipes to execute, or NULL on EOF @@ -5576,6 +5760,7 @@ static struct pipe *parse_stream(char **pstring, struct in_str *input, int end_trigger) { + IF_HUSH_ALIAS(const struct alias *alias;) struct pipe *pi; struct parse_context ctx; int heredoc_cnt; @@ -5587,6 +5772,7 @@ static struct pipe *parse_stream(char **pstring, end_trigger ? end_trigger : 'X'); debug_enter(); + enable_all_aliases(); initialize_context(&ctx); /* If very first arg is "" or '', ctx.word.data may end up NULL. @@ -5695,6 +5881,18 @@ static struct pipe *parse_stream(char **pstring, } if (ch == ' ' || ch == '\t') { +#if ENABLE_HUSH_ALIAS + /* Check for alias expansion (only for first word of command) */ + if (G_interactive_fd && !ctx.command->argv) { + alias = word_matches_alias(&ctx); + if (alias) { + add_to_albuf_and_get_next_char: + i_prepend_to_alias_buffer(input, strchr(alias->str, '=') + 1, ch); + continue; /* get next char (which will be from albuf) */ + } + } +#endif + #if ENABLE_HUSH_LINENO_VAR /* "while ...; do<whitespace><newline> * CMD" @@ -5719,6 +5917,14 @@ static struct pipe *parse_stream(char **pstring, if (ch == '\n') { IF_HUSH_LINENO_VAR(ch_is_newline:) +#if ENABLE_HUSH_ALIAS + /* Check for alias expansion (only for first word of command) */ + if (G_interactive_fd && !ctx.command->argv) { + alias = word_matches_alias(&ctx); + if (alias) + goto add_to_albuf_and_get_next_char; + } +#endif if (done_word(&ctx)) goto parse_error_exitcode1; /* Is this a case when newline is simply ignored? @@ -5751,6 +5957,7 @@ static struct pipe *parse_stream(char **pstring, if (pi->num_cmds != 0 /* check #1 */ && pi->followup != PIPE_BG /* check #2 */ ) { + debug_printf_parse("newline is treated as ws\n"); continue; /* ignore newline */ } } @@ -5763,7 +5970,7 @@ static struct pipe *parse_stream(char **pstring, goto parse_error_exitcode1; } ctx.is_assignment = MAYBE_ASSIGNMENT; - debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); + debug_printf_parse("newline is treated as ';', ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); next = '\0'; ch = ';'; } else { @@ -5838,7 +6045,10 @@ static struct pipe *parse_stream(char **pstring, } term_group: if (end_trigger && end_trigger == ch - && (ch != ';' || heredoc_cnt == 0) + && (ch != ';' + /* it's ";". Can exit parse_stream() only if have no heredocs to consume, and alias buffer is empty */ + || (heredoc_cnt == 0 && !i_has_alias_buffer(input)) + ) #if ENABLE_HUSH_CASE && (ch != ')' || ctx.ctx_res_w != RES_MATCH @@ -5846,6 +6056,14 @@ static struct pipe *parse_stream(char **pstring, ) #endif ) { +#if ENABLE_HUSH_ALIAS + /* Check for alias expansion (only for first word of command) */ + if (G_interactive_fd && !ctx.command->argv) { + alias = word_matches_alias(&ctx); + if (alias) + goto add_to_albuf_and_get_next_char; + } +#endif if (done_word(&ctx)) goto parse_error_exitcode1; if (done_pipe(&ctx, PIPE_SEQ)) { @@ -5881,6 +6099,7 @@ static struct pipe *parse_stream(char **pstring, "end_trigger char found\n", ctx.list_head); debug_leave(); + i_free_alias_buffer(input); return ctx.list_head; } } @@ -6029,6 +6248,14 @@ static struct pipe *parse_stream(char **pstring, } #endif case ';': +#if ENABLE_HUSH_ALIAS + /* Check for alias expansion (only for first word of command) */ + if (G_interactive_fd && !ctx.command->argv) { + alias = word_matches_alias(&ctx); + if (alias) + goto add_to_albuf_and_get_next_char; + } +#endif if (done_word(&ctx)) goto parse_error_exitcode1; done_pipe(&ctx, PIPE_SEQ); @@ -6041,6 +6268,8 @@ static struct pipe *parse_stream(char **pstring, ctx.ctx_res_w = RES_MATCH; /* "we are in PATTERN)" */ } #endif + finished_cmd_reenable_aliases: + enable_all_aliases(); /* try: { an_alias; an_alias; } */ finished_cmd: /* We just finished a cmd. New one may start * with an assignment */ @@ -6071,7 +6300,7 @@ static struct pipe *parse_stream(char **pstring, goto parse_error_exitcode1; } } - goto finished_cmd; + goto finished_cmd_reenable_aliases; /* try: an_alias &[&] an_alias */ case '|': #if ENABLE_HUSH_CASE if (ctx.ctx_res_w == RES_MATCH) { @@ -6111,7 +6340,7 @@ static struct pipe *parse_stream(char **pstring, } done_command(&ctx); } - goto finished_cmd; + goto finished_cmd_reenable_aliases; /* try: an_alias |[|] an_alias */ case '(': #if ENABLE_HUSH_CASE /* "case... in (PATTERNS)..."? */ @@ -6130,7 +6359,7 @@ static struct pipe *parse_stream(char **pstring, goto parse_error_exitcode1; debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); heredoc_cnt += n; - goto finished_cmd; + goto finished_cmd_reenable_aliases; } case ')': #if ENABLE_HUSH_CASE @@ -6215,6 +6444,7 @@ static struct pipe *parse_stream(char **pstring, debug_leave(); debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); debug_printf_parse("parse_stream return %p: EOF\n", pi); + i_free_alias_buffer(input); return pi; parse_error_exitcode1: @@ -6254,6 +6484,7 @@ static struct pipe *parse_stream(char **pstring, *pstring = NULL; #endif debug_leave(); + i_free_alias_buffer(input); return ERR_PTR; } @@ -9082,13 +9313,14 @@ static NORETURN void pseudo_exec( */ #if BB_MMU int rcode; - debug_printf_exec("pseudo_exec: run_list\n"); + debug_printf_exec("pseudo_exec: run_list(command->group)\n"); reset_traps_to_defaults(); rcode = run_list(command->group); /* OK to leak memory by not calling free_pipe_list, * since this process is about to exit */ _exit(rcode); #else + debug_printf_exec("pseudo_exec: re_exec(command->group_as_string)\n"); re_execute_shell(&nommu_save->argv_from_re_execing, command->group_as_string, G.global_argv[0], @@ -9886,13 +10118,20 @@ static NOINLINE int run_pipe(struct pipe *pi) #if ENABLE_HUSH_LINENO_VAR G.execute_lineno = command->lineno; #endif - command->pid = BB_MMU ? fork() : vfork(); if (!command->pid) { /* child */ -#if ENABLE_HUSH_JOB disable_restore_tty_pgrp_on_exit(); - CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ +#if ENABLE_HUSH_INTERACTIVE + if (BB_MMU && pi->followup == PIPE_BG) { + /* This disables alias expansion in . FILE & */ + /* (bash does it only for (. FILE)& */ + G.interactive_fd = 0; + } +#endif + if (BB_MMU) + CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ +#if ENABLE_HUSH_JOB /* Every child adds itself to new process group * with pgid == pid_of_first_child_in_pipe */ if (G.run_list_level == 1 && G_interactive_fd) { @@ -11295,12 +11534,14 @@ static int FAST_FUNC builtin_type(char **argv) char *path = NULL; if (0) {} /* make conditional compile easier below */ - /*else if (find_alias(*argv)) - type = "an alias";*/ # if ENABLE_HUSH_FUNCTIONS else if (find_function(*argv)) type = "a function"; # endif +# if ENABLE_HUSH_ALIAS + else if (find_alias(*argv)) + type = "an alias"; +# endif else if (find_builtin(*argv)) type = "a shell builtin"; else { @@ -12594,3 +12835,106 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) return l; } #endif + +#if ENABLE_HUSH_ALIAS +static void new_alias(char *str, char *eq) +{ + struct alias *alias, **aliaspp; + + //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); + aliaspp = find_alias_slot(str, eq); + str = xstrdup(str); + alias = *aliaspp; + if (alias) { + /* Replace existing alias */ + free(alias->str); + alias->str = str; + } else { + /* Create new alias */ + alias = xzalloc(sizeof(*alias)); + alias->str = str; + *aliaspp = alias; + } +} +static int FAST_FUNC builtin_alias(char **argv) +{ + const struct alias *alias; + + if (!argv[1]) { + alias = G.top_alias; + while (alias) { +//todo: use NAME='VALUE' format, not NAME=VALUE + printf("alias %s\n", alias->str); + alias = alias->next; + } + return 0; + } + + while (*++argv) { + /* The characters /, $, `, = and any of the shell + * metacharacters or quoting characters + * may not appear in an alias name */ + char *eq = end_of_alias_name(*argv); + if (*eq == '=') { + /* alias NAME=VALUE */ + new_alias(*argv, eq); + } else { + eq = strchrnul(eq, '='); + if (*eq == '=') { + bb_error_msg("alias: '%.*s': invalid alias name", (int)(eq - *argv), *argv); + continue; /* continue processing argv (bash compat) */ + } + //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); + alias = *find_alias_slot(*argv, eq); + if (alias) { +//todo: use NAME='VALUE' format, not NAME=VALUE + printf("alias %s\n", alias->str); + } else { + bb_error_msg("unalias: '%s': not found" + 2, *argv); + /* return 1; - no, continue processing argv (bash compat) */ + } + } + } + return 0; +} + +static void unset_alias(struct alias **aliaspp) +{ + struct alias *alias = *aliaspp; + + *aliaspp = alias->next; + free(alias->str); + free(alias); +} +static int FAST_FUNC builtin_unalias(char **argv) +{ + int ret = 0; + + if (!argv[1]) { + bb_simple_error_msg("usage: unalias -a | NAME..."); + return EXIT_FAILURE; + } + + while (*++argv) { + struct alias **aliaspp; + + if (strcmp(*argv, "-a") == 0) { + /* Remove all aliases */ + while (G.top_alias) { + unset_alias(&G.top_alias); + } + /* bash: "unalias -a NO_SUCH_ALIAS" says nothing (won't try to unalias NO_SUCH_ALIAS): */ + break; + } + //bb_error_msg("%s:%d: -> find_alias_slot", __func__, __LINE__); + aliaspp = find_alias_slot(*argv, strchr(*argv, '\0')); /* think of "unalias a=b" case! */ + if (*aliaspp) { + unset_alias(aliaspp); + } else { + bb_error_msg("unalias: '%s': not found", *argv); + ret = EXIT_FAILURE; + } + } + return ret; +} +#endif |
