From f9dfb51001254f2f50ba081be791e45c7f5965a5 Mon Sep 17 00:00:00 2001
From: Ville Juven <ville.juven@unikie.com>
Date: Wed, 22 Feb 2023 18:19:59 +0200
Subject: [PATCH] nsh/nshlib: Add alias support for nsh

This adds support for string aliases into nsh. There are some nuances that
are not handled correctly yet:

- Reserved words can be overloaded, which is a clear POSIX violation
---
 nshlib/Kconfig       |  18 ++
 nshlib/Makefile      |   4 +
 nshlib/nsh.h         |  31 +++
 nshlib/nsh_alias.c   | 440 +++++++++++++++++++++++++++++++++++++++++++
 nshlib/nsh_command.c |   7 +
 nshlib/nsh_console.h |  10 +
 nshlib/nsh_parse.c   | 209 ++++++++++++++++----
 7 files changed, 683 insertions(+), 36 deletions(-)
 create mode 100644 nshlib/nsh_alias.c

diff --git a/nshlib/Kconfig b/nshlib/Kconfig
index 9607facb7..2123c1bf3 100644
--- a/nshlib/Kconfig
+++ b/nshlib/Kconfig
@@ -185,6 +185,24 @@ config NSH_DISABLEBG
 		where a minimal footprint is a necessity and background command
 		execution is not.
 
+config NSH_ALIAS
+	bool "Enable alias support"
+	default !DEFAULT_SMALL
+	---help---
+		Enable alias support for nsh. This enables command substitution from
+		a table of alias strings for individual commands. The maximum amount
+		of alias strings is configurable with NSH_ALIAS_MAX_AMOUNT.
+
+if NSH_ALIAS
+
+config NSH_ALIAS_MAX_AMOUNT
+	int "Maximum amount of aliases"
+	default 1
+	---help---
+		Set the maximum amount of alias entries.
+
+endif # NSH_ALIAS
+
 endmenu # Command Line Configuration
 
 config NSH_BUILTIN_APPS
diff --git a/nshlib/Makefile b/nshlib/Makefile
index 3987a3a7d..351002dc9 100644
--- a/nshlib/Makefile
+++ b/nshlib/Makefile
@@ -106,4 +106,8 @@ ifeq ($(CONFIG_NSH_LOGIN_PASSWD),y)
 CSRCS += nsh_passwdcmds.c
 endif
 
+ifeq ($(CONFIG_NSH_ALIAS),y)
+CSRCS += nsh_alias.c
+endif
+
 include $(APPDIR)/Application.mk
diff --git a/nshlib/nsh.h b/nshlib/nsh.h
index 50205c8cc..ddf294672 100644
--- a/nshlib/nsh.h
+++ b/nshlib/nsh.h
@@ -715,6 +715,25 @@ struct nsh_parser_s
 #endif
 };
 
+#ifdef CONFIG_NSH_ALIAS
+struct nsh_alias_s
+{
+  FAR struct nsh_alias_s *next;    /* Single link list for traversing */
+  FAR char               *name;    /* Name of the alias */
+  FAR char               *value;   /* Value behind the name */
+  union
+  {
+    struct
+    {
+      uint8_t             exp : 1; /* Already expanded ? */
+      uint8_t             rem : 1; /* Marked for deletion */
+    };
+
+    uint8_t               flags;   /* Raw value */
+  };
+};
+#endif
+
 /* This is the general form of a command handler */
 
 struct nsh_vtbl_s; /* Defined in nsh_console.h */
@@ -793,6 +812,13 @@ int nsh_usbconsole(void);
 #  define nsh_usbconsole() (-ENOSYS)
 #endif
 
+#ifdef CONFIG_NSH_ALIAS
+FAR struct nsh_alias_s *nsh_aliasfind(FAR struct nsh_vtbl_s *vtbl,
+                                      FAR const char *token);
+void nsh_aliasfree(FAR struct nsh_vtbl_s *vtbl,
+                   FAR struct nsh_alias_s *alias);
+#endif
+
 #ifndef CONFIG_NSH_DISABLESCRIPT
 int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd,
                FAR const char *path, bool log);
@@ -1193,6 +1219,11 @@ int cmd_pmconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
 #  endif
 #endif
 
