Add shell aliases via lush.aliases table (issue #28)

Aliases rewrite the command string before pipeline splitting, so alias
values may contain pipes (e.g. lush.aliases.foo = "echo hi |"). Expansion
happens once per dispatch to prevent recursion.
This commit is contained in:
Cormac Shannon
2026-03-20 22:58:01 +00:00
parent e115a061bc
commit 68a5273094
2 changed files with 151 additions and 0 deletions

70
lcmd.c
View File

@@ -897,6 +897,67 @@ static int try_builtin (lua_State *L, ParsedArgs *pa) {
}
/* ===== alias expansion ===== */
/*
** Extract the first whitespace-delimited word from cmd, look it up in
** lush.aliases. If found, replace the command string on the Lua stack
** (position 1) with alias_value + remaining_text, and update *cmd.
** Expansion happens on the raw string, before pipeline splitting,
** so alias values may contain pipes.
** Returns 1 if expanded, 0 if no alias.
*/
static int expand_alias (lua_State *L, const char **cmd) {
const char *s = *cmd;
const char *rest;
size_t wordlen;
const char *alias;
size_t alias_len, rest_len, total;
char *expanded;
/* skip leading whitespace */
while (*s == ' ' || *s == '\t') s++;
if (*s == '\0') return 0;
/* find end of first word */
rest = s;
while (*rest && *rest != ' ' && *rest != '\t') rest++;
wordlen = (size_t)(rest - s);
/* look up in lush.aliases */
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_LUSH);
lua_getfield(L, -1, "aliases");
lua_pushlstring(L, s, wordlen);
lua_gettable(L, -2);
if (!lua_isstring(L, -1)) {
lua_pop(L, 3);
return 0;
}
alias = lua_tolstring(L, -1, &alias_len);
rest_len = strlen(rest); /* includes leading space if any */
total = alias_len + rest_len;
expanded = (char *)malloc(total + 1);
if (expanded == NULL) {
lua_pop(L, 3);
return 0;
}
memcpy(expanded, alias, alias_len);
memcpy(expanded + alias_len, rest, rest_len);
expanded[total] = '\0';
lua_pop(L, 3);
/* replace command on Lua stack */
lua_pushstring(L, expanded);
lua_replace(L, 1);
free(expanded);
*cmd = lua_tostring(L, 1);
return 1;
}
/* ===== lushCmd_command ===== */
int lushCmd_command (lua_State *L) {
@@ -910,6 +971,9 @@ int lushCmd_command (lua_State *L) {
DynBuf buf_out, buf_err;
struct sigaction sa_old, sa_new;
/* expand alias before pipeline splitting */
expand_alias(L, &cmd);
/* try to split into pipeline stages */
if (split_pipeline(cmd, stages, &nstages) != 0)
return luaL_error(L, "invalid pipeline syntax in command");
@@ -1076,6 +1140,9 @@ int lushCmd_interactive (lua_State *L) {
struct sigaction sa_old_pipe, sa_old_int, sa_old_quit, sa_new;
DynBuf buf_out, buf_err;
/* expand alias before pipeline splitting */
expand_alias(L, &cmd);
/* try to split into pipeline stages */
if (split_pipeline(cmd, stages, &nstages) != 0)
return luaL_error(L, "invalid pipeline syntax in command");
@@ -1217,6 +1284,9 @@ static const luaL_Reg lushlib[] = {
LUAMOD_API int luaopen_lush (lua_State *L) {
luaL_newlib(L, lushlib);
/* create aliases subtable */
lua_createtable(L, 0, 4);
lua_setfield(L, -2, "aliases");
/* intern function name strings for OP_LUSH VM access */
lushname[LUSH_OP_COMMAND] = luaS_new(L, "command");
lushname[LUSH_OP_INTERACTIVE] = luaS_new(L, "interactive");