+#ifdef CONFIG_NSH_ALIAS
+int cmd_alias(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
+int cmd_unalias(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv);
+#endif
+
 /****************************************************************************
  * Name: nsh_extmatch_count
  *
diff --git a/nshlib/nsh_alias.c b/nshlib/nsh_alias.c
new file mode 100644
index 000000000..3a819dd02
--- /dev/null
+++ b/nshlib/nsh_alias.c
@@ -0,0 +1,440 @@
+/****************************************************************************
+ * apps/nshlib/nsh_alias.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <nuttx/queue.h>
+
+#include "nsh.h"
+#include "nsh_console.h"
+
+#ifdef CONFIG_NSH_ALIAS
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Macro to get head of alias list */
+
+#define alias_head(list)        (FAR struct nsh_alias_s *)sq_peek(list)
+#define alias_remfirst(list)    (FAR struct nsh_alias_s *)sq_remfirst(list)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Alias message format */
+
+static const char g_aliasfmt[]    = "alias %s='%s'\n";
+static const char g_savefailfmt[] = "alias %s='%s' failed\n";
+
+/* Common for both alias / unalias */
+
+static const char g_noaliasfmt[]  = "%s: %s not found\n";
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: alias_init
+ ****************************************************************************/
+
+void alias_init(FAR struct nsh_vtbl_s *vtbl)
+{
+  int i;
+
+  if (!sq_empty(&vtbl->alist) || !sq_empty(&vtbl->afreelist))
+    {
+      /* If either list is non-empty, we are initialized already */
+
+      return;
+    }
+
+  sq_init(&vtbl->alist);
+  sq_init(&vtbl->afreelist);
+
+  for (i = 0; i < CONFIG_NSH_ALIAS_MAX_AMOUNT; i++)
+    {
+      sq_addlast((FAR struct sq_entry_s *)&vtbl->atab[i], &vtbl->afreelist);
+    }
+
+  return;
+}
+
+/****************************************************************************
+ * Name: alias_find
+ ****************************************************************************/
+
+static FAR struct nsh_alias_s *alias_find(FAR struct nsh_vtbl_s *vtbl,
+                                          FAR const char *name)
+{
+  FAR struct nsh_alias_s *alias;
+
+  for (alias = alias_head(&vtbl->alist); alias; alias = alias->next)
+    {
+      if (strcmp(alias->name, name) == 0)
+        {
+          return alias;
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: alias_delete
+ ****************************************************************************/
+
+static void alias_delete(FAR struct nsh_vtbl_s *vtbl,
+                         FAR struct nsh_alias_s *alias)
+{
+  if (alias)
+    {
+      if (alias->exp)
+        {
+          /* Mark it for removal, but keep the data intact */
+
+          alias->rem = 1;
+          return;
+        }
+
+      if (alias->name)
+        {
+          free(alias->name);
+        }
+
+      if (alias->value)
+        {
+          free(alias->value);
+        }
+
+      alias->name = NULL;
+      alias->value = NULL;
+      alias->flags = 0;
+
+      sq_rem((FAR sq_entry_t *)alias, &vtbl->alist);
+      sq_addfirst((FAR sq_entry_t *)alias, &vtbl->afreelist);
+    }
+}
+
+/****************************************************************************
+ * Name: alias_save
+ ****************************************************************************/
+
+static int alias_save(FAR struct nsh_vtbl_s *vtbl, FAR const char *name,
+                      FAR const char *value)
+{
+  FAR struct nsh_alias_s *alias;
+  int ret = OK;
+
+  if (!name || *name == '\0' || !value)
+    {
+      return -EINVAL;
+    }
+
+  if ((alias = alias_find(vtbl, name)) != NULL)
+    {
+      /* Update the value */
+
+      free(alias->value);
+      alias->value = strdup(value);
+    }
+  else if ((alias = alias_remfirst(&vtbl->afreelist)) != NULL)
+    {
+      /* Create new value */
+
+      alias->name = strdup(name);
+      alias->value = strdup(value);
+      sq_addlast((FAR sq_entry_t *)alias, &vtbl->alist);
+    }
+
+  if (!alias || !alias->name || !alias->value)
+    {
+      /* Something went wrong, clean up after ourselves */
+
+      alias_delete(vtbl, alias);
+      ret = -ENOMEM;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: alias_printall
+ ****************************************************************************/
+
+static void alias_printall(FAR struct nsh_vtbl_s *vtbl)
+{
+  FAR struct nsh_alias_s *alias;
+
+  for (alias = alias_head(&vtbl->alist); alias; alias = alias->next)
+    {
+      nsh_output(vtbl, g_aliasfmt, alias->name, alias->value);
+    }
+}
+
+/****************************************************************************
+ * Name: alias_removeall
+ ****************************************************************************/
+
+static void alias_removeall(FAR struct nsh_vtbl_s *vtbl)
+{
+  FAR struct nsh_alias_s *alias;
+  FAR struct nsh_alias_s *next;
+
+  alias = alias_head(&vtbl->alist);
+
+  while (alias)
+    {
+      next = alias->next;
+      alias_delete(vtbl, alias);
+      alias = next;
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nsh_aliasfind
+ *
+ * Description:
+ *   Find alias for token. Returns the alias structure or NULL if not found.
+ *
+ * Input Parameters:
+ *   vtbl  - NSH session data.
+ *   token - The argument string to find.
+ *
+ * Returned Value:
+ *   The alias is returned, if one found, otherwise NULL.
+ *
+ ****************************************************************************/
+
+FAR struct nsh_alias_s *nsh_aliasfind(FAR struct nsh_vtbl_s *vtbl,
+                                      FAR const char *token)
+{
+  FAR struct nsh_alias_s *alias;
+
+  /* Init, if necessary */
+
+  alias_init(vtbl);
+
+  if (token)
+    {
+      /* See if such an alias exists ? */
+
+      alias = alias_find(vtbl, token);
+      if (alias && !alias->exp && alias->value)
+        {
+          /* Yes, return the alias */
+
+          return alias;
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: nsh_aliasfree
+ *
+ * Description:
+ *   Free memory for any deleted alias, aliases are kept in memory until all
+ *   references to it have been freed.
+ *
+ * Input Parameters:
+ *   vtbl  - NSH session data.
+ *   alias - Pointer to alias data that is freed.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void nsh_aliasfree(FAR struct nsh_vtbl_s *vtbl,
+                   FAR struct nsh_alias_s *alias)
+{
+  alias_delete(vtbl, alias);
+}
+
+/****************************************************************************
+ * Name: cmd_alias
+ *
+ * Description:
+ *   Handle 'alias' command from terminal. Multiple alias values can be given
+ *   by a single command.
+ *
+ *   Command does one of three things:
+ *   1) If no arguments are given, every alias is printed to terminal.
+ *   2) If a known alias name is given, the value is printed to terminal.
+ *   3) If a "name=value" pair is given, a new alias is created, if there is
+ *      room.
+ *
+ * Input Parameters:
+ *   vtbl - The NSH console.
+ *   argc - Amount of argument strings in command.
+ *   argv - The argument strings.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int cmd_alias(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
+{
+  FAR struct nsh_alias_s *alias;
+  FAR char **arg;
+  FAR char *value;
+  int ret = OK;
+
+  /* Init, if necessary */
+
+  alias_init(vtbl);
+
+  if (argc < 2)
+    {
+      /* Print the alias list */
+
+      alias_printall(vtbl);
+      return ret;
+    }
+
+  /* Traverse through the argument vector */
+
+  for (arg = argv + 1; *arg; arg++)
+    {
+      /* Look for name=value */
+
+      if ((value = strchr(*arg, '=')) != NULL)
+        {
+          /* Save new / modify existing alias */
+
+          *value++ = '\0';
+
+          ret = alias_save(vtbl, *arg, value);
+          if (ret < 0)
+            {
+              nsh_error(vtbl, g_savefailfmt, *arg, value);
+            }
+        }
+      else if ((alias = alias_find(vtbl, *arg)) != NULL)
+        {
+          /* Found it */
+
+          nsh_output(vtbl, g_aliasfmt, alias->name, alias->value);
+        }
+      else
+        {
+          /* Nothing found */
+
+          nsh_error(vtbl, g_noaliasfmt, "alias", *arg);
+          ret = -ENOENT;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: cmd_unalias
+ *
+ * Description:
+ *   Handle 'cmd_unalias' command from terminal.
+ *
+ *   Command does one of two things:
+ *   1) If the '-a' argument is given, all aliases are destroyed.
+ *   2) If a known alias name is given, the name=value pair is destroyed.
+ *
+ * Input Parameters:
+ *   vtbl - The NSH console.
+ *   argc - Amount of argument strings in command.
+ *   argv - The argument strings.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int cmd_unalias(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
+{
+  FAR struct nsh_alias_s *alias;
+  FAR char **arg;
+  int option;
+  int ret = OK;
+
+  /* Init, if necessary */
+
+  alias_init(vtbl);
+
+  if (argc < 2)
+    {
+      /* No arguments given, this is an error */
+
+      return -EINVAL;
+    }
+
+  /* If '-a' is provided, then just wipe them all */
+
+  if ((option = getopt(argc, argv, "a")) != ERROR)
+    {
+      alias_removeall(vtbl);
+      return ret;
+    }
+
+  /* Traverse through the argument vector */
+
+  for (arg = argv + 1; *arg; arg++)
+    {
+      if ((alias = alias_find(vtbl, *arg)) != NULL)
+        {
+          /* Found it */
+
+          alias_delete(vtbl, alias);
+        }
+      else
+        {
+          /* Nothing found */
+
+          nsh_error(vtbl, g_noaliasfmt, "unalias", *arg);
+          ret = -ENOENT;
+        }
+    }
+
+  return ret;
+}
+
+#endif /* CONFIG_NSH_ALIAS */
diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c
index e5ff25289..79da9341d 100644
--- a/nshlib/nsh_command.c
+++ b/nshlib/nsh_command.c
@@ -104,6 +104,13 @@ static const struct cmdmap_s g_cmdmap[] =
   { "addroute", cmd_addroute, 3, 4, "<target> [<netmask>] <router>" },
 #endif
 
+#ifdef CONFIG_NSH_ALIAS
+  { "alias",    cmd_alias,   1, CONFIG_NSH_MAXARGUMENTS,
+    "[name[=value] ... ]" },
+  { "unalias",  cmd_unalias, 1, CONFIG_NSH_MAXARGUMENTS,
+    "[-a] name [name ... ]" },
+#endif
+
 #if defined(CONFIG_NET) && defined(CONFIG_NET_ARP) && !defined(CONFIG_NSH_DISABLE_ARP)
   { "arp",      cmd_arp,      1, 6,
     "[-i <ifname>] [-a <ipaddr>|-d <ipaddr>|-s <ipaddr> <hwaddr>]" },
diff --git a/nshlib/nsh_console.h b/nshlib/nsh_console.h
index cf5e75a89..176f9bdba 100644
--- a/nshlib/nsh_console.h
+++ b/nshlib/nsh_console.h
@@ -33,6 +33,8 @@
 #include <stdbool.h>
 #include <errno.h>
 
+#include <nuttx/queue.h>
+
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
@@ -119,6 +121,14 @@ struct nsh_vtbl_s
   char iobuffer[IOBUFFERSIZE];
 #endif
 
+#ifdef CONFIG_NSH_ALIAS
+  /* Shell alias support */
+
+  struct nsh_alias_s atab[CONFIG_NSH_ALIAS_MAX_AMOUNT];
+  struct sq_queue_s  alist;
+  struct sq_queue_s  afreelist;
+#endif
+
   /* Parser state data */
 
   struct nsh_parser_s np;
diff --git a/nshlib/nsh_parse.c b/nshlib/nsh_parse.c
index 87bf50c1a..f2cc78cd0 100644
--- a/nshlib/nsh_parse.c
+++ b/nshlib/nsh_parse.c
@@ -54,7 +54,8 @@
  */
 
 #undef HAVE_MEMLIST
-#if defined(CONFIG_NSH_CMDPARMS) || defined(CONFIG_NSH_ARGCAT)
+#if defined(CONFIG_NSH_CMDPARMS) || defined(CONFIG_NSH_ALIAS) || \
+    defined(CONFIG_NSH_ARGCAT)
 #  define HAVE_MEMLIST 1
 #endif
 
@@ -89,6 +90,19 @@
 #  define NEED_NULLSTRING       1
 #endif
 
+/* Mark already expanded aliases into a list, to prevent recursion */
+
+#ifdef CONFIG_NSH_ALIAS
+#  define NSH_ALIASLIST_TYPE       struct nsh_alist_s
+#  define NSH_ALIASLIST_INIT(l)    memset(&(l), 0, sizeof(struct nsh_alist_s))
+#  define NSH_ALIASLIST_ADD(l, a)  nsh_alist_add((l), (a))
+#  define NSH_ALIASLIST_FREE(v, l) nsh_alist_free((v), (l))
+#else
+#  define NSH_ALIASLIST_TYPE       uint8_t
+#  define NSH_ALIASLIST_INIT(l)    do { (l) = 0; } while (0)
+#  define NSH_ALIASLIST_FREE(v, l)
+#endif
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -115,6 +129,14 @@ struct nsh_memlist_s
 };
 #endif
 
+#ifdef CONFIG_NSH_ALIAS
+struct nsh_alist_s
+{
+  int nallocs;
+  FAR struct nsh_alias_s *allocs[CONFIG_NSH_ALIAS_MAX_AMOUNT];
+};
+#endif
+
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
@@ -125,6 +147,13 @@ static void nsh_memlist_add(FAR struct nsh_memlist_s *memlist,
 static void nsh_memlist_free(FAR struct nsh_memlist_s *memlist);
 #endif
 
+#ifdef CONFIG_NSH_ALIAS
+static void nsh_alist_add(FAR struct nsh_alist_s *alist,
+                          FAR struct nsh_alias_s *alias);
+static void nsh_alist_free(FAR struct nsh_vtbl_s *vtbl,
+                           FAR struct nsh_alist_s *alist);
+#endif
+
 #ifndef CONFIG_NSH_DISABLEBG
 static void nsh_releaseargs(struct cmdarg_s *arg);
 static pthread_addr_t nsh_child(pthread_addr_t arg);
@@ -155,6 +184,11 @@ static FAR char *nsh_strchr(FAR const char *str, int ch);
 #  define nsh_strchr(s,c) strchr(s,c)
 #endif
 
+#ifdef CONFIG_NSH_ALIAS
+static FAR char *nsh_aliasexpand(FAR struct nsh_vtbl_s *vtbl,
+               FAR char *cmdline, FAR NSH_ALIASLIST_TYPE *alist);
+#endif
+
 #ifdef NSH_HAVE_VARS
 static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
                FAR char *varname);
@@ -173,6 +207,7 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl,
 static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
                               FAR char **saveptr,
                               FAR NSH_MEMLIST_TYPE *memlist,
+                              FAR NSH_ALIASLIST_TYPE *alist,
                               FAR int *isenvvar);
 
 #ifndef CONFIG_NSH_DISABLESCRIPT
@@ -185,17 +220,20 @@ static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl);
 static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl);
 #ifndef CONFIG_NSH_DISABLE_LOOPS
 static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
-                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist);
+                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
+                    FAR NSH_ALIASLIST_TYPE *alist);
 #endif
 #ifndef CONFIG_NSH_DISABLE_ITEF
 static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
-                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist);
+                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
+                    FAR NSH_ALIASLIST_TYPE *alist);
 #endif
 #endif
 
 #ifndef CONFIG_NSH_DISABLEBG
 static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
-               FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist);
+               FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
+               FAR NSH_ALIASLIST_TYPE *alist);
 #endif
 
 #ifdef CONFIG_NSH_CMDPARMS
@@ -334,6 +372,57 @@ static void nsh_memlist_free(FAR struct nsh_memlist_s *memlist)
 }
 #endif
 
+/****************************************************************************
+ * Name: nsh_alist_add
+ ****************************************************************************/
+
+#ifdef CONFIG_NSH_ALIAS
+static void nsh_alist_add(FAR struct nsh_alist_s *alist,
+                          FAR struct nsh_alias_s *alias)
+{
+  if (alist && alias)
+    {
+      int index = alist->nallocs;
+      if (index < CONFIG_NSH_ALIAS_MAX_AMOUNT)
+        {
+          alias->exp = 1;
+          alist->allocs[index] = alias;
+          alist->nallocs = index + 1;
+        }
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: nsh_alist_free
+ ****************************************************************************/
+
+#ifdef CONFIG_NSH_ALIAS
+static void nsh_alist_free(FAR struct nsh_vtbl_s *vtbl,
+                           FAR struct nsh_alist_s *alist)
+{
+  if (vtbl && alist)
+    {
+      FAR struct nsh_alias_s *alias;
+      int index;
+
+      for (index = 0; index < alist->nallocs; index++)
+        {
+          alias = alist->allocs[index];
+          alias->exp = 0;
+          if (alias->rem == 1)
+            {
+              nsh_aliasfree(vtbl, alias);
+            }
+
+          alist->allocs[index] = NULL;
+        }
+
+      alist->nallocs = 0;
+    }
+}
+#endif
+
 /****************************************************************************
  * Name: nsh_releaseargs
  ****************************************************************************/
@@ -1036,6 +1125,31 @@ static FAR char *nsh_strchr(FAR const char *str, int ch)
 }
 #endif
 
+/****************************************************************************
+ * Name: nsh_aliasexpand
+ ****************************************************************************/
+
+#ifdef CONFIG_NSH_ALIAS
+static FAR char *nsh_aliasexpand(FAR struct nsh_vtbl_s *vtbl,
+               FAR char *cmdline, FAR NSH_ALIASLIST_TYPE *alist)
+{
+  FAR struct nsh_alias_s *alias;
+
+  /* Does such an alias exist ? */
+
+  alias = nsh_aliasfind(vtbl, cmdline);
+  if (alias)
+    {
+      /* Yes, expand and mark it as already expanded */
+
+      NSH_ALIASLIST_ADD(alist, alias);
+      return alias->value;
+    }
+
+  return cmdline;
+}
+#endif
+
 /****************************************************************************
  * Name: nsh_envexpand
  ****************************************************************************/
@@ -1171,7 +1285,7 @@ static FAR char *nsh_rmquotes(FAR char *qbegin, FAR char *qend)
       ch     = *ptr++;
       *dst++ = ch;
     }
-  while(ch != '\0');
+  while (ch != '\0');
 
   return qend - 2;
 }
@@ -1513,6 +1627,7 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl,
 static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
                               FAR char **saveptr,
                               FAR NSH_MEMLIST_TYPE *memlist,
+                              FAR NSH_ALIASLIST_TYPE *alist,
                               FAR int *isenvvar)
 {
   FAR char *pbegin     = *saveptr;
@@ -1523,6 +1638,7 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
   FAR char *prev;
   bool escaped;
 #endif
+  bool quoted;
 
   /* Find the beginning of the next token */
 
@@ -1578,6 +1694,7 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
        * make sure that we do not break up any quoted substrings.
        */
 
+      quoted = false;
 #ifdef CONFIG_NSH_QUOTE
       escaped = false;
 
@@ -1611,7 +1728,7 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
 
           /* Are we entering a quoted string ? */
 
-          if (nsh_strchr(g_quote_separator, *pend))
+          if ((quoted = (nsh_strchr(g_quote_separator, *pend) != NULL)))
             {
               /* Yes, find the terminator and continue from there */
 
@@ -1622,7 +1739,7 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
 
                   char qterm[2];
 
-                  qterm[0] = ptr;
+                  qterm[0] = *pend;
                   qterm[1] = '\0';
 
                   nsh_error(vtbl, g_fmtnomatching, qterm, qterm);
@@ -1669,6 +1786,15 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
 
       *saveptr = pend;
 
+#ifdef CONFIG_NSH_ALIAS
+      /* Expand aliases (if applicable) first, quoting prevents this */
+
+      if (!quoted)
+        {
+          pbegin = nsh_aliasexpand(vtbl, pbegin, alist);
+        }
+#endif
+
       /* Perform expansions as necessary for the argument */
 
       argument = nsh_argexpand(vtbl, pbegin, &allocation, isenvvar);
@@ -1769,7 +1895,8 @@ static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
 
 #if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS)
 static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
-                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist)
+                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
+                    FAR NSH_ALIASLIST_TYPE *alist)
 {
   FAR struct nsh_parser_s *np = &vtbl->np;
   FAR char *cmd = *ppcmd;
@@ -1792,7 +1919,7 @@ static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
 
           /* Get the cmd following the "while" or "until" */
 
-          *ppcmd = nsh_argument(vtbl, saveptr, memlist, 0);
+          *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, 0);
           if (*ppcmd == NULL || **ppcmd == '\0')
             {
               nsh_error(vtbl, g_fmtarginvalid, cmd);
@@ -1849,7 +1976,7 @@ static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
         {
           /* Get the cmd following the "do" -- there may or may not be one */
 
-          *ppcmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+          *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
 
           /* Verify that "do" is valid in this context */
 
@@ -1869,7 +1996,7 @@ static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
         {
           /* Get the cmd following the "done" -- there should be one */
 
-          *ppcmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+          *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
           if (*ppcmd)
             {
               nsh_error(vtbl, g_fmtarginvalid, "done");
@@ -1961,7 +2088,8 @@ errout:
 
 #if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_ITEF)
 static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
-                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist)
+                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
+                    FAR NSH_ALIASLIST_TYPE *alist)
 {
   FAR struct nsh_parser_s *np = &vtbl->np;
   FAR char *cmd = *ppcmd;
@@ -1976,7 +2104,7 @@ static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
         {
           /* Get the cmd following the if */
 
-          *ppcmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+          *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
           if (*ppcmd == NULL || **ppcmd == '\0')
             {
               nsh_error(vtbl, g_fmtarginvalid, "if");
@@ -1991,7 +2119,7 @@ static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
 
               /* Get the next cmd */
 
-              *ppcmd = nsh_argument(vtbl, saveptr, memlist, 0);
+              *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, 0);
               if (*ppcmd == NULL || **ppcmd == '\0')
                 {
                   nsh_error(vtbl, g_fmtarginvalid, "if");
@@ -2033,7 +2161,7 @@ static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
            * one.
            */
 
-          *ppcmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+          *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
 
           /* Verify that "then" is valid in this context */
 
@@ -2054,7 +2182,7 @@ static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
            * one.
            */
 
-          *ppcmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+          *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
 
           /* Verify that "else" is valid in this context */
 
@@ -2073,7 +2201,7 @@ static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
         {
           /* Get the cmd following the fi -- there should be one */
 
-          *ppcmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+          *ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
           if (*ppcmd)
             {
               nsh_error(vtbl, g_fmtarginvalid, "fi");
@@ -2129,7 +2257,8 @@ errout:
 
 #ifndef CONFIG_NSH_DISABLEBG
 static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
-                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist)
+                    FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
+                    FAR NSH_ALIASLIST_TYPE *alist)
 {
   FAR char *cmd = *ppcmd;
 
@@ -2148,10 +2277,11 @@ static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
 
           /* Get the cmd (or -d option of nice command) */
 
-          cmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+          cmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
           if (cmd && strcmp(cmd, "-d") == 0)
             {
-              FAR char *val = nsh_argument(vtbl, saveptr, memlist, NULL);
+              FAR char *val = nsh_argument(vtbl, saveptr, memlist, alist,
+                                           NULL);
               if (val)
                 {
                   FAR char *endptr;
@@ -2163,7 +2293,7 @@ static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
                       return ERROR;
                     }
 
-                  cmd = nsh_argument(vtbl, saveptr, memlist, NULL);
+                  cmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
                 }
             }
 
@@ -2196,6 +2326,7 @@ static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
                              FAR const char *redirfile)
 {
   NSH_MEMLIST_TYPE memlist;
+  NSH_ALIASLIST_TYPE alist;
   FAR char *argv[MAX_ARGV_ENTRIES];
   FAR char *saveptr;
   FAR char *cmd;
@@ -2210,6 +2341,7 @@ static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
 
   memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
   NSH_MEMLIST_INIT(memlist);
+  NSH_ALIASLIST_INIT(alist);
 
   /* If any options like nice, redirection, or backgrounding are attempted,
    * these will not be recognized and will just be passed through as
@@ -2233,7 +2365,7 @@ static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
   /* Parse out the command at the beginning of the line */
 
   saveptr = cmdline;
-  cmd = nsh_argument(vtbl, &saveptr, &memlist, NULL);
+  cmd = nsh_argument(vtbl, &saveptr, &memlist, &alist, NULL);
 
   /* Check if any command was provided -OR- if command processing is
    * currently disabled.
@@ -2268,7 +2400,7 @@ static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
   argv[0] = cmd;
   for (argc = 1; argc < MAX_ARGV_ENTRIES - 1; argc++)
     {
-      argv[argc] = nsh_argument(vtbl, &saveptr, &memlist, NULL);
+      argv[argc] = nsh_argument(vtbl, &saveptr, &memlist, &alist, NULL);
       if (!argv[argc])
         {
           break;
@@ -2297,6 +2429,7 @@ exit:
 #endif
   vtbl->np.np_redirect = redirsave;
 
+  NSH_ALIASLIST_FREE(vtbl, &alist);
   NSH_MEMLIST_FREE(&memlist);
   return ret;
 }
@@ -2313,6 +2446,7 @@ exit:
 static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
 {
   NSH_MEMLIST_TYPE memlist;
+  NSH_ALIASLIST_TYPE alist;
   FAR char *argv[MAX_ARGV_ENTRIES];
   FAR char *saveptr;
   FAR char *cmd;
@@ -2326,6 +2460,7 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
 
   memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
   NSH_MEMLIST_INIT(memlist);
+  NSH_ALIASLIST_INIT(alist);
 
 #ifndef CONFIG_NSH_DISABLEBG
   vtbl->np.np_bg       = false;
@@ -2336,26 +2471,26 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
   /* Parse out the command at the beginning of the line */
 
   saveptr = cmdline;
-  cmd = nsh_argument(vtbl, &saveptr, &memlist, NULL);
+  cmd = nsh_argument(vtbl, &saveptr, &memlist, &alist, NULL);
 
 #ifndef CONFIG_NSH_DISABLESCRIPT
 #ifndef CONFIG_NSH_DISABLE_LOOPS
   /* Handle while-do-done and until-do-done loops */
 
-  if (nsh_loop(vtbl, &cmd, &saveptr, &memlist) != 0)
+  if (nsh_loop(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
     {
-      NSH_MEMLIST_FREE(&memlist);
-      return nsh_saveresult(vtbl, true);
+      ret = nsh_saveresult(vtbl, true);
+      goto dynlist_free;
     }
 #endif
 
 #ifndef CONFIG_NSH_DISABLE_ITEF
   /* Handle if-then-else-fi */
 
-  if (nsh_itef(vtbl, &cmd, &saveptr, &memlist) != 0)
+  if (nsh_itef(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
     {
-      NSH_MEMLIST_FREE(&memlist);
-      return nsh_saveresult(vtbl, true);
+      ret = nsh_saveresult(vtbl, true);
+      goto dynlist_free;
     }
 
 #endif
@@ -2364,10 +2499,10 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
   /* Handle nice */
 
 #ifndef CONFIG_NSH_DISABLEBG
-  if (nsh_nice(vtbl, &cmd, &saveptr, &memlist) != 0)
+  if (nsh_nice(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
     {
-      NSH_MEMLIST_FREE(&memlist);
-      return nsh_saveresult(vtbl, true);
+      ret = nsh_saveresult(vtbl, true);
+      goto dynlist_free;
     }
 #endif
 
@@ -2386,8 +2521,8 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
        * status.
        */
 
-      NSH_MEMLIST_FREE(&memlist);
-      return OK;
+      ret = OK;
+      goto dynlist_free;
     }
 
   /* Parse all of the arguments following the command name.  The form
@@ -2409,7 +2544,7 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
     {
       int isenvvar = 0; /* flag for if an environment variable gets expanded */
 
-      argv[argc] = nsh_argument(vtbl, &saveptr, &memlist, &isenvvar);
+      argv[argc] = nsh_argument(vtbl, &saveptr, &memlist, &alist, &isenvvar);
 
       if (!argv[argc])
         {
@@ -2526,6 +2661,8 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
       vtbl->np.np_redirect = redirect_save;
     }
 
+dynlist_free:
+  NSH_ALIASLIST_FREE(vtbl, &alist);
   NSH_MEMLIST_FREE(&memlist);
   return ret;
 }