summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Wiles <keith.wiles@intel.com>2017-09-09 18:02:42 -0500
committerKeith Wiles <keith.wiles@intel.com>2017-11-25 07:43:12 -0600
commit6c50bbbcb009d6d5d4707b6ef50a55d435c80a1f (patch)
tree99c4f2a80c2537bb20141e8b1e3a7d8191de4309
parentc3c6aa6c3966ecee912d72a2ca3806fb89b28ebb (diff)
downloaddpdk-draft-cli-6c50bbbcb009d6d5d4707b6ef50a55d435c80a1f.zip
dpdk-draft-cli-6c50bbbcb009d6d5d4707b6ef50a55d435c80a1f.tar.gz
dpdk-draft-cli-6c50bbbcb009d6d5d4707b6ef50a55d435c80a1f.tar.xz
cli: new CLI code
-rw-r--r--lib/librte_cli/Makefile77
-rw-r--r--lib/librte_cli/README3
-rw-r--r--lib/librte_cli/cli.c1011
-rw-r--r--lib/librte_cli/cli.h1167
-rw-r--r--lib/librte_cli/cli.rst597
-rw-r--r--lib/librte_cli/cli_auto_complete.c270
-rw-r--r--lib/librte_cli/cli_auto_complete.h61
-rw-r--r--lib/librte_cli/cli_cmap.c377
-rw-r--r--lib/librte_cli/cli_cmap.h207
-rw-r--r--lib/librte_cli/cli_cmds.c749
-rw-r--r--lib/librte_cli/cli_cmds.h61
-rw-r--r--lib/librte_cli/cli_common.h90
-rw-r--r--lib/librte_cli/cli_env.c246
-rw-r--r--lib/librte_cli/cli_env.h189
-rw-r--r--lib/librte_cli/cli_file.c301
-rw-r--r--lib/librte_cli/cli_file.h273
-rw-r--r--lib/librte_cli/cli_gapbuf.c181
-rw-r--r--lib/librte_cli/cli_gapbuf.h715
-rw-r--r--lib/librte_cli/cli_help.c139
-rw-r--r--lib/librte_cli/cli_help.h150
-rw-r--r--lib/librte_cli/cli_history.c261
-rw-r--r--lib/librte_cli/cli_history.h191
-rw-r--r--lib/librte_cli/cli_lib.rst632
-rw-r--r--lib/librte_cli/cli_map.c317
-rw-r--r--lib/librte_cli/cli_map.h134
-rw-r--r--lib/librte_cli/cli_scrn.c209
-rw-r--r--lib/librte_cli/cli_scrn.h360
-rw-r--r--lib/librte_cli/cli_search.c333
-rw-r--r--lib/librte_cli/cli_search.h237
-rw-r--r--lib/librte_cli/cli_string_fns.c574
-rw-r--r--lib/librte_cli/cli_string_fns.h314
-rw-r--r--lib/librte_cli/cli_vt100.c150
-rw-r--r--lib/librte_cli/cli_vt100.h290
-rw-r--r--lib/librte_cli/cli_vt100_keys.c304
-rw-r--r--lib/librte_cli/cli_vt100_keys.h60
-rw-r--r--lib/librte_cli/rte_cli_version.map6
36 files changed, 11236 insertions, 0 deletions
diff --git a/lib/librte_cli/Makefile b/lib/librte_cli/Makefile
new file mode 100644
index 0000000..5a77d3e
--- /dev/null
+++ b/lib/librte_cli/Makefile
@@ -0,0 +1,77 @@
+# BSD LICENSE
+#
+# Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_cli.a
+
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
+
+EXPORT_MAP := rte_cli_version.map
+
+LIBABIVER := 1
+
+# all source are stored in SRCS-y
+SRCS-y += cli.c
+SRCS-y += cli_cmds.c
+SRCS-y += cli_map.c
+SRCS-y += cli_gapbuf.c
+SRCS-y += cli_file.c
+SRCS-y += cli_env.c
+SRCS-y += cli_auto_complete.c
+SRCS-y += cli_help.c
+SRCS-y += cli_history.c
+SRCS-y += cli_search.c
+SRCS-y += cli_cmap.c
+SRCS-y += cli_vt100.c
+SRCS-y += cli_vt100_keys.c
+SRCS-y += cli_scrn.c
+SRCS-y += cli_string_fns.c
+
+CFLAGS += -D_GNU_SOURCE
+
+# install includes
+SYMLINK-y-include += cli.h
+SYMLINK-y-include += cli_common.h
+SYMLINK-y-include += cli_map.h
+SYMLINK-y-include += cli_gapbuf.h
+SYMLINK-y-include += cli_file.h
+SYMLINK-y-include += cli_env.h
+SYMLINK-y-include += cli_search.h
+SYMLINK-y-include += cli_help.h
+SYMLINK-y-include += cli_history.h
+SYMLINK-y-include += cli_vt100.h
+SYMLINK-y-include += cli_vt100_keys.h
+SYMLINK-y-include += cli_scrn.h
+SYMLINK-y-include += cli_string_fns.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_cli/README b/lib/librte_cli/README
new file mode 100644
index 0000000..868b89f
--- /dev/null
+++ b/lib/librte_cli/README
@@ -0,0 +1,3 @@
+The cli directory is a simple command line interface.
+
+Please read the cli.rst and libcli.rst files for more details.
diff --git a/lib/librte_cli/cli.c b/lib/librte_cli/cli.c
new file mode 100644
index 0000000..b069f9a
--- /dev/null
+++ b/lib/librte_cli/cli.c
@@ -0,0 +1,1011 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <poll.h>
+#include <string.h>
+
+#include <rte_version.h>
+#include <rte_timer.h>
+#include <rte_log.h>
+#include <rte_string_fns.h>
+
+#include "cli.h"
+#include "cli_string_fns.h"
+
+#ifdef RTE_LIBRTE_LUA
+#ifdef CLI_STANDALONE
+#include "lua.h"
+#include "lauxlib.h"
+#else
+#include <lua.h>
+#include <lauxlib.h>
+#endif
+#endif
+
+RTE_DEFINE_PER_LCORE(struct cli *, cli);
+
+int
+cli_use_timers(void)
+{
+ if (!this_cli)
+ return 1;
+ return this_cli->flags & CLI_USE_TIMERS;
+}
+
+int
+cli_nodes_unlimited(void)
+{
+ if (!this_cli)
+ return 0;
+ return this_cli->flags & CLI_NODES_UNLIMITED;
+}
+
+int
+cli_yield_io(void)
+{
+ if (!this_cli)
+ return 1;
+ return this_cli->flags & CLI_YIELD_IO;
+}
+
+/* The CLI write routine, using write() call */
+void
+cli_write(const void *msg, int len)
+{
+ if (!msg)
+ return;
+
+ if (len <= 0) {
+ len = strlen(msg);
+ if (!len)
+ return;
+ }
+
+ if (write(fileno(this_scrn->fd_out), msg, len) != len)
+ cli_printf("write() in cli_write() failed\n");
+}
+
+/* Allocate a node from the CLI node pool */
+static inline struct cli_node *
+cli_alloc(void)
+{
+ struct cli *cli = this_cli;
+ struct cli_node *node;
+
+ node = (struct cli_node *)TAILQ_FIRST(&cli->free_nodes);
+ if (node)
+ TAILQ_REMOVE(&cli->free_nodes, node, next);
+ else if (cli_nodes_unlimited()) {
+ struct cli_node *orig = cli->node_mem;
+ size_t size;
+
+ size = (cli->nb_nodes + CLI_DEFAULT_NB_NODES) *
+ sizeof(struct cli_node);
+ cli->node_mem = realloc(cli->node_mem, size);
+ if (cli->node_mem == NULL) {
+ cli->node_mem = orig;
+ return NULL;
+ }
+ if (cli->node_mem == orig) {
+ uint32_t i;
+
+ node = &cli->node_mem[cli->nb_nodes];
+ for (i = 0; i < CLI_DEFAULT_NB_NODES; i++, node++)
+ TAILQ_INSERT_TAIL(&cli->free_nodes, node, next);
+ cli->nb_nodes += CLI_DEFAULT_NB_NODES;
+ } else
+ return NULL;
+ }
+
+ return node;
+}
+
+/* Free a node back to the CLI mempool */
+static inline void
+cli_free(struct cli_node *node)
+{
+ TAILQ_INSERT_TAIL(&this_cli->free_nodes, node, next);
+}
+
+/* Add a directory to the executable path */
+int
+cli_add_bin(struct cli_node *dir)
+{
+ struct cli *cli = this_cli;
+ int i;
+
+ if (!dir || !is_directory(dir))
+ return -1;
+
+ /* Skip first special entry for current working directory */
+ for (i = 1; i < CLI_MAX_BINS; i++)
+ if (cli->bins[i] == NULL) {
+ cli->bins[i] = dir;
+ return 0;
+ }
+ return -1;
+}
+
+/* Remove a directory from the executable path */
+int
+cli_del_bin(struct cli_node *dir)
+{
+ struct cli *cli = this_cli;
+ int i;
+
+ if (!dir || !is_directory(dir))
+ return -1;
+
+ for (i = 0; i < CLI_MAX_BINS; i++)
+ if (cli->bins[i] == dir) {
+ cli->bins[i] = NULL;
+
+ /* compress the list of directories */
+ if ((i + 1) < CLI_MAX_BINS) {
+ memmove(&cli->bins[i], &cli->bins[i + 1],
+ (CLI_MAX_BINS - (i + 1)) *
+ sizeof(void *));
+ cli->bins[CLI_MAX_BINS - 1] = NULL;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+/* Add a directory to the executable path using the path string */
+int
+cli_add_bin_path(const char *path)
+{
+ struct cli_node *node;
+
+ if (cli_find_node(path, &node)) {
+ if (cli_add_bin(node))
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+/* Helper routine to remove nodes to the CLI tree */
+int
+cli_remove_node(struct cli_node *node)
+{
+ struct cli_node *parent, *n;
+
+ if (!node)
+ return 0;
+
+ parent = node->parent;
+ if (!parent) /* Can not remove '/' or root */
+ return -1;
+
+ switch (node->type) {
+ case CLI_DIR_NODE:
+ if (!TAILQ_EMPTY(&node->items))
+ while (!TAILQ_EMPTY(&node->items)) {
+ n = TAILQ_FIRST(&node->items);
+ if (cli_remove_node(n))
+ return -1;
+ }
+ break;
+ case CLI_CMD_NODE:
+ case CLI_FILE_NODE:
+ case CLI_ALIAS_NODE:
+ case CLI_STR_NODE:
+ break;
+ default:
+ return -1;
+ }
+
+ if (is_directory(node))
+ cli_del_bin(node);
+
+ TAILQ_REMOVE(&parent->items, node, next);
+ cli_free(node);
+
+ return 0;
+}
+
+/* Helper routine to add nodes to the CLI tree */
+static struct cli_node *
+__add_node(const char *name, struct cli_node *parent,
+ int type, cli_funcs_t func, const char *short_desc)
+{
+ struct cli_node *node;
+
+ if (!name)
+ return NULL;
+
+ switch (type) {
+ case CLI_DIR_NODE:
+ if (parent && (strcmp(name, CLI_ROOT_NAME) == 0))
+ return NULL;
+ if (!parent && strcmp(name, CLI_ROOT_NAME))
+ return NULL;
+ if (func.cfunc)
+ return NULL;
+ break;
+ case CLI_CMD_NODE:
+ if (!parent || !func.cfunc)
+ return NULL;
+ break;
+ case CLI_FILE_NODE:
+ if (!parent || !func.ffunc)
+ return NULL;
+ break;
+ case CLI_ALIAS_NODE:
+ if (!parent || func.cfunc)
+ return NULL;
+ break;
+ case CLI_STR_NODE:
+ if (!func.sfunc && !short_desc)
+ return NULL;
+ break;
+ default:
+ return NULL;
+ }
+
+ node = cli_alloc();
+ if (node == NULL) {
+ cli_printf("%s: No nodes left\n", __func__);
+ return NULL;
+ }
+
+ node->type = type;
+ node->parent = parent;
+
+ switch (type) {
+ case CLI_CMD_NODE:
+ case CLI_ALIAS_NODE:
+ node->cfunc = func.cfunc;
+ break;
+ case CLI_FILE_NODE:
+ node->ffunc = func.ffunc;
+ break;
+ case CLI_DIR_NODE:
+ case CLI_STR_NODE:
+ break;
+ default:
+ break;
+ }
+ node->short_desc = short_desc;
+ snprintf(node->name, sizeof(node->name), "%s", name);
+ node->name_sz = strlen(node->name);
+
+ if (parent)
+ TAILQ_INSERT_HEAD(&parent->items, node, next);
+
+ return node;
+}
+
+/* Add a direcrtory to the CLI tree */
+struct cli_node *
+cli_add_dir(const char *name, struct cli_node *dir)
+{
+ struct cli *cli = this_cli;
+ char *argv[CLI_MAX_ARGVS], *p;
+ char path[CLI_MAX_PATH_LENGTH];
+ int cnt, i;
+ struct cli_node *n, *ret;
+ cli_funcs_t funcs;
+
+ RTE_ASSERT(cli != NULL);
+ if (!name)
+ return NULL;
+
+ /* return the last node if directory path already exists */
+ if (cli_find_node((char *)(uintptr_t)name, &ret))
+ return ret;
+
+ /* Set the function structure to NULL */
+ funcs.cfunc = NULL;
+
+ p = cli->scratch;
+ if (!dir) /* Passed in a NULL to start at root node */
+ dir = cli->root.tqh_first;
+
+ memset(path, '\0', sizeof(path));
+
+ p = cli->scratch;
+
+ /* Grab a local copy of the directory path */
+ strncpy(p, name, CLI_MAX_SCRATCH_LENGTH);
+
+ if (p[0] == '/') { /* Start from root */
+ dir = cli->root.tqh_first;
+ p++; /* Skip the / in the orignal path */
+ path[0] = '/'; /* Add root to the path */
+ }
+
+ cnt = rte_strtok(p, "/", argv, CLI_MAX_ARGVS);
+
+ n = NULL;
+ for (i = 0; i < cnt; i++) {
+ /* Append each directory part to the search path */
+ strcat(path, argv[i]);
+
+ if (cli_find_node(path, &ret)) {
+ dir = ret;
+ continue;
+ }
+
+ n = __add_node(argv[i], dir, CLI_DIR_NODE, funcs, NULL);
+ if (n == NULL)
+ break;
+ dir = n;
+ }
+ return n;
+}
+
+/* Add a command executable to the CLI tree */
+struct cli_node *
+cli_add_cmd(const char * name,
+ struct cli_node *dir, cli_cfunc_t func,
+ const char *short_desc)
+{
+ cli_funcs_t funcs;
+
+ funcs.cfunc = func;
+ return __add_node(name, dir, CLI_CMD_NODE, funcs, short_desc);
+}
+
+/* Add a command alias executable to the CLI tree */
+struct cli_node *
+cli_add_alias(const char *name,
+ struct cli_node *dir,
+ const char *line, const char *short_desc)
+{
+ struct cli_node *alias;
+ cli_funcs_t funcs;
+
+ funcs.cfunc = NULL;
+ alias = __add_node(name, dir, CLI_ALIAS_NODE, funcs, short_desc);
+ alias->alias_str = (const char *)strdup(line);
+
+ return alias;
+}
+
+/* Add a file to the CLI tree */
+struct cli_node *
+cli_add_file(const char * name,
+ struct cli_node *dir, cli_ffunc_t func,
+ const char *short_desc)
+{
+ cli_funcs_t funcs;
+
+ funcs.ffunc = func;
+ return __add_node(name, dir, CLI_FILE_NODE, funcs, short_desc);
+}
+
+/* Add a string to the CLI tree */
+int
+cli_add_str(const char * name, cli_sfunc_t func, const char *str)
+{
+ return cli_env_string(this_cli->env, name, func, str);
+}
+
+/* Add a directory/commands/files/... to a directory */
+int
+cli_add_tree(struct cli_node *parent, struct cli_tree *tree)
+{
+ struct cli *cli = this_cli;
+ struct cli_tree *t;
+ struct cli_dir *d;
+ struct cli_cmd *c;
+ struct cli_file *f;
+ struct cli_alias *a;
+ struct cli_str *s;
+ struct cli_node *n;
+
+ if (!tree)
+ return -1;
+
+ if (!parent)
+ parent = cli->root.tqh_first;
+
+ for (t = tree; t->type != CLI_UNK_NODE; t++) {
+ switch (t->type) {
+ case CLI_DIR_NODE:
+ d = &t->dir;
+
+ if (!(n = cli_add_dir(d->name, parent))) {
+ RTE_LOG(INFO, EAL,
+ "Add directory %s failed\n", d->name);
+ return -1;
+ }
+
+ parent = n;
+ break;
+
+ case CLI_CMD_NODE:
+ c = &t->cmd;
+ if (!cli_add_cmd(c->name, parent,
+ c->cfunc, c->short_desc)) {
+ RTE_LOG(INFO, EAL,
+ "Add command %s failed\n", c->name);
+ return -1;
+ }
+ break;
+
+ case CLI_FILE_NODE:
+ f = &t->file;
+ if (!cli_add_file(f->name, parent,
+ f->ffunc, f->short_desc)) {
+ RTE_LOG(INFO, EAL,
+ "Add file %s failed\n", f->name);
+ return -1;
+ }
+ break;
+
+ case CLI_ALIAS_NODE:
+ a = &t->alias;
+ if (!cli_add_alias(a->name, parent,
+ a->alias_atr, a->short_desc)) {
+ RTE_LOG(INFO, EAL,
+ "Add alias %s failed\n", a->name);
+ return -1;
+ }
+ break;
+
+ case CLI_STR_NODE:
+ s = &t->str;
+ if (cli_add_str(s->name, s->sfunc, s->string)) {
+ RTE_LOG(INFO, EAL,
+ "Add string %s failed\n", s->name);
+ return -1;
+ }
+ break;
+
+ case CLI_UNK_NODE:
+ default:
+ RTE_LOG(INFO, EAL, "Unknown Node type %d\n", t->type);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* execute a command or alias node in the CLI tree */
+int
+cli_execute(void)
+{
+ struct cli *cli = this_cli;
+ struct cli_node *node;
+ int argc, ret, sz;
+ struct gapbuf *gb = cli->gb;
+ char *line, *p, *hist;
+
+ RTE_ASSERT(cli != NULL);
+
+ sz = gb_data_size(gb);
+ sz = RTE_MAX(sz, CLI_MAX_PATH_LENGTH);
+
+ line = alloca(sz + 1);
+ if (!line)
+ return -1;
+
+ memset(line, '\0', sz + 1);
+
+ /* gb_copy_to_buf() forces linebuf to be null terminated */
+ gb_copy_to_buf(gb, line, sz);
+
+ /* Trim the string of whitspace on front and back */
+ p = rte_strtrim(line);
+ if (!strlen(p))
+ return 0;
+
+ if (p[0] == '#') /* Found a comment line starting with a '#' */
+ return 0;
+ else if (p[0] == '!') { /* History command */
+ hist = cli_history_line(atoi(&p[1]));
+ if (!hist) {
+ cli_printf("Invalid number %d\n", atoi(&p[1]));
+ return 0;
+ }
+ /* History lines are already trimmed and ready to be executed */
+ strcpy(line, hist);
+#ifdef RTE_CLI_HOST_COMMANDS
+ } else if (p[0] == '@') { /* System execute a command */
+ ret = cli_system(&p[1]);
+ if (!ret)
+ cli_history_add(p);
+ return ret;
+#endif
+ } else
+ cli_history_add(p);
+
+ /* Process the line for environment variable substitution */
+ cli_env_substitution(cli->env, p, sz - (p - line));
+
+ argc = rte_strqtok(p, " \r\n", cli->argv, CLI_MAX_ARGVS);
+
+ if (!argc)
+ return 0;
+
+ node = cli_find_cmd(cli->argv[0]);
+ if (!node) {
+ cli_printf("** command not found (%s)\n", cli->argv[0]);
+ return -1;
+ }
+
+ ret = -1;
+ switch (node->type) {
+ case CLI_CMD_NODE:
+ cli->exe_node = node;
+ ret = node->cfunc(argc, cli->argv);
+ cli->exe_node = NULL;
+ break;
+
+ case CLI_ALIAS_NODE:
+ /* Delete the alias history line just added */
+ cli_history_del();
+
+ cli->scratch[0] = '\0'; /* Reset scratch to empty */
+
+ /* If there is more data after command name save it */
+ if (gb_data_size(gb) > node->name_sz)
+ gb_copy_to_buf(cli->gb, cli->scratch, gb_data_size(gb));
+
+ sz = strlen(cli->scratch);
+
+ gb_reset_buf(gb);
+
+ gb_str_insert(gb, (char *)(uintptr_t)node->alias_str,
+ strlen(node->alias_str));
+
+ /* Add the extra line arguments */
+ sz = sz - node->name_sz;
+ if (sz > 0)
+ gb_str_insert(gb, &cli->scratch[node->name_sz], sz);
+ ret = cli_execute();
+ break;
+
+ case CLI_DIR_NODE:
+ cli_printf("** (%s) is a directory\n", cli->argv[0]);
+ break;
+
+ case CLI_FILE_NODE:
+ cli_printf("** (%s) is a file\n", cli->argv[0]);
+ break;
+
+ case CLI_STR_NODE:
+ cli_printf("** (%s) is a string\n", cli->argv[0]);
+ break;
+
+ case CLI_UNK_NODE:
+ default:
+ cli_printf("** unknown type (%s)\n", cli->argv[0]);
+ break;
+ }
+ cli_history_reset();
+ return ret;
+}
+
+/* Process the input for the CLI from the user */
+void
+cli_input(char *str, int n)
+{
+ char c;
+ int ret;
+
+ RTE_ASSERT(this_cli->gb != NULL);
+ RTE_ASSERT(str != NULL);
+
+ while (n--) {
+ c = *str++;
+
+ ret = vt100_parse_input(this_cli->vt, c);
+
+ if (ret < 0) {
+ if (ret == VT100_DONE)
+ if ((c >= ' ') && (c <= '~')) {
+ /* Output the character typed */
+ cli_write(&c, 1);
+
+ /* Add the character to the buffer */
+ gb_insert(this_cli->gb, c);
+
+ /* Display the rest on insert */
+ cli_save_cursor();
+ cli_display_right();
+ cli_restore_cursor();
+ }
+ } else if (ret != 0)
+ /* Found a vt100 key sequence, execute function */
+ if (ret < VT100_MAX_KEYS)
+ vt100_cmd_list[ret].func();
+ }
+}
+
+/* Poll the I/O routine for characters */
+static int
+cli_poll(char *c)
+{
+ struct pollfd fds;
+
+ fds.fd = fileno(this_scrn->fd_in);
+ fds.events = POLLIN;
+ fds.revents = 0;
+
+ if (cli_use_timers())
+ rte_timer_manage();
+
+ if (poll(&fds, 1, 0)) {
+ if ((fds.revents & (POLLERR | POLLNVAL)) == 0) {
+ if ((fds.revents & POLLHUP))
+ this_cli->quit_flag = 1;
+ else if ((fds.revents & POLLIN))
+ if (read(fds.fd, c, 1) > 0)
+ return 1;
+ } else
+ this_cli->quit_flag = 1;
+ }
+ return 0;
+}
+
+/* Display a prompt and wait for a key press */
+char
+cli_pause(const char *msg, const char *keys)
+{
+ char prompt[128], c;
+
+ prompt[0] = '\0';
+
+ if (msg) {
+ strcpy(prompt, msg);
+ strcat(prompt, ": ");
+ cli_printf("%s", prompt);
+ }
+
+ if (!keys)
+ keys = " qQ\n\r" vt100_q_escape;
+
+ do {
+ if (cli_poll(&c))
+ if (strchr(keys, c)) {
+ /* clear the line of the prompt */
+ cli_printf("\r%*s\r", (int)strlen(prompt), " ");
+ return c;
+ }
+ } while (this_cli->quit_flag == 0);
+
+ return '\0';
+}
+
+/* Main entry point into the CLI system to start accepting user input */
+void
+cli_start(const char *msg)
+{
+ char c;
+
+ RTE_ASSERT(this_cli != NULL);
+
+ cli_printf("\n** Version: %s, %s with%s timers\n", rte_version(),
+ (msg == NULL) ? "Command Line Interface" : msg,
+ (this_cli->flags & CLI_USE_TIMERS) ? "" : "out");
+
+ this_cli->prompt(0);
+
+ cli_execute_cmdfiles();
+
+ for (this_cli->quit_flag = 0; this_cli->quit_flag == 0; )
+ if (cli_poll(&c))
+ cli_input(&c, 1);
+
+ cli_printf("\n");
+}
+
+void
+cli_start_with_timers(const char *msg)
+{
+ RTE_ASSERT(this_cli != NULL);
+
+ this_cli->flags |= CLI_USE_TIMERS;
+
+ cli_start(msg);
+}
+
+/* Create a CLI root node for the tree */
+struct cli_node *
+cli_create_root(const char *dirname)
+{
+ struct cli_node *root;
+ cli_funcs_t funcs;
+
+ funcs.cfunc = NULL;
+
+ /* Create and add the root directory */
+ root = __add_node(dirname, NULL, CLI_DIR_NODE, funcs, NULL);
+ if (!root)
+ return NULL;
+
+ TAILQ_INSERT_HEAD(&this_cli->root, root, next);
+
+ /* point at the root directory for current working directory */
+ set_cwd(root);
+
+ return root;
+}
+
+/* Default CLI prompt routine */
+static void
+__default_prompt(int cont)
+{
+ char *str = cli_cwd_path();
+
+ if (strlen(str) > 1) /* trim the trailing '/' from string */
+ str[strlen(str) - 1] = '\0';
+
+ vt100_color(SCRN_GREEN, SCRN_NO_CHANGE, SCRN_BOLD);
+ cli_printf("%s:", (cont) ? " >> " : "DPDK-cli");
+ vt100_color(SCRN_BLUE, SCRN_NO_CHANGE, SCRN_BOLD);
+ cli_printf("%s", str);
+ vt100_color(SCRN_DEFAULT_FG, SCRN_DEFAULT_BG, SCRN_OFF);
+ cli_printf("> ");
+}
+
+/* Main entry point to create a CLI system */
+int
+cli_create(cli_prompt_t prompt, cli_tree_t default_func,
+ int nb_entries, uint32_t nb_hist)
+{
+ int i;
+ size_t size;
+ struct cli *cli;
+ struct cli_node *node;
+ struct cli_node *root;
+
+ cli = malloc(sizeof(struct cli));
+ if (cli == NULL) {
+ RTE_LOG(ERR, EAL, "Unable to allocate CLI structure\n");
+ return -1;
+ }
+ memset(cli, '\0', sizeof(struct cli));
+
+ this_cli = cli;
+
+ if (nb_entries == 0)
+ nb_entries = CLI_DEFAULT_NB_NODES;
+ else if (nb_entries == -1) {
+ cli->flags |= CLI_NODES_UNLIMITED;
+ nb_entries = CLI_DEFAULT_NB_NODES;
+ }
+ cli->nb_nodes = nb_entries;
+
+ if (nb_hist == (uint32_t)CLI_DEFAULT_HISTORY)
+ nb_hist = CLI_DEFAULT_HIST_LINES;
+
+ size = (nb_entries * sizeof(struct cli_node));
+ cli->node_mem = malloc(size);
+ if (cli->node_mem == NULL) {
+ RTE_LOG(ERR, EAL, "Unable to allocate CLI node structures\n");
+ goto error_exit;
+ }
+ memset(cli->node_mem, '\0', size);
+
+ cli->nb_hist = nb_hist;
+
+ TAILQ_INIT(&cli->root); /* Init the directory list */
+ TAILQ_INIT(&cli->free_nodes); /* List of free nodes */
+ TAILQ_INIT(&cli->help_nodes); /* List of help nodes */
+
+ CIRCLEQ_INIT(&cli->free_hist); /* List of free hist nodes */
+ CIRCLEQ_INIT(&cli->hd_hist); /* Init the history for list head */
+
+ if (scrn_create_with_defaults(SCRN_THEME_ON))
+ goto error_exit;
+
+ cli->vt = vt100_create();
+ if (!cli->vt)
+ goto error_exit;
+
+ cli->scratch = calloc(CLI_MAX_SCRATCH_LENGTH + 1, 1);
+ if (!cli->scratch)
+ goto error_exit;
+
+ cli->argv = calloc(CLI_MAX_ARGVS, sizeof(void *));
+ if (!cli->argv)
+ goto error_exit;
+
+ /* Set the user or default prompt routine */
+ cli->prompt = (prompt == NULL) ? __default_prompt : prompt;
+
+ /* Create the pool for the number of nodes */
+ node = cli->node_mem;
+ for (i = 0; i < nb_entries; i++, node++)
+ TAILQ_INSERT_TAIL(&cli->free_nodes, node, next);
+
+ root = cli_create_root(CLI_ROOT_NAME);
+ if (!root) {
+ RTE_LOG(ERR, EAL, "Unable to create root directory\n");
+ goto error_exit;
+ }
+
+ /* Set current working directory to root*/
+ set_cwd(root);
+
+ if (cli_set_history(nb_hist)) {
+ RTE_LOG(ERR, EAL, "Unable to create history\n");
+ goto error_exit;
+ }
+
+ /* create and initialize the gap buffer structures */
+ cli->gb = gb_create();
+ if (!cli->gb) {
+ RTE_LOG(ERR, EAL, "Unable to create Gap Buffer\n");
+ goto error_exit;
+ }
+
+ /* Startup the environment system */
+ cli->env = cli_env_create();
+ if (!cli->env)
+ goto error_exit;
+
+ /* when null call our default tree setup routine */
+ if (default_func == NULL)
+ default_func = cli_default_tree_init;
+
+ /* now call the user supplied func or ours if default_func was NULL */
+ if (default_func())
+ goto error_exit;
+
+ return 0;
+
+error_exit:
+ cli_destroy();
+ return -1;
+}
+
+/* Cleanup the CLI allocation of memory */
+void
+cli_destroy(void)
+{
+ struct cli *cli = this_cli;
+
+ if (!cli)
+ return;
+
+ gb_destroy(cli->gb);
+ vt100_destroy(cli->vt);
+ scrn_destroy();
+ cli_history_delete();
+
+ free(cli->scratch);
+ free(cli->kill);
+ free(cli->argv);
+ free(cli->hist_mem);
+ free(cli->node_mem);
+ free(cli);
+
+ this_cli = NULL;
+}
+
+/* Helper routine around the cli_create() routine */
+int
+cli_create_with_defaults(void)
+{
+ return cli_create(NULL, NULL, CLI_DEFAULT_NODES,
+ CLI_DEFAULT_HIST_LINES);
+}
+
+/* Helper routine around the cli_create() routine */
+int
+cli_create_with_tree(cli_tree_t tree)
+{
+ return cli_create(NULL, tree, CLI_DEFAULT_NODES,
+ CLI_DEFAULT_HIST_LINES);
+}
+
+/* Add a new prompt routine to the CLI system */
+cli_prompt_t
+cli_set_prompt(cli_prompt_t prompt)
+{
+ struct cli *cli = this_cli;
+ cli_prompt_t old;
+
+ old = cli->prompt; /* Save old prompt function */
+ cli->prompt = prompt; /* Install new prompt function */
+
+ if (cli->prompt == NULL)/* Set to default function if NULL */
+ cli->prompt = __default_prompt;
+
+ return old;
+}
+
+/**
+ * Load and execute a command file or Lua script file.
+ *
+ */
+int
+cli_execute_cmdfile(const char *filename)
+{
+ if (filename == NULL)
+ return 0;
+
+ gb_reset_buf(this_cli->gb);
+
+ if (strstr(filename, ".lua") || strstr(filename, ".LUA") ) {
+#ifdef RTE_LIBRTE_LUA
+ if (!this_cli->user_state) {
+ cli_printf(">>> User State for CLI not set for Lua\n");
+ return -1;
+ }
+ /* Execute the Lua script file. */
+ if (luaL_dofile(this_cli->user_state, filename) != 0) {
+ cli_printf("%s", lua_tostring(this_cli->user_state,-1));
+ return -1;
+ }
+#else
+ cli_printf(">>> Lua is not enabled in configuration!\n");
+#endif
+ } else {
+ FILE *fd;
+ char buff[256];
+
+ fd = fopen(filename, "r");
+ if (fd == NULL)
+ return -1;
+
+ /* Read and feed the lines to the cmdline parser. */
+ while (fgets(buff, sizeof(buff), fd))
+ cli_input(buff, strlen(buff));
+
+ fclose(fd);
+ }
+ return 0;
+}
+
+int
+cli_execute_cmdfiles(void)
+{
+ int i, cnt;
+ const char *path;
+
+ cnt = this_cli->cmd_files.idx;
+
+ for (i = 0; i < cnt; i++) {
+ if ((path = this_cli->cmd_files.filename[i]) == NULL)
+ continue;
+
+ if (cli_execute_cmdfile(path))
+ return -1;
+
+ free((char *)(uintptr_t)path);
+ this_cli->cmd_files.filename[i] = NULL;
+ }
+ this_cli->cmd_files.idx = 0;
+ return 0;
+}
diff --git a/lib/librte_cli/cli.h b/lib/librte_cli/cli.h
new file mode 100644
index 0000000..0e9cefb
--- /dev/null
+++ b/lib/librte_cli/cli.h
@@ -0,0 +1,1167 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_H_
+#define _CLI_H_
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+#include <libgen.h>
+#include <sys/queue.h>
+
+#include <rte_common.h>
+#include <rte_debug.h>
+#include <rte_memory.h>
+#include <rte_per_lcore.h>
+
+#include <cli_common.h>
+#include <cli_env.h>
+#include <cli_search.h>
+
+#include <cli_file.h>
+#include <cli_gapbuf.h>
+#include <cli_help.h>
+#include <cli_history.h>
+#include <cli_map.h>
+#include <cli_vt100.h>
+#include <cli_vt100_keys.h>
+
+#include <rte_string_fns.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CLI_USE_TIMERS 0x0001 /**< call rte_timer_manager() on input */
+#define CLI_NODES_UNLIMITED 0x0002 /**< Allocate nodes with no limit */
+#define CLI_YIELD_IO 0x0004
+
+#define CLI_ROOT_NAME "/"
+#define CLI_BIN_NAME "bin"
+
+enum {
+ CLI_MAX_ARGVS = 64, /**< Max number of args to support */
+ CLI_DEFAULT_NB_NODES = 256, /**< Default number of nodes */
+ CLI_DEFAULT_HIST_LINES = 128, /**< Default number of history lines */
+ CLI_MAX_PATH_LENGTH = 2048, /**< Max path string length */
+ CLI_MAX_SCRATCH_LENGTH = 4096, /**< Max scratch space length */
+ CLI_NAME_LEN = 64, /**< Max node name dir/cmd/file/.. */
+ CLI_MAX_LIST_NODES = 128, /**< Max number of nodes to list */
+ CLI_MAX_BINS = 32, /**< Max number of bin directories */
+ CLI_DEFAULT_NODES = 0, /**< Use default node count */
+ CLI_SCREEN_WIDTH = 80 /**< Size of screen width */
+};
+
+#define CLI_RECURSE_FLAG (1 << 0)
+#define CLI_LONG_LIST_FLAG (1 << 1)
+
+/* bitmap for the node type */
+typedef enum {
+ CLI_UNK_NODE = 0x0000, /**< Unknown node type */
+ CLI_DIR_NODE = 0x0001, /**< Directory node type */
+ CLI_CMD_NODE = 0x0002, /**< Command node type */
+ CLI_FILE_NODE = 0x0004, /**< File node type */
+ CLI_ALIAS_NODE = 0x0008, /**< Alias node type */
+ CLI_STR_NODE = 0x0010, /**< String node type */
+} node_type_t;
+
+/* Keep this list in sync with the node_type_t enum above */
+#define CLI_NODE_TYPES \
+ { "Unknown", "Directory", "Command", "File", "Alias", "String", NULL }
+
+enum {
+ CLI_EXE_TYPE = (CLI_CMD_NODE | CLI_ALIAS_NODE),
+ CLI_ALL_TYPE = (CLI_EXE_TYPE | CLI_FILE_NODE | CLI_DIR_NODE),
+ CLI_OTHER_TYPE = (CLI_DIR_NODE | CLI_FILE_NODE)
+};
+
+struct cli;
+struct cli_node;
+
+typedef int (*cli_cfunc_t)(int argc, char **argv);
+/**< CLI function pointer type for a command/alias node */
+typedef int (*cli_ffunc_t)(struct cli_node *node, char *buff, int len,
+ uint32_t opt);
+/**< CLI function pointer type for a file type node */
+
+typedef void (*cli_prompt_t)(int continuation);
+/**< CLI prompt routine */
+typedef int (*cli_tree_t)(void);
+/**< CLI function pointer type for user initialization */
+
+/* Generic node structure for all node types in the directory tree */
+struct cli_node {
+ TAILQ_ENTRY(cli_node) next; /**< link list of commands */
+ struct cli_node *parent; /**< Parent directory (NULL == ROOT) */
+ char name[CLI_NAME_LEN]; /**< Name of Node */
+ uint16_t name_sz; /**< Number of bytes in name w/o null */
+ uint16_t fstate; /**< File State */
+ uint16_t fflags; /**< File flags */
+ uint16_t pad0;
+ node_type_t type; /**< Node Type Root, Dir or cmd */
+ union {
+ cli_cfunc_t cfunc; /**< Function pointer for commands */
+ cli_ffunc_t ffunc; /**< Function pointer for files */
+ cli_sfunc_t sfunc; /**< Function pointer for Strings */
+ };
+ const char *short_desc; /**< Short description */
+ const char *alias_str; /**< Alias string */
+ size_t foffset; /**< Current offset in file */
+ size_t file_size; /**< Size of file */
+ char *file_data; /**< Pointer to file data */
+ TAILQ_HEAD(, cli_node) items; /**< List of nodes for directory */
+} __rte_cache_aligned; /**< Structure for each node type */
+
+#define MAX_CMD_FILES 16
+
+typedef struct {
+ char *filename[MAX_CMD_FILES];
+ uint32_t idx;
+} cli_files_t;
+
+struct cli {
+ TAILQ_HEAD(, cli_node) root; /**< head of node entries or root */
+ CIRCLEQ_HEAD(, cli_hist) hd_hist; /**< History circular queue */
+
+ uint16_t flags; /**< Flags about CLI */
+ volatile uint16_t quit_flag; /**< When set to non-zero quit */
+ uint32_t nb_nodes; /**< total number of nodes */
+
+ uint32_t nb_hist; /**< total number of history lines */
+ cli_files_t cmd_files; /**< array of command filename pointers */
+ struct cli_hist *curr_hist; /**< Current history */
+ struct cli_node *bins[CLI_MAX_BINS]; /**< Arrays of bin directories,
+ first is the current working directory */
+ struct cli_node *exe_node; /**< Node currently being executed */
+
+ struct cli_env *env; /**< Environment variables */
+ struct gapbuf *gb; /**< Gap buffer information */
+ struct cli_vt100 *vt; /**< vt100 information */
+ char **argv; /**< array of argument string pointers */
+
+ cli_prompt_t prompt; /**< Prompt function pointer */
+ char *scratch; /**< Place to build the path string */
+ char *kill; /**< strdup() string of last kill data */
+
+ struct cli_node *node_mem; /**< Base address of node memory */
+ struct cli_hist *hist_mem; /**< Base address of history memory */
+
+ TAILQ_HEAD(, help_node) help_nodes; /**< head of help */
+ TAILQ_HEAD(, cli_node) free_nodes; /**< Free list of nodes */
+ CIRCLEQ_HEAD(, cli_hist) free_hist; /**< free list of history nodes */
+ void *user_state; /**< Pointer to some state variable */
+} __rte_cache_aligned;
+
+RTE_DECLARE_PER_LCORE(struct cli *, cli);
+#define this_cli RTE_PER_LCORE(cli)
+
+typedef union {
+ cli_cfunc_t cfunc; /**< Function pointer for commands */
+ cli_ffunc_t ffunc; /**< Function pointer for files */
+ cli_sfunc_t sfunc; /**< Function pointer for strings */
+} cli_funcs_t; /* Internal: Used in argument list for adding nodes */
+
+struct cli_dir {
+ const char *name; /**< directory name */
+};
+
+struct cli_cmd {
+ const char *name; /**< Name of command */
+ cli_cfunc_t cfunc; /**< Function pointer */
+ const char *short_desc; /**< Short description */
+}; /**< List of commands for cli_add_cmds() */
+
+struct cli_alias {
+ const char *name; /**< Name of command */
+ const char *alias_atr; /**< Alias string */
+ const char *short_desc; /**< Short description */
+}; /**< List of alias for cli_add_aliases() */
+
+struct cli_file {
+ const char *name; /**< Name of command */
+ cli_ffunc_t ffunc; /**< Read/Write function pointer */
+ const char *short_desc; /**< Short description */
+}; /**< List of alias for cli_add_aliases() */
+
+struct cli_str {
+ const char *name; /**< Name of command */
+ cli_sfunc_t sfunc; /**< Function pointer */
+ const char *string; /**< Default string */
+}; /**< List of commands for cli_add_str() */
+
+struct cli_tree {
+ node_type_t type; /**< type of node to create */
+ union {
+ struct cli_dir dir; /**< directory and bin directory */
+ struct cli_cmd cmd; /**< command nodes */
+ struct cli_file file; /**< file nodes */
+ struct cli_alias alias; /**< alias nodes */
+ struct cli_str str; /**< string node */
+ };
+}; /**< Used to help create a directory tree */
+
+#define c_dir(n) \
+ { \
+ CLI_DIR_NODE, .dir = {(n) } \
+ }
+#define c_cmd(n, f, h) \
+ { \
+ CLI_CMD_NODE, .cmd = {(n), (f), (h) } \
+ }
+#define c_file(n, rw, h) \
+ { \
+ CLI_FILE_NODE, .file = {(n), (rw), (h) } \
+ }
+#define c_alias(n, l, h) \
+ { \
+ CLI_ALIAS_NODE, .alias = {(n), (l), (h) } \
+ }
+#define c_str(n, f, s) \
+ { \
+ CLI_STR_NODE, .str = {(n), (f), (s) } \
+ }
+#define c_end() \
+ { \
+ CLI_UNK_NODE, .dir = { NULL } \
+ }
+
+/**
+ * The CLI write routine, using write() call
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param msg
+ * The string to be written
+ * @param len
+ * Number of bytes to write or if -1 then strlen(msg) is used.
+ * @return
+ * N/A
+ */
+void cli_write(const void *msg, int len);
+
+static inline void cli_set_user_state(void *val)
+{
+ this_cli->user_state = val;
+}
+
+/**
+ * CLI root directory node.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * Pointer to current working directory.
+ */
+static inline struct cli_node *get_root(void)
+{
+ RTE_ASSERT(this_cli != NULL);
+ return this_cli->root.tqh_first;
+}
+
+/**
+ * CLI current working directory.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * Pointer to current working directory.
+ */
+static inline struct cli_node *get_cwd(void)
+{
+ RTE_ASSERT(this_cli != NULL);
+ return this_cli->bins[0];
+}
+
+/**
+ * set CLI current working directory.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param node
+ * Pointer to set as the current working directory
+ * @return
+ * None
+ */
+static inline void set_cwd(struct cli_node *node)
+{
+ RTE_ASSERT(this_cli != NULL);
+ this_cli->bins[0] = node;
+}
+
+/**
+ * Check if this_cli pointer is valid
+ *
+ * @return
+ * 1 if true else 0
+ */
+static inline int is_cli_valid(void)
+{
+ return (this_cli) ? 1 : 0;
+}
+
+/**
+ * Helper routine to compare two strings exactly
+ *
+ * @param s1
+ * Pointer to first string.
+ * @param s2
+ * Pointer to second string.
+ * @return
+ * 0 failed to compare and 1 is equal.
+ */
+static inline int is_match(const char *s1, const char *s2)
+{
+ if (!s1 || !s2)
+ return 0;
+
+ while ((*s1 != '\0') && (*s2 != '\0')) {
+ if (*s1++ != *s2++)
+ return 0;
+ }
+ if (*s1 != *s2)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Test if the node is of a given type(s)
+ *
+ * @param node
+ * Pointer the cli_node structure
+ * @return
+ * True if node is one of the types given
+ */
+static inline int is_node(struct cli_node *node, uint32_t types)
+{
+ return node->type & types;
+}
+
+/**
+ * Test if the node is a command
+ *
+ * @param node
+ * Pointer the cli_node structure
+ * @return
+ * True if command else false if not
+ */
+static inline int is_command(struct cli_node *node)
+{
+ return is_node(node, CLI_CMD_NODE);
+}
+
+/**
+ * Test if the node is alias
+ *
+ * @param node
+ * Pointer the cli_node structure
+ * @return
+ * True if alias else false if not
+ */
+static inline int is_alias(struct cli_node *node)
+{
+ return is_node(node, CLI_ALIAS_NODE);
+}
+
+/**
+ * Test if the node is a file
+ *
+ * @param node
+ * Pointer the cli_node structure
+ * @return
+ * True if a file else false if not
+ */
+static inline int is_file(struct cli_node *node)
+{
+ return is_node(node, CLI_FILE_NODE);
+}
+
+/**
+ * Test if the node is directory
+ *
+ * @param node
+ * Pointer the cli_node structure
+ * @return
+ * True if directory else false if not
+ */
+static inline int is_directory(struct cli_node *node)
+{
+ return is_node(node, CLI_DIR_NODE);
+}
+
+/**
+ * Test if the node is executable
+ *
+ * @param node
+ * Pointer the cli_node structure
+ * @return
+ * True if executable else false if not
+ */
+static inline int is_executable(struct cli_node *node)
+{
+ return is_command(node) || is_alias(node);
+}
+
+/**
+ * Print out the short description for commands.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * -1 just to remove code having to return error anyway.
+ */
+static inline int cli_usage(void)
+{
+ if (this_cli && this_cli->exe_node) {
+ const char *p = this_cli->exe_node->short_desc;
+
+ cli_printf(" Usage: %s\n", (p) ? p : "No description found");
+ }
+ return -1;
+}
+
+/**
+ * Return the string for the given node type
+ *
+ * @param node
+ * struct cli_node pointer
+ * @return
+ * String for the node type.
+ */
+static inline const char *cli_node_type(struct cli_node *node)
+{
+ const char *node_str[] = CLI_NODE_TYPES;
+ switch (node->type) {
+ case CLI_UNK_NODE:
+ default:
+ break;
+ case CLI_DIR_NODE:
+ return node_str[1];
+ case CLI_CMD_NODE:
+ return node_str[2];
+ case CLI_FILE_NODE:
+ return node_str[3];
+ case CLI_ALIAS_NODE:
+ return node_str[4];
+ }
+ return node_str[0];
+}
+
+/**
+ * Create the current working directory string, which is the complete
+ * path to node. Uses CLI routines to output the string to the console.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param node
+ * Starting node or last file/dir to be printed
+ * @param path
+ * Pointer to a path buffer string.
+ * @return
+ * Return the pointer to the cli->scratch buffer or buf with path string.
+ */
+static inline char *cli_path_string(struct cli_node *node, char *path)
+{
+ if (!path)
+ path = this_cli->scratch;
+
+ if (!node)
+ node = get_cwd();
+
+ if (node->parent) {
+ cli_path_string(node->parent, path);
+ strcat(path, node->name);
+ strcat(path, "/");
+ } else
+ strcpy(path, "/");
+
+ return path;
+}
+
+/**
+ * path string of current working directory
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param entry
+ * The node to free.
+ * @return
+ * N/A
+ */
+static inline char *cli_cwd_path(void)
+{
+ return cli_path_string(get_cwd(), NULL);
+}
+
+/**
+ * Print the current working directory string, which is the complete
+ * path to node. Uses CLI routines to output the string to the console.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param node
+ * Starting node or last file/dir to be printed
+ * @return
+ * N/A.
+ */
+static inline void cli_pwd(struct cli_node *node)
+{
+ cli_printf("%s", cli_path_string(node, NULL));
+}
+
+/**
+ * Move the vt100 cursor to the left one character
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_cursor_left(void)
+{
+ cli_write(vt100_left_arr, -1);
+}
+
+/**
+ * Move the vt100 cursor to the right one character
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_cursor_right(void)
+{
+ cli_write(vt100_right_arr, -1);
+}
+
+/**
+ * Save the vt100 cursor location
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_save_cursor(void)
+{
+ cli_write(vt100_save_cursor, -1);
+}
+
+/**
+ * Restore the cursor to the saved location on the console
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_restore_cursor(void)
+{
+ cli_write(vt100_restore_cursor, -1);
+}
+
+/**
+ * Print out the left side of the input in the Gap Buffer.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_display_left(void)
+{
+ if (gb_left_data_size(this_cli->gb))
+ cli_write(gb_start_of_buf(this_cli->gb),
+ gb_left_data_size(this_cli->gb));
+}
+
+/**
+ * Print out the right side of the input in the Gap Buffer.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_display_right(void)
+{
+ if (gb_right_data_size(this_cli->gb))
+ cli_write(gb_end_of_gap(this_cli->gb),
+ gb_right_data_size(this_cli->gb));
+}
+
+/**
+ * Print out the complete line in the Gap Buffer.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_display_line(void)
+{
+ uint32_t i;
+
+ this_cli->prompt(0);
+
+ cli_display_left();
+ cli_display_right();
+
+ gb_move_gap_to_point(this_cli->gb);
+
+ for (i = 0;
+ i < (gb_data_size(this_cli->gb) - gb_point_offset(this_cli->gb));
+ i++)
+ cli_cursor_left();
+}
+
+/**
+ * Clear the console screen
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_clear_screen(void)
+{
+ cli_write(vt100_clear_screen, -1);
+}
+
+/**
+ * clear from cursor to end of line
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_clear_to_eol(void)
+{
+ cli_write(vt100_clear_right, -1);
+}
+
+/**
+ * Clear the current line or the line given
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param lineno
+ * if lineno is -1 then clear the current line else the lineno given.
+ * @return
+ * N/A
+ */
+static inline void cli_clear_line(int lineno)
+{
+ if (lineno > 0)
+ cli_printf(vt100_pos_cursor, lineno, 0);
+ else
+ cli_write("\r", 1);
+
+ cli_write(vt100_clear_line, -1);
+}
+
+/**
+ * Move the cursor up by the number of lines given
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param lineno
+ * Number of lines to move the cursor
+ * @return
+ * N/A
+ */
+static inline void cli_move_cursor_up(int lineno)
+{
+ while (lineno--)
+ cli_printf(vt100_up_arr);
+}
+
+/**
+ * Set the number of lines in history
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param nb_hist
+ * Number of lines in history if zero disable history.
+ * @return
+ * zero on success or -1 on error
+ */
+static inline int cli_set_history_size(uint32_t nb_hist)
+{
+ return cli_set_history(nb_hist);
+}
+
+/**
+ * Get the total number of lines in history
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * total number of line for history
+ */
+static inline uint32_t cli_get_history_size(void)
+{
+ return this_cli->nb_hist;
+}
+
+/**
+ * List the history lines
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+static inline void cli_history_list(void)
+{
+ cli_history_dump();
+}
+
+/**
+ * Return the CLI root node.
+ *
+ * @return
+ * Pointer to root node.
+ */
+static inline struct cli_node *cli_root_node(void)
+{
+ return this_cli->root.tqh_first;
+}
+
+/**
+ * Add a input text string the cli input parser
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param str
+ * Pointer to string to insert
+ * @param n
+ * Number of bytes in string
+ * @return
+ * N/A
+ */
+void cli_input(char *str, int n);
+
+/**
+ * Create the CLI engine
+ *
+ * @param prompt_func
+ * Function pointer to call for displaying the prompt.
+ * @param tree_func
+ * The user supplied function to init the tree or can be NULL. If NULL then
+ * a default tree is initialized with basic commands.
+ * @param nb_entries
+ * Total number of commands, files, aliases and directories. If 0 then use
+ * the default number of nodes. If -1 then unlimited number of nodes.
+ * @param nb_hist
+ * The number of lines to keep in history. If zero then turn off history.
+ * If the value is CLI_DEFAULT_HISTORY use CLI_DEFAULT_HIST_LINES
+ * @return
+ * 0 on success or -1
+ */
+int cli_create(cli_prompt_t prompt_func, cli_tree_t tree_func, int nb_entries,
+ uint32_t nb_hist);
+
+/**
+ * Create the CLI engine using system defaults.
+ *
+ * @return
+ * 0 on success or -1
+ */
+int cli_create_with_defaults(void);
+
+/**
+ * Create the CLI engine using system defaults and supplied tree init function.
+ *
+ * @param tree
+ * The user supplied function to init the tree or can be NULL. If NULL then
+ * a default tree is initialized with default commands.
+ * @return
+ * 0 on success or -1
+ */
+int cli_create_with_tree(cli_tree_t tree);
+
+/**
+ * Set the CLI prompt function pointer
+ *
+ * @param prompt
+ * Function pointer to display the prompt
+ * @return
+ * Return the old prompt function pointer or NULL if one does not exist
+ */
+cli_prompt_t cli_set_prompt(cli_prompt_t prompt);
+
+/**
+ * Create the root directory
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param dirname
+ * Name of root directory, if null uses '/'
+ * @return
+ * NULL on error or the root node on success.
+ */
+struct cli_node *cli_create_root(const char *dirname);
+
+/**
+ * Create the default directory tree
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * 0 on success or non-zero on error
+ */
+int cli_default_tree_init(void);
+
+/**
+ * Destroy the CLI engine
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+void cli_destroy(void);
+
+/**
+ * Start the CLI running
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param msg
+ * User message to be displayed on startup
+ * @return
+ * N/A
+ */
+void cli_start(const char *msg);
+
+/**
+ * Start the CLI running and use timerss
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param msg
+ * User message to be displayed on startup
+ * @return
+ * N/A
+ */
+void cli_start_with_timers(const char *msg);
+
+/**
+ * Execute command line string in cli->input
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * zero on success or -1 on error
+ */
+int cli_execute(void);
+
+/**
+ * Add a bin directory to the bin list
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param node
+ * Directory to add to bin list
+ * @return
+ * 0 is ok, -1 is full
+ */
+int cli_add_bin(struct cli_node *node);
+
+/**
+ * Remove a bin directory from the bin list
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param node
+ * Directory to add to bin list
+ * @return
+ * 0 is ok, -1 is not found
+ */
+int cli_del_bin(struct cli_node *node);
+
+/**
+ * Add a bin directory to the bin list using path
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param path
+ * path to bin directory to add, must exist first.
+ * @return
+ * 0 is ok, -1 is full
+ */
+int cli_add_bin_path(const char *path);
+
+/**
+ * Add a cli directory
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param dirname
+ * String pointing to the directory name
+ * @param parent
+ * Parent node of the new directory
+ * @return
+ * pointer to directory entry or NULL on error
+ */
+struct cli_node *cli_add_dir(const char *dirname, struct cli_node *parent);
+
+/**
+ * Add a command to a directory
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param name
+ * Pointer to command name string
+ * @param dir
+ * Directory node pointer
+ * @param func
+ * Pointer to function to execute
+ * @param short_desc
+ * Short string for help to display
+ * @return
+ * NULL on error or the node address for the command
+ */
+struct cli_node *cli_add_cmd(const char *name, struct cli_node *dir,
+ cli_cfunc_t func, const char *short_desc);
+
+/**
+ * Add an alias string or special command type
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param name
+ * Pointer to command name string
+ * @param dir
+ * Directory node pointer
+ * @param line
+ * Pointer to alias string
+ * @param short_desc
+ * Short string for help to display
+ * @return
+ * NULL on error or the node address for the command
+ */
+struct cli_node *cli_add_alias(const char *name, struct cli_node *dir,
+ const char *line, const char *short_desc);
+
+/**
+ * Add an file to a directory
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param name
+ * Pointer to command name string
+ * @param dir
+ * Directory node pointer
+ * @param func
+ * Pointer to a function attached to the file.
+ * @param short_desc
+ * Short string for help to display
+ * @return
+ * NULL on error or the node pointer.
+ */
+struct cli_node *cli_add_file(const char *name, struct cli_node *dir,
+ cli_ffunc_t func, const char *short_desc);
+
+/**
+ * Add a string to the system.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param name
+ * Pointer to command name string
+ * @param dir
+ * Directory node pointer
+ * @param func
+ * Pointer to a function attached to the string.
+ * @param str
+ * Value of string if no function defined.
+ * @return
+ * NULL on error or the node pointer.
+ */
+int cli_add_str(const char *name, cli_sfunc_t func, const char *str);
+
+/**
+ * Add a list of nodes to a directory
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param dir
+ * Node pointer to directory for add commands
+ * @param treee
+ * Pointer to list of nodes to add to the tree
+ * @return
+ * -1 on error or 0 for OK
+ */
+int cli_add_tree(struct cli_node *dir, struct cli_tree *tree);
+
+/**
+ * Set the I/O file descriptors
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param in
+ * File descriptor for input
+ * @param out
+ * File descriptor for output
+ * @return
+ * N/A
+ */
+void cli_set_io(FILE *in, FILE *out);
+
+/**
+ * Set the I/O to use stdin/stdout
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * 0 on success or non-0 on error
+ */
+int cli_stdin_setup(void);
+
+/**
+ * Restore the stdin/stdout tty params from setup
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * N/A
+ */
+void cli_stdin_restore(void);
+
+/**
+ * Pause and wait for input character
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param keys
+ * List of keys to force return, if NULL defaults to ESC and q/Q
+ * @return
+ * character that terminated the pause or zero.
+ */
+char cli_pause(const char *msg, const char *keys);
+
+/**
+ * Add filenames to the CLI command list.
+ *
+ * @param filename
+ * Path of command file.
+ * @return
+ * 0 is OK and -1 if error
+ */
+static inline int cli_add_cmdfile(const char *filename)
+{
+ if (this_cli->cmd_files.idx >= MAX_CMD_FILES)
+ return -1;
+
+ this_cli->cmd_files.filename[this_cli->cmd_files.idx++] =
+ strdup(filename);
+
+ return 0;
+}
+
+/**
+ * execute a command file
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param path
+ * Pointer to path to file
+ * @return
+ * 0 on OK or -1 on error
+ */
+int cli_execute_cmdfile(const char *path);
+
+/**
+ * execute a list for command files
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * 0 on OK or -1 on error
+ */
+int cli_execute_cmdfiles(void);
+
+/**
+ * Remove a node from the directory tree
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param node
+ * The pointer to the node to remove
+ * @return
+ * 0 on OK and -1 on error
+ */
+int cli_remove_node(struct cli_node *node);
+
+/**
+ * return true if timers are enabled.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * non-zero if true else 0
+ */
+int cli_use_timers(void);
+
+/**
+ * return true if allocating unlimited nodes are enabled.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * non-zero if true else 0
+ */
+int cli_nodes_unlimited(void);
+
+/**
+ * return true if calling yield should are enabled.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @return
+ * non-zero if true else 0
+ */
+int cli_yield_io(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_H_ */
diff --git a/lib/librte_cli/cli.rst b/lib/librte_cli/cli.rst
new file mode 100644
index 0000000..bf515de
--- /dev/null
+++ b/lib/librte_cli/cli.rst
@@ -0,0 +1,597 @@
+.. BSD LICENSE
+ Copyright(c) 2017 Intel Corporation. All rights reserved.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CLI Sample Application
+===============================
+
+CLI stands for "Command Line Interface".
+
+This chapter describes the CLI sample application that is part of the
+Data Plane Development Kit (DPDK). The CLI is a workalike replacement for
+cmdline library in DPDK and has a simpler programming interface and programming
+model.
+
+The primary goal of CLI is to allow the developer to create commands quickly
+and with very little compile or runtime configuration. Using standard Unix*
+like constructs which are very familar to the developer. Allowing the developer
+to construct a set of commands for development or deployment of the application.
+
+The CLI design uses a directory like design instead of a single level command
+line interface. Allowing the developer to use a directory style solution to
+controlling a DPDK application. The directory style design is nothing new, but
+it does have some advantages.
+
+One advantage allows the directory path for the command to be part of the
+information used in executing the command. The next advantage is creating
+directories to make a hierarchy of commands, plus allowing whole directroy
+trees to dynamically come and go as required by the developer.
+
+Some of the advantages are:
+
+ * CLI has no global variable other then the single thread variable called *this_cli* which can only be accessed from the thread which created the CLI instance.
+ * CLI supports commands, files, aliases, directories.
+ - The alias command is just a string using a simple substitution support for other commands similar to the bash shell like alias commands.
+ - Files can be static or dynamic information, can be changed on the fly and saved for later. The file is backed with a simple function callback to allow the developer to update the content or not.
+ * Added support for color and cursor movement APIs similar to Pktgen if needed by the developer.
+ * It is a work alike replacement for cmdline library. Both cmdline and CLI can be used in the same application if care is taken.
+ * Uses a simple fake like directory layout for command and files. Allowing for command hierarchy as path to the command can allow for specific targets to be identified without having to state it on the command line.
+ * Has auto-complete for commands, similar to Unix/Linux autocomplete and provides support for command option help as well.
+ * Callback functions for commands are simply just argc/argv like functions.
+ - The CLI does not convert arguments for the user, it is up to the developer to decode the argv[] values.
+ - Most of the arguments converted in the current cmdline are difficult to use or not required as the developer just picks string type and does the conversion himself.
+ * Dynamically be able to add and remove commands, directories, files and aliases, does not need to be statically compiled into the application.
+ * No weird structures in the code and reduces the line count for testpmd from 12K to 4.5K lines. I convert testpmd to have both CMDLINE and CLI with a command line option.
+ * Two methods to parse command lines, first is the standard argc/argv method in the function.
+ - The second method is to use a map of strings with simple printf like formatting to detect which command line the user typed.
+ - An ID value it returned to the used to indicate which mapping string was found to make the command line to be used in a switch statement.
+ * Environment variable support using the **env** command or using an API.
+ * Central help support if needed (optional).
+
+Overview
+--------
+
+The CLI sample application is a simple application that demonstrates the
+use of the command line interface in the DPDK. This application is a
+readline-like interface that can be used to control a DPDK application.
+
+One of the advantages of CLI over Cmdline is it is dynamic, which means
+nodes or items can be added and removed on the fly. Which allows adding
+new directories, file or commands as needed or removing these items at runtime.
+The CLI has no global modifiable variable as the one global pointer is a
+thread based variable. Which allows the developer to have multiple CLI
+commands per thread if needed.
+
+Another advantage is the calling of the backend function to support a
+command is very familar to developers as it is basically just a argc/argv
+style command and the developer gets the complete command line.
+
+One other advantage is the use of MAP structures, to help identify commands
+quickly plus allowing the developer to define new versions of commands and
+be able to identify these new versions using a simple identifier value. Look at
+the sample application to see a simple usage.
+
+Another advantage of CLI is how simple it is to add new directroies, files and
+commands for user development. The basic concept is for the developer to use
+standard Unix like designs. To add a command a developer needs to add an entry
+to the cli_tree_t structure and create a function using the following prototype:
+
+.. code-block:: c
+
+ int user_cmd(int argc, char **argv);
+
+The argc/argv is exactly like the standard usage in a Unix* system, which allows
+for using getopt() and other standard functions. The Cmdline structures and
+text conversions were defined at compile time in most cases. In CLI the routine
+is passed the argc/argv information to convert these options as needed. The cli
+variable being a thread Local Storage (TLS) all user routines a CLI routine only
+need to access the thread variable to eliminate needing a global variable to
+reference the specific CLI instance and passing the value in the API.
+
+The user can also set environment variables using the **env** command. These
+variables are also parsed in the command line a direct substitution is done.
+
+The CLI system also has support for simple files along with alias like commands.
+These alias commands are fixed strings which are executed instead of a function
+provided by the developer. If the user has more arguments these are appended
+to the alias string and processed as if typed on the command line.
+
+.. note::
+
+ The CLI library was designed to be used in production code and the Cmdline
+ was not validated to the same standard as other DPDK libraries. The goal
+ is to provide a production CLI design.
+
+The CLI sample application supports some of the features of the Cmdline
+library such as, completion, cut/paste and some other special bindings that
+make configuration and debug faster and easier.
+
+The CLI design uses some very simple VT100 control strings for displaying data
+and accepting input from the user. Some of the control strings are used to
+clear the screen or line and position the cursor on a VT100 compatible terminal.
+The CLI screen code also supports basic color and many other VT100 commands.
+
+The application also shows how the CLI application can be extended to handle
+a list of commands and user input.
+
+The example presents a simple command prompt **DPDK-cli:/>** similar to a Unix*
+shell command along with a directory like file system.
+
+Some of the **default** commands contained under /sbin directory are:
+
+ * **ls**: list the current or provided directory files/commands.
+ * **cd**: Change directory command.
+ * **pwd**: print out the current working directory.
+ * **history**: List the current command line history if enabled.
+ * **more**: A simple command to page contents of files.
+ * **help**: display a the help screen.
+ * **quit**: exit the CLI application, also **Ctrl-x** will exit as well.
+ * **mkdir**: add a directory to the current directory.
+ * **delay**: wait for a given number of microseconds.
+ * **sleep**: wait for a given number of seconds.
+ * **rm**: remove a directory, file or command. Removing a file will delete the data.
+ * **cls**: clear the screen and redisplay the prompt.
+ * **version**: Display the current DPDK version being used.
+ * **path**: display the current search path for executable commands.
+ * **cmap**: Display the current system core and socket information.
+ * **hugepages**: Display the current hugepage information.
+ * **sizes**: a collection system structure and buffer sizes for debugging.
+ * **copyright**: a file containing DPDK copyright information.
+ * **env**: a command show/set/modify the environment variables.
+
+Some example commands under /bin directory are:
+
+ * **ll**: an alias command to display long ls listing **ls -l**
+ * **h**: alias command for **history**
+ * **hello**: a simple Hello World! command.
+ * **show**: has a number of commands using the map feature.
+
+Under the /data directory is:
+
+ * **pci**: a simple example file for displaying the **lspci** command in CLI.
+
+.. note::
+
+ To terminate the application, use **Ctrl-x** or the command **quit**.
+
+Auto completion
+---------------
+
+CLI does support auto completion at the file or directory level, meaning the
+arguments to commands are not expanded as was done in Cmdline code. The CLI
+auto completion works similar to the standard Unix* system by expanding
+commands and directory paths. In normal Unix* like commands the user needs to
+execute the command asking for the help information and CLI uses this method.
+
+Special command features
+------------------------
+
+Using the '!' followed by a number from the history list of commands you can
+execute that command again. Using the UP/Down arrows the user can quickly find
+and execute or modify a previous command in history.
+
+The user can also execute host level commands if enabled using the '@' prefix
+to a command line e.g. @ls or @lspci or ... line is passed to popen or system
+function to be executed and the output displayed on the console if any output.
+To disable set CONFIG_RTE_CLI_HOST_COMMANDS=n in configuration file.
+
+Compiling the Application
+-------------------------
+
+#. Go to example directory:
+
+.. code-block:: console
+
+ export RTE_SDK=/path/to/rte_sdk
+ cd ${RTE_SDK}/examples/cli
+
+#. Set the target (a default target is used if not specified). For example:
+
+.. code-block:: console
+
+ export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+ Refer to the *DPDK Getting Started Guide* for possible RTE_TARGET values.
+
+#. Build the application:
+
+.. code-block:: console
+
+ make
+
+Running the Application
+-----------------------
+
+To run the application in linuxapp environment, issue the following command:
+
+.. code-block:: console
+
+ $ ./build/cli
+
+.. note::
+ The example cli application does not require to be run as superuser
+ as it does not startup DPDK by calling rte_eal_init() routine. Which means
+ it also does not use DPDK features except for a few routines not requiring
+ EAL initialization.
+
+Refer to the *DPDK Getting Started Guide* for general information on running applications
+and the Environment Abstraction Layer (EAL) options.
+
+Explanation
+-----------
+
+The following sections provide some explanation of the code.
+
+EAL Initialization and cmdline Start
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first task is the initialization of the Environment Abstraction Layer (EAL),
+if required for the application.
+
+.. code-block:: c
+
+ int
+ main(int argc, char **argv)
+ {
+ if (cli_create_with_tree(init_tree) ==0) {
+ cli_start(NULL, 0); /* NULL is some init message done only once */
+ /* 0 means do not use color themes */
+ cli_destroy();
+ }
+ ...
+
+The cli_start() function returns when the user types **Ctrl-x** or uses the
+quit command in this case, the application exits. The cli_create() call takes
+four arguments and each has a default value if not provided. The API used here
+is the cli_create_with_tree(), which uses defaults for three of the arguments.
+
+.. code-block:: c
+
+ /**
+ * Create the CLI engine
+ *
+ * @param prompt_func
+ * Function pointer to call for displaying the prompt.
+ * @param tree_func
+ * The user supplied function to init the tree or can be NULL. If NULL then
+ * a default tree is initialized with basic commands.
+ * @param nb_entries
+ * Total number of commands, files, aliases and directories. If 0 then use
+ * the default number of nodes. If -1 then unlimited number of nodes.
+ * @param nb_hist
+ * The number of lines to keep in history. If zero then turn off history.
+ * If the value is CLI_DEFAULT_HISTORY use CLI_DEFAULT_HIST_LINES
+ * @return
+ * 0 on success or -1
+ */
+ int cli_create(cli_prompt_t prompt_func, cli_tree_t tree_func,
+ int nb_entries, uint32_t nb_hist);
+
+The cli_create_with_tree() has only one argument which is the structure to use
+in order to setup the initial directory structure. Also the wrapper function
+int cli_create_with_defaults(void) can be used as well.
+
+Consult the cli.h header file for the default values. Also the alias node is a
+special alias file to allow for aliasing a command to another command.
+
+The tree init routine is defined like:
+
+.. code-block:: c
+
+ static struct cli_tree my_tree[] = {
+ c_dir("/data"),
+ c_file("pci", pci_file, "display lspci information"),
+ c_dir("/bin"),
+ c_cmd("hello", hello_cmd, "Hello-World!!"),
+ c_alias("h", "history", "display history commands"),
+ c_alias("ll", "ls -l", "long directory listing alias"),
+ c_end()
+ };
+
+ static int
+ init_tree(void)
+ {
+ /*
+ * Root is created already and using system default cmds and dirs, the
+ * developer is not required to use the system default cmds/dirs.
+ */
+ if (cli_default_tree_init())
+ return -1;
+
+ /* Using NULL here to start at root directory */
+ if (cli_add_tree(NULL, my_tree))
+ return -1;
+
+ cli_help_add("Show", show_map, show_help);
+
+ return cli_add_bin_path("/bin");
+ }
+
+
+The above structure is used to create the tree structure at initialization
+time. The struct cli_tree or cli_tree_t typedef can be used to setup a new
+directory tree or argument the default tree.
+
+The elements are using a set of macros c_dir, c_file, c_cmd, c_alias and c_end.
+These macros help fill out the cli_tree_t structure for the given type of item.
+
+The developer can create his own tree structure with any commands that are
+needed and/or call the cli_default_tree_init() routine to get the default
+structure of commands. If the developer does not wish to call the default
+CLI routine, then he must call the cli_create_root() function first before
+adding other nodes. Other nodes can be added and removed at anytime.
+
+CLI Map command support
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The CLI command has two types of support to handle arguments normal argc/argv
+and the map system. As shown above the developer creates a directory tree and
+attaches a function to a command. The function takes the CLI pointer plus the
+argc/argv arguments and the developer can just parse the arguments to decode
+the command arguments. Sometimes you have multiple commands or different versions
+of a command being handled by a single routine, this is were the map support
+comes into play.
+
+The map support defines a set of struct cli_map map[]; to help detect the
+correct command from the user. In the list of cli_map structures a single
+structure contains two items a developer defined index value and a command
+strings. The index value is used on the function to identify the specific type
+of command found in the list. The string is a special printf like string to
+help identify the command typed by the user. One of the first things todo in
+the command routine is to call the cli_mapping() function passing in the CLI
+pointer and the argc/argv values.The two method can be used at the same time.
+
+The cli_mapping() command matches up the special format string with the values
+in the argc/argv array and returns the developer supplied index value or really
+the pointer the struct cli_map instance.
+
+Now the developer can use the cli_map.index value in a switch() statement to
+locate the command the user typed or if not found a return of -1.
+
+Example:
+
+.. code-block:: c
+
+ static int
+ hello_cmd(int argc, char **argv)
+ {
+ int i, opt;
+
+ optind = 1;
+ while((opt = getopt(argc, argv, "?")) != -1) {
+ switch(opt) {
+ case '?': cli_usage(); return 0;
+ default:
+ break;
+ }
+ }
+
+ cli_printf("Hello command said: Hello World!! ");
+ for(i = 1; i < argc; i++)
+ cli_printf("%s ", argv[i]);
+ cli_printf("\n");
+
+ return 0;
+ }
+
+ static int
+ pci_file(struct cli_node *node, char *buff, int len, uint32_t opt)
+ {
+ if (is_file_open(opt)) {
+ FILE *f;
+
+ if (node->file_data && (node->fflags & CLI_FREE_DATA))
+ free(node->file_data);
+
+ node->file_data = malloc(32 * 1024);
+ if (!node->file_data)
+ return -1;
+ node->file_size = 32 * 1024;
+ node->fflags = CLI_DATA_RDONLY | CLI_FREE_DATA;
+
+ f = popen("lspci", "r");
+ if (!f)
+ return -1;
+
+ node->file_size = fread(node->file_data, 1, node->file_size, f);
+
+ pclose(f);
+ return 0;
+ }
+ return cli_file_handler(node, buff, len, opt);
+ }
+
+ static struct cli_map show_map[] = {
+ { 10, "show %P" },
+ { 20, "show %P mac %m" },
+ { 30, "show %P vlan %d mac %m" },
+ { 40, "show %P %|vlan|mac" },
+ { -1, NULL }
+ };
+
+ static const char *show_help[] = {
+ "show <portlist>",
+ "show <portlist> mac <ether_addr>",
+ "show <portlist> vlan <vlanid> mac <ether_addr>",
+ "show <portlist> [vlan|mac]",
+ NULL
+ };
+
+ static int
+ show_cmd(int argc, char **argv)
+ {
+ struct cli_map *m;
+ uint32_t portlist;
+ struct ether_addr mac;
+
+ m = cli_mapping(Show_info.map, argc, argv);
+ if (!m)
+ return -1;
+
+ switch(m->index) {
+ case 10:
+ rte_parse_portlist(argv[1], &portlist);
+ cli_printf(" Show Portlist: %08x\n", portlist);
+ break;
+ case 20:
+ rte_parse_portlist(argv[1], &portlist);
+ rte_ether_aton(argv[3], &mac);
+ cli_printf(" Show Portlist: %08x, MAC: "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ portlist,
+ mac.addr_bytes[0],
+ mac.addr_bytes[1],
+ mac.addr_bytes[2],
+ mac.addr_bytes[3],
+ mac.addr_bytes[4],
+ mac.addr_bytes[5]);
+ break;
+ case 30:
+ rte_parse_portlist(argv[1], &portlist);
+ rte_ether_aton(argv[5], &mac);
+ cli_printf(" Show Portlist: %08x vlan %d MAC: "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ portlist,
+ atoi(argv[3]),
+ mac.addr_bytes[0],
+ mac.addr_bytes[1],
+ mac.addr_bytes[2],
+ mac.addr_bytes[3],
+ mac.addr_bytes[4],
+ mac.addr_bytes[5]);
+ break;
+ case 40:
+ rte_parse_portlist(argv[1], &portlist);
+ rte_ether_aton("1234:4567:8901", &mac);
+ cli_printf(" Show Portlist: %08x %s: ",
+ portlist, argv[2]);
+ if (argv[2][0] == 'm')
+ cli_printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac.addr_bytes[0],
+ mac.addr_bytes[1],
+ mac.addr_bytes[2],
+ mac.addr_bytes[3],
+ mac.addr_bytes[4],
+ mac.addr_bytes[5]);
+ else
+ cli_printf("%d\n", 101);
+ break;
+ default:
+ cli_help_show_group("Show");
+ return -1;
+ }
+ return 0;
+ }
+
+ static struct cli_tree my_tree[] = {
+ c_dir("/data"),
+ c_file("pci", pci_file, "display lspci information"),
+ c_dir("/bin"),
+ c_cmd("show", show_cmd, "show mapping options"),
+ c_cmd("hello", hello_cmd, "Hello-World!!"),
+ c_alias("h", "history", "display history commands"),
+ c_alias("ll", "ls -l", "long directory listing alias"),
+ c_end()
+ };
+
+Here is the cli_tree for this example, note it has a lot more commands. The show_cmd
+or show command is located a number of lines down. This cli_tree creates in the
+/bin directory a number of commands, which one is the show command. The
+show command has four different formats if you look at the show_map[].
+
+The user types one of these commands and cli_mapping() attempts to locate the
+correct entry in the list. You will also notice another structure called pcap_help,
+which is an array of strings giving a cleaner and longer help description of
+each of the commands.
+
+These two structure show_map/show_help can be added to the cli_help system
+to provide help for a command using a simple API.
+
+.. code-block::c
+
+ cli_help_add("Show", show_map, show_help);
+
+ cli_help_show_group("Show");
+
+or we can use the cli_help_show_all() API to show all added help information.
+
+.. code-block:: c
+
+ cli_help_show_all(NULL);
+
+The following is from Pktgen source code to add more help to the global
+help for the system.
+
+.. code-block:: c
+
+ cli_help_add("Title", NULL, title_help);
+ cli_help_add("Page", page_map, page_help);
+ cli_help_add("Enable", enable_map, enable_help);
+ cli_help_add("Set", set_map, set_help);
+ cli_help_add("Range", range_map, range_help);
+ cli_help_add("Sequence", seq_map, seq_help);
+ cli_help_add("PCAP", pcap_map, pcap_help);
+ cli_help_add("Start", start_map, start_help);
+ cli_help_add("Debug", debug_map, debug_help);
+ cli_help_add("Misc", misc_map, misc_help);
+ cli_help_add("Theme", theme_map, theme_help);
+ cli_help_add("Status", NULL, status_help);
+
+Understanding the CLI system
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The command line interface is defined as a fake directory tree with executables,
+directories and files. The user uses shell like standard commands to move about
+the directory and execute commands. The CLI is not a powerful as the
+Bash shell, but has a number of similar concepts.
+
+Our fake directory tree has a '/' or root directory which is created when
+cli_create() is called along with the default sbin directory. The user starts out
+at the root directory '/' and is allowed to cd to other directories, which could
+contain more executables, aliases or directories. The max number of directory
+levels is limited to the number of nodes given at startup.
+
+The default directory tree starts out as just root (/) and a sbin directory.
+Also it contains a file called copyright in root, which can be displayed
+using the default 'more copyright' command.
+
+A number of default commands are predefined in the /sbin directory and are
+defined above. Other bin directories can be added to the system if needed,
+but a limit of CLI_MAX_BINS is defined in the cli.h header file.
+
+The CLI structure is created at run time adding directories, commands and
+aliases as needed, which is different from the cmdline interface in DPDK today.
+
+The basic concept for a command is similar to a standard Linux executable,
+meaning the command when executed it is passed the command line in a argc/argv
+format to be parsed by the function. The function is attached to a command file
+in the directory tree and is executed when the user types the name of the
+function along with it arguments. Some examples of the default commands can be
+seen in the lib/librte_cli/cli_cmds.c file.
+
diff --git a/lib/librte_cli/cli_auto_complete.c b/lib/librte_cli/cli_auto_complete.c
new file mode 100644
index 0000000..063e180
--- /dev/null
+++ b/lib/librte_cli/cli_auto_complete.c
@@ -0,0 +1,270 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <fnmatch.h>
+
+#include <rte_string_fns.h>
+
+#include "cli.h"
+#include "cli_auto_complete.h"
+#include "cli_string_fns.h"
+
+static uint32_t
+_column_count(struct cli_node **nodes, uint32_t node_cnt, uint32_t *len)
+{
+ uint32_t i, mlen = 8, cs;
+
+ if (!nodes || !len)
+ return CLI_SCREEN_WIDTH / mlen;
+
+ /* Calculate the column size */
+ for (i = 0; i < node_cnt; i++)
+ mlen = RTE_MAX(mlen, strlen(nodes[i]->name));
+ mlen++; /* Make sure we have at least a space between */
+
+ *len = mlen;
+ cs = CLI_SCREEN_WIDTH / mlen;
+
+ return cs;
+}
+
+static int
+_print_nodes(struct cli_node **nodes, uint32_t node_cnt,
+ uint32_t dir_only, char *match, struct cli_node **ret)
+{
+ struct cli_node *n;
+ uint32_t i, cnt = 0, ccnt, found = 0, slen, csize;
+
+ if (!node_cnt || !nodes)
+ return 0;
+
+ ccnt = _column_count(nodes, node_cnt, &csize);
+
+ slen = (match) ? strlen(match) : 0;
+
+ /* display the node names */
+ for (i = 0; i < node_cnt; i++) {
+ n = nodes[i];
+
+ if (dir_only && !is_directory(n))
+ continue;
+
+ if (slen && strncmp(n->name, match, slen))
+ continue;
+
+ if (!cnt)
+ cli_printf("\n");
+
+ cli_printf("%-*s", csize, n->name);
+ if ((++cnt % ccnt) == 0)
+ cli_printf("\n");
+
+ /* Found a possible match */
+ if (ret)
+ *ret = n;
+ found++;
+ }
+
+ /* if not nodes found cnt will be zero and no CR */
+ if (cnt % ccnt)
+ cli_printf("\n");
+
+ return found;
+}
+
+static int
+qsort_compare(const void *p1, const void *p2)
+{
+ const struct cli_node *n1, *n2;
+
+ n1 = *(const struct cli_node *const *)p1;
+ n2 = *(const struct cli_node *const *)p2;
+
+ return strcmp(n1->name, n2->name);
+}
+
+static int
+complete_args(int argc, char **argv, uint32_t types)
+{
+ struct cli_node **nodes = NULL, *node = NULL;
+ struct gapbuf *gb;
+ char *match;
+ uint32_t node_cnt, found = 0, dir_only = 0, slen;
+
+ if (argc)
+ match = argv[argc - 1];
+ else
+ match = NULL;
+
+ gb = this_cli->gb;
+
+ if (match) {
+ uint32_t stype;
+ uint32_t slashes;
+ char *p;
+
+ /* Count the number of slashes in the path */
+ slashes = rte_strcnt(match, '/');
+
+ if (slashes) {
+ /* full path to command given */
+ if (cli_find_node(match, &node))
+ if (is_executable(node))
+ return 0;
+
+ /* if not found get last directory in path */
+ node = cli_last_node_in_path(match);
+
+ if ((slashes == 1) && (match && (match[0] == '/'))) {
+ match++;
+ dir_only++;
+ }
+ }
+
+ stype = CLI_ALL_TYPE; /* search for all nodes */
+ if (argc > 1)
+ stype = CLI_OTHER_TYPE; /* search for non-exe nodes */
+
+ node_cnt = cli_node_list_with_type(node, stype,
+ (void * *)&nodes);
+ p = strrchr(match, '/');
+ if (p)
+ match = ++p;
+ } else
+ node_cnt = cli_node_list_with_type(NULL, types,
+ (void * *)&nodes);
+
+ if (node_cnt) {
+ struct cli_node *mnode = NULL;
+
+ if (node_cnt > 1)
+ qsort(nodes, node_cnt, sizeof(void *), qsort_compare);
+
+ found = _print_nodes(nodes, node_cnt, dir_only, match, &mnode);
+
+ /*
+ * _match is a pointer to the last matched node
+ * _found is a flag to determine if pointer is valid
+ */
+ if (mnode && (found == 1)) { /* Found a possible match */
+ struct cli_node *node = (struct cli_node *)mnode;
+ char *s;
+ int nlen;
+
+ s = strrchr(match, '/');
+ if (s)
+ match = ++s;
+
+ slen = strlen(match);
+ nlen = (strlen(node->name) - slen);
+
+ if (nlen > 0) /* Add the rest of the matching command */
+ gb_str_insert(gb, &node->name[slen], nlen);
+
+ if (is_directory(node))
+ gb_str_insert(gb, (char *)(uintptr_t)"/", 1);
+ else
+ gb_str_insert(gb, (char *)(uintptr_t)" ", 1);
+ }
+ }
+ cli_node_list_free(nodes);
+
+ return found;
+}
+
+void
+cli_auto_complete(void)
+{
+ char *argv[CLI_MAX_ARGVS + 1];
+ char *line;
+ int argc, size, ret;
+
+ memset(argv, '\0', sizeof(argv));
+
+ size = gb_data_size(this_cli->gb);
+ if (!size)
+ return;
+
+ line = alloca(size + 1);
+ if (!line)
+ return;
+ memset(line, '\0', size + 1);
+
+ gb_copy_to_buf(this_cli->gb, line, size);
+
+ argc = rte_strtok(line, " \r\n", argv, CLI_MAX_ARGVS);
+
+ if (argc == 0) {
+ ret = complete_args(argc, argv, CLI_ALL_TYPE);
+
+ if (ret)
+ cli_display_line();
+ return;
+ }
+
+ /* no space before cursor maybe a command completion request */
+ if (gb_get_prev(this_cli->gb) != ' ') {
+ if (argc == 1) /* Only one word then look for a command */
+ ret = complete_args(argc, argv, CLI_ALL_TYPE);
+ else /* If more then one word then look for file/dir */
+ ret = complete_args(argc, argv,
+ CLI_FILE_NODE | CLI_DIR_NODE);
+
+ /* if we get an error then redisplay the line */
+ if (ret)
+ cli_display_line();
+ } else {
+ char *save = alloca(size + 1);
+
+ if (!save)
+ return;
+
+ memset(save, '\0', size + 1);
+
+ /* Call function to print out help text, plus save a copy */
+ gb_copy_to_buf(this_cli->gb, save, size);
+
+ /* Add the -? to the command */
+ gb_str_insert(this_cli->gb, (char *)(uintptr_t)"-?", 2);
+
+ cli_execute();
+
+ /* reset the input buffer to remove -? */
+ gb_reset_buf(this_cli->gb);
+
+ /* insert the saved string back to the input buffer */
+ gb_str_insert(this_cli->gb, save, size);
+
+ cli_display_line();
+ }
+}
diff --git a/lib/librte_cli/cli_auto_complete.h b/lib/librte_cli/cli_auto_complete.h
new file mode 100644
index 0000000..71731a5
--- /dev/null
+++ b/lib/librte_cli/cli_auto_complete.h
@@ -0,0 +1,61 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_AUTO_COMPLETE_H_
+#define _CLI_AUTO_COMPLETE_H_
+
+/**
+ * @file
+ * Command line interface auto complete routines.
+ *
+ */
+
+#include "cli.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* Handle the tab key for auto complete (Internal)
+*
+* @return
+* N/A
+*/
+void cli_auto_complete(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_AUTO_COMPLETE_H_ */
diff --git a/lib/librte_cli/cli_cmap.c b/lib/librte_cli/cli_cmap.c
new file mode 100644
index 0000000..42a50f4
--- /dev/null
+++ b/lib/librte_cli/cli_cmap.c
@@ -0,0 +1,377 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Prints a CPU core map on the form
+ * "S/C/T L"
+ * where
+ * - S is the CPU socket ID
+ * - C is the physical CPU core ID
+ * - T is the hyper-thread ID
+ * - L is the logical core ID
+ *
+ * This tool parses the information from "/proc/cpuinfo" which should
+ * be present on all Linux systems.
+ *
+ * NOTE: this tool has only been tested on systems with x86/x86_64
+ * CPUs so far.
+ *
+ * Written 2011 by Kenneth Jonsson, WindRiver.
+ * Adapted to DPDK by Keith Wiles, WindRiver 2013-01-08
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "cli_cmap.h"
+
+static char *model_name;
+
+char *
+cmap_cpu_model(void)
+{
+ return model_name;
+}
+
+static const char *
+as_str(const char *line)
+{
+ if (*line != ':')
+ return as_str(line + 1);
+ return line + 1;
+}
+
+static unsigned
+as_int(const char *line)
+{
+ return atoi(as_str(line));
+}
+
+static lcore_t *
+new_lcore(const char *line, lcore_t *rest)
+{
+ lcore_t *lc = calloc(1, sizeof(lcore_t));
+
+ lc->next = rest;
+ lc->u.lid = as_int(line);
+
+ return lc;
+}
+
+static lcore_t *
+set_raw_socket_id(const char *line, lcore_t *lc)
+{
+ lc->u.sid = as_int(line);
+ return lc;
+}
+
+static lcore_t *
+set_raw_core_id(const char *line, lcore_t *lc)
+{
+ lc->u.cid = as_int(line);
+ return lc;
+}
+
+static lcore_t *
+set_model_name(const char *line, lcore_t *lc)
+{
+ if (!model_name)
+ model_name = strdup(as_str(line));
+ return lc;
+}
+
+static unsigned
+get_next_thread_id(const lcore_t *lc, unsigned socket_id, unsigned core_id)
+{
+ if (lc == NULL)
+ return 0;
+ if (lc->u.sid == socket_id && lc->u.cid == core_id)
+ return lc->u.tid + 1;
+ return get_next_thread_id(lc->next, socket_id, core_id);
+}
+
+static lcore_t *
+set_thread_id_str(const char *unused, lcore_t *lc)
+{
+ (void)unused;
+ lc->u.tid = get_next_thread_id(lc->next, lc->u.sid, lc->u.cid);
+ return lc;
+}
+
+static lcore_t *
+ignore_line(const char *unused, lcore_t *lc)
+{
+ (void)unused;
+ return lc;
+}
+
+static do_line_fn
+get_matching_action(const char *line)
+{
+ static struct action actions[] = {
+ { "processor", new_lcore },
+ { "physical id", set_raw_socket_id },
+ { "core id", set_raw_core_id },
+ { "model name", set_model_name },
+ { "\n", set_thread_id_str },
+ { NULL, NULL }
+ };
+ struct action *action;
+
+ for (action = actions; action->fn != NULL; ++action)
+ if (strncmp(action->desc, line, strlen(action->desc)) == 0)
+ return action->fn;
+
+ return ignore_line;
+}
+
+/*
+ * Remaps a property value from 'from' to 'to'. This is done for all
+ * logical cores.
+ */
+static void
+remap(lcore_t *lc,
+ unsigned from,
+ unsigned to,
+ getter_fn get,
+ setter_fn set)
+{
+ if (lc) {
+ if (get(lc) == from)
+ set(lc, to);
+
+ remap(lc->next, from, to, get, set);
+ }
+}
+
+/*
+ * Returns the first entry that is equal to or as close as possible to
+ * 'v' in the property returned by 'get'.
+ */
+static lcore_t *
+closest_gte(lcore_t *lc, lcore_t *sel, unsigned v, getter_fn get)
+{
+ if (lc == NULL)
+ return sel;
+
+ if (get(lc) >= v && (sel == NULL || get(sel) - v > get(lc) - v))
+ return closest_gte(lc->next, lc, v, get);
+
+ return closest_gte(lc->next, sel, v, get);
+}
+
+/*
+ * Makes the property returned and set by 'get'/'set' start from zero
+ * and increase by one for each unique value that propery has.
+ * Ex: core id "0,1,4,5,0,1,4,5" -> "0,1,2,3,0,1,2,3"
+ */
+static void
+zero_base(lcore_t *head, getter_fn get, setter_fn set)
+{
+ unsigned id = 0;
+ lcore_t *lc;
+
+ while ((lc = closest_gte(head, NULL, id, get)) != NULL) {
+ remap(lc, get(lc), id, get, set);
+ ++id;
+ }
+}
+
+static void
+get_and_free_lcore_info(lcore_t *lc, lc_info_t *get)
+{
+ if (lc) {
+ get_and_free_lcore_info(lc->next, get + 1);
+ get->word = lc->u.word;
+ free(lc);
+ }
+}
+
+static inline int
+count_cores(lcore_t *lcores)
+{
+ int num = 0;
+
+ while (lcores) {
+ lcores = lcores->next;
+ num++;
+ }
+ return num;
+}
+
+static int
+my_getline(char **line, size_t *line_sz, int fd)
+{
+ char *l, c;
+ size_t sz, i;
+
+ if (*line == NULL) {
+ if (*line_sz == 0)
+ *line_sz = MAX_LINE_SIZE;
+ l = malloc(*line_sz);
+ if (l == NULL)
+ return -1;
+ *line = l;
+ } else
+ l = *line;
+
+ for (i = 0, sz = 0; i < *line_sz; i++) {
+ if (read(fd, &c, 1) != 1)
+ return -1;
+ *l++ = c;
+ sz++;
+ if (c == '\n')
+ break;
+ }
+ *l = '\0';
+ return sz;
+}
+
+#define MAX_CNT 256
+static lc_info_t lcore_info[MAX_CNT];
+
+struct cmap *
+cmap_create(void)
+{
+ int fd;
+ char *line = NULL;
+ struct cmap *cmap;
+ lc_info_t *lc_info = &lcore_info[0];
+ size_t line_sz = 0;
+ lcore_t *lcores = NULL;
+
+ memset(lcore_info, '\0', sizeof(lcore_info));
+
+ cmap = malloc(sizeof(struct cmap));
+ if (!cmap)
+ return NULL;
+
+ if ( (fd = open(PROC_CPUINFO, O_RDONLY)) < 0) {
+ fprintf(stderr, "Cannot open %s on this system\n",
+ PROC_CPUINFO);
+ free(cmap);
+ return NULL;
+ }
+
+ while (my_getline(&line, &line_sz, fd) >= 0)
+ lcores = get_matching_action(line) (line, lcores);
+
+ if (fd) close(fd);
+ if (line) free(line);
+
+ zero_base(lcores, cmap_socket_id, cmap_set_socket_id);
+ zero_base(lcores, cmap_core_id, cmap_set_core_id);
+
+ cmap->linfo = lc_info;
+
+ cmap->model = model_name;
+ cmap->num_cores = count_cores(lcores);
+ cmap->sid_cnt = cmap_cnt(lcores, cmap_socket_id);
+ cmap->cid_cnt = cmap_cnt(lcores, cmap_core_id);
+ cmap->tid_cnt = cmap_cnt(lcores, cmap_thread_id);
+
+ get_and_free_lcore_info(lcores, lc_info);
+
+ return cmap;
+}
+
+void
+cmap_free(struct cmap *cmap)
+{
+ free(cmap);
+}
+
+/* Helper for building log strings.
+ * The macro takes an existing string, a printf-like format string and optional
+ * arguments. It formats the string and appends it to the existing string,
+ * while avoiding possible buffer overruns.
+ */
+#define strncatf(dest, fmt, ...) do { \
+ char _buff[1024]; \
+ snprintf(_buff, sizeof(_buff), fmt, ## __VA_ARGS__); \
+ strncat(dest, _buff, sizeof(dest) - strlen(dest) - 1); \
+} while (0)
+
+static __inline__ uint8_t
+sct(struct cmap *cm, uint8_t s, uint8_t c, uint8_t t) {
+ lc_info_t *lc = cm->linfo;
+ uint8_t i;
+
+ for (i = 0; i < cm->num_cores; i++, lc++)
+ if (lc->sid == s && lc->cid == c && lc->tid == t)
+ return lc->lid;
+
+ return 0;
+}
+
+void
+cmap_dump(FILE *f)
+{
+ struct cmap *c = cmap_create();
+ int i;
+
+ if (!f)
+ f = stdout;
+
+ fprintf(f, "CPU : %s", c->model);
+ fprintf(f, " %d lcores, %u socket%s, %u core%s per socket and "
+ "%u thread%s per core\n",
+ c->num_cores,
+ c->sid_cnt, c->sid_cnt > 1 ? "s" : "",
+ c->cid_cnt, c->cid_cnt > 1 ? "s" : "",
+ c->tid_cnt, c->tid_cnt > 1 ? "s" : "");
+
+ fprintf(f, "Socket : ");
+ for (i = 0; i < c->sid_cnt; i++)
+ fprintf(f, "%4d ", i);
+
+ for (i = 0; i < c->cid_cnt; i++) {
+ fprintf(f, " Core %3d : [%2d,%2d] ", i,
+ sct(c, 0, i, 0), sct(c, 0, i, 1));
+ if (c->sid_cnt > 1)
+ fprintf(f, "[%2d,%2d] ",
+ sct(c, 1, i, 0), sct(c, 1, i, 1));
+ if (c->sid_cnt > 2)
+ fprintf(f, "[%2d,%2d] ",
+ sct(c, 2, i, 0), sct(c, 2, i, 1));
+ if (c->sid_cnt > 3)
+ fprintf(f, "[%2d,%2d] ",
+ sct(c, 3, i, 0), sct(c, 3, i, 1));
+ fprintf(f, "\n");
+ }
+ cmap_free(c);
+}
diff --git a/lib/librte_cli/cli_cmap.h b/lib/librte_cli/cli_cmap.h
new file mode 100644
index 0000000..034237a
--- /dev/null
+++ b/lib/librte_cli/cli_cmap.h
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) <2017>, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * - Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __CLI_CMAP_H
+#define __CLI_CMAP_H
+
+#include <stdint.h>
+
+#define MAX_LINE_SIZE 4096
+
+#define PROC_CPUINFO "/proc/cpuinfo"
+
+typedef union {
+ struct {
+ uint8_t lid; /* Logical core ID */
+ uint8_t sid; /* CPU socket ID */
+ uint8_t cid; /* Physical CPU core ID */
+ uint8_t tid; /* Hyper-thread ID */
+ };
+ uint32_t word;
+} lc_info_t;
+
+typedef struct lcore {
+ struct lcore *next;
+ lc_info_t u;
+} lcore_t;
+
+struct cmap {
+ uint16_t num_cores;
+ uint16_t sid_cnt;
+ uint16_t cid_cnt;
+ uint16_t tid_cnt;
+ lc_info_t *linfo;
+ char *model;
+};
+
+typedef lcore_t *(*do_line_fn)(const char *line, lcore_t *);
+typedef unsigned (*getter_fn)(const lcore_t *);
+typedef void (*setter_fn)(lcore_t *, unsigned new_val);
+
+typedef struct action {
+ const char *desc;
+ do_line_fn fn;
+} action_t;
+
+/**
+ * Create a cmap structure for the current system
+ *
+ * @return
+ * The pointer to the cmap structure or NULL on error
+ */
+struct cmap *cmap_create(void);
+
+/**
+ * Return the current CPU model string
+ *
+ * @return
+ * Pointer to current CPU model string.
+ */
+char *cmap_cpu_model(void);
+
+/**
+ * Free up the resources attached to a cmap structure
+ *
+ * @param cmap
+ * A valid cmap pointer
+ */
+void cmap_free(struct cmap *cmap);
+
+/**
+ * Return the socket id for a given lcore (Internal)
+ *
+ * @param lc
+ * Pointer to the given lcore structure
+ * @return
+ * The socket ID value
+ */
+static inline unsigned int
+cmap_socket_id(const lcore_t *lc)
+{
+ return lc->u.sid;
+}
+
+/**
+ * Set the socket id for a given lcore (Internal)
+ *
+ * @param lc
+ * Pointer to the given lcore structure
+ * @param v
+ * Set the socket id value
+ * @return
+ * N/A
+ */
+static inline void
+cmap_set_socket_id(lcore_t *lc, unsigned v)
+{
+ lc->u.sid = v;
+}
+
+/**
+ * Return the core id for a given lcore (Internal)
+ *
+ * @param lc
+ * Pointer to the given lcore structure
+ * @return
+ * The core ID value
+ */
+static inline unsigned int
+cmap_core_id(const lcore_t *lc)
+{
+ return lc->u.cid;
+}
+
+/**
+ * Set the core id for a given lcore (Internal)
+ *
+ * @param lc
+ * Pointer to the given lcore structure
+ * @param v
+ * Set the core id value
+ * @return
+ * N/A
+ */
+static inline void
+cmap_set_core_id(lcore_t *lc, unsigned v)
+{
+ lc->u.cid = v;
+}
+
+/**
+ * Return the thread id for a given lcore (Internal)
+ *
+ * @param lc
+ * Pointer to the given lcore structure
+ * @return
+ * The thread ID value
+ */
+static inline unsigned int
+cmap_thread_id(const lcore_t *lc)
+{
+ return lc->u.tid;
+}
+
+/**
+ * Returns the number of unique values that exist of the property.
+ *
+ * @param lc
+ * Pointer to the lcore structure
+ * @param get
+ * A function pointer to help count the type of values
+ */
+static inline unsigned int
+cmap_cnt(lcore_t *lc, getter_fn get)
+{
+ unsigned cnt = 0;
+
+ if (!get)
+ return cnt;
+
+ while (lc) {
+ if (cnt < get(lc))
+ cnt = get(lc);
+ lc = lc->next;
+ }
+ return cnt + 1;
+}
+
+/**
+ * Dump out the CMAP data
+ *
+ * @param f
+ * The file descriptor for output
+ */
+void cmap_dump(FILE *f);
+
+#endif /*_CLI_CMAP_H */
diff --git a/lib/librte_cli/cli_cmds.c b/lib/librte_cli/cli_cmds.c
new file mode 100644
index 0000000..130de56
--- /dev/null
+++ b/lib/librte_cli/cli_cmds.c
@@ -0,0 +1,749 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include <rte_version.h>
+#include <rte_cycles.h>
+#include <rte_timer.h>
+#include <rte_devargs.h>
+#include <rte_pci.h>
+#include <rte_debug.h>
+#include <rte_log.h>
+#include <rte_string_fns.h>
+
+#include "cli.h"
+#include "cli_cmds.h"
+#include "cli_cmap.h"
+#include "cli_map.h"
+#include "cli_file.h"
+#include "cli_string_fns.h"
+
+static int
+__print_help(struct cli_node *node, char *search)
+{
+ struct cli_node *cmd;
+
+ if (!node)
+ node = get_cwd();
+ else if (!is_directory(node))
+ return -1;
+
+ TAILQ_FOREACH(cmd, &node->items, next) {
+ if (is_executable(cmd)) {
+ if (search) {
+ if (strcmp(cmd->name, search) == 0) {
+ cli_printf(" %-16s %s\n", cmd->name,
+ cmd->short_desc);
+ return 1;
+ }
+ } else
+ cli_printf(" %-16s %s\n", cmd->name,
+ cmd->short_desc);
+ }
+ }
+ return 0;
+}
+
+static int
+chelp_cmd(int argc, char **argv)
+{
+ struct cli *cli = this_cli;
+ struct cli_node *bin;
+ char *search = NULL;
+ int i, opt, all = 0;
+
+ optind = 0;
+ while ((opt = getopt(argc, argv, "a?")) != -1) {
+ switch (opt) {
+ case '?': cli_usage(); return 0;
+ case 'a': all = 1; break;
+ default:
+ break;
+ }
+ }
+ if (optind < argc)
+ search = argv[optind];
+
+ cli_printf("*** CLI Help ***\n");
+ cli_printf(" Use <command> -? to show usage for a command\n");
+ cli_printf(" Use !<NN> to execute a history line\n");
+ cli_printf(" Use @<host command> to execute a host binary\n");
+ cli_printf(" Use Up/Down arrows to access history commands\n\n");
+ cli_printf(" Use 'chelp -a' to list all commands\n");
+
+ if (all == 0) {
+ /* Look in the current directory first for a command */
+ cli_printf("*** Current directory commands ***\n");
+
+ return __print_help(NULL, search);
+ }
+
+ cli_printf("*** All executable commands in path ***\n");
+
+ /* Did not find a command in local then look in the bin dirs */
+ for (i = 0; i < CLI_MAX_BINS; i++) {
+ bin = cli->bins[i];
+ if (bin == NULL)
+ continue;
+
+ cli_printf("%s:\n", bin->name);
+
+ if (__print_help(bin, search))
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+cd_cmd(int argc, char **argv)
+{
+ struct cli_node *node;
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "-?")) {
+ cli_usage();
+ return 0;
+ }
+
+ if (!cli_find_node(argv[1], &node)) {
+ cli_printf("** Invalid directory: %s\n", argv[1]);
+ return -1;
+ }
+ set_cwd(node);
+ }
+
+ return 0;
+}
+
+static int
+pwd_cmd(int argc, char **argv)
+{
+ char *str = cli_cwd_path();
+
+ if (argc > 1 && !strcmp(argv[1], "-?")) {
+ cli_usage();
+ return 0;
+ }
+
+ /* trim off the trailing '/' if needed */
+ if (strlen(str) > 1)
+ str[strlen(str) - 1] = '\0';
+
+ cli_printf("%s\n", str);
+ return 0;
+}
+
+static int
+__list_long_dir(struct cli_node *node,
+ uint32_t type __rte_unused, args_t *args)
+{
+ uint16_t flags = args->arg1.u16[3];
+ uint16_t spc = args->arg2.u16[0];
+
+ if (is_alias(node))
+ cli_printf(" %*s%-16s %s : %s\n", spc, "",
+ node->name, cli_node_type(node), node->alias_str);
+ else if (is_command(node))
+ cli_printf(" %*s%-16s %s : %s\n", spc, "",
+ node->name, cli_node_type(node), node->short_desc);
+ else
+ cli_printf(" %*s%-16s %s\n", spc, "",
+ node->name, cli_node_type(node));
+
+ if ((flags & CLI_RECURSE_FLAG) && is_directory(node)) {
+ args->arg2.u16[0] += 2;
+ cli_scan_directory(node, __list_long_dir, type, args);
+ args->arg2.u16[0] = spc;
+ }
+
+ return 0;
+}
+
+static int
+__list_dir(struct cli_node *node, uint32_t flag __rte_unused, args_t *args)
+
+{
+ char buf[CLI_NAME_LEN + 1];
+ uint16_t cnt = args->arg1.u16[0];
+ uint16_t mlen = args->arg1.u16[1];
+ uint16_t col = args->arg1.u16[2];
+ uint16_t flags = args->arg1.u16[3];
+
+ if (!node)
+ return -1;
+
+ if (is_directory(node)) {
+ char dbuf[CLI_NAME_LEN + 1];
+ snprintf(dbuf, sizeof(dbuf), "[%s]", node->name);
+ snprintf(buf, sizeof(buf), "%-*s", mlen, dbuf);
+ } else
+ snprintf(buf, sizeof(buf), "%-*s", mlen, node->name);
+
+ cli_printf("%s", buf);
+ if ((++cnt % col) == 0)
+ cli_printf("\n");
+
+ if ((flags & CLI_RECURSE_FLAG) && is_directory(node)) {
+ cli_printf("\n");
+ args->arg1.u16[0] = 0;
+ cli_scan_directory(node, __list_dir, CLI_ALL_TYPE, args);
+ args->arg1.u16[0] = cnt;
+ cli_printf("\n");
+ }
+
+ args->arg1.u16[0] = cnt;
+ return 0;
+}
+
+static int
+ls_cmd(int argc, char **argv)
+{
+ struct cli_node *node = get_cwd();
+ args_t args;
+ uint32_t flags = 0;
+ int opt;
+
+ optind = 0;
+ while ((opt = getopt(argc, argv, "?rl")) != -1) {
+ switch (opt) {
+ case '?': cli_usage(); return 0;
+ case 'r': flags |= CLI_RECURSE_FLAG; break;
+ case 'l': flags |= CLI_LONG_LIST_FLAG; break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc)
+ if (cli_find_node(argv[optind], &node) == 0) {
+ cli_printf("Invalid directory (%s)!!\n", argv[optind]);
+ return -1;
+ }
+
+ memset(&args, 0, sizeof(args));
+
+ args.arg1.u16[0] = 0;
+ args.arg1.u16[1] = 16;
+ args.arg1.u16[2] = 80 / 16;
+ args.arg1.u16[3] = flags;
+ args.arg2.u16[0] = 0;
+
+ if (flags & CLI_LONG_LIST_FLAG)
+ cli_scan_directory(node, __list_long_dir, CLI_ALL_TYPE, &args);
+ else
+ cli_scan_directory(node, __list_dir, CLI_ALL_TYPE, &args);
+
+ printf("\n");
+ return 0;
+}
+
+static int
+scrn_cmd(int argc __rte_unused, char **argv __rte_unused)
+{
+ cli_clear_screen();
+ return 0;
+}
+
+static int
+quit_cmd(int argc __rte_unused, char **argv __rte_unused)
+{
+ this_cli->quit_flag = 1;
+ return 0;
+}
+
+static int
+hist_cmd(int argc, char **argv)
+{
+ if (argc > 1 && !strcmp(argv[1], "-?"))
+ cli_usage();
+ else
+ cli_history_list();
+ return 0;
+}
+
+static int
+more_cmd(int argc, char **argv)
+{
+ struct cli_node *node;
+ char *buf, c;
+ int i, len, n, k, lines = 24;
+ int opt;
+
+ optind = 0;
+ while ((opt = getopt(argc, argv, "?n:")) != -1) {
+ switch (opt) {
+ case '?': cli_usage(); return 0;
+ case 'n': lines = atoi(optarg); break;
+ default:
+ break;
+ }
+ }
+
+ if (optind >= argc)
+ return 0;
+
+ len = 256;
+ buf = alloca(len + 1);
+ if (!buf)
+ return -1;
+
+ for (i = optind; i < argc; i++) {
+ k = 0;
+ node = cli_file_open(argv[i], "r");
+ if (!node) {
+ cli_printf("** (%s) is not a file\n", argv[i]);
+ continue;
+ }
+ do {
+ n = cli_readline(node, buf, len);
+ if (n > 0)
+ cli_printf("%s", buf); /* contains a newline */
+ if (++k >= lines) {
+ k = 0;
+ c = cli_pause("More", NULL);
+ if ((c == vt100_escape) ||
+ (c == 'q') || (c == 'Q'))
+ break;
+ }
+ } while (n > 0);
+ cli_file_close(node);
+ }
+
+ cli_printf("\n");
+
+ return 0;
+}
+
+/* Helper for building log strings.
+ * The macro takes an existing string, a printf-like format string and optional
+ * arguments. It formats the string and appends it to the existing string, while
+ * avoiding possible buffer overruns.
+ */
+#define strncatf(dest, fmt, ...) do { \
+ char _buff[1024]; \
+ snprintf(_buff, sizeof(_buff), fmt, ## __VA_ARGS__); \
+ strncat(dest, _buff, sizeof(dest) - strlen(dest) - 1); \
+} while (0)
+
+static __inline__ uint8_t
+sct(struct cmap *cm, uint8_t s, uint8_t c, uint8_t t) {
+ lc_info_t *lc = cm->linfo;
+ uint8_t i;
+
+ for (i = 0; i < cm->num_cores; i++, lc++)
+ if (lc->sid == s && lc->cid == c && lc->tid == t)
+ return lc->lid;
+
+ return 0;
+}
+
+static int
+core_cmd(int argc __rte_unused, char **argv __rte_unused)
+{
+ struct cmap *c;
+ int i;
+
+ c = cmap_create();
+
+ cli_printf("CPU : %s", c->model);
+ cli_printf(" %d lcores, %u socket%s, %u core%s per socket and "
+ "%u thread%s per core\n",
+ c->num_cores,
+ c->sid_cnt, c->sid_cnt > 1 ? "s" : "",
+ c->cid_cnt, c->cid_cnt > 1 ? "s" : "",
+ c->tid_cnt, c->tid_cnt > 1 ? "s" : "");
+
+ cli_printf("Socket : ");
+ for (i = 0; i < c->sid_cnt; i++)
+ cli_printf("%4d ", i);
+
+ for (i = 0; i < c->cid_cnt; i++) {
+ cli_printf(" Core %3d : [%2d,%2d] ", i,
+ sct(c, 0, i, 0), sct(c, 0, i, 1));
+ if (c->sid_cnt > 1)
+ cli_printf("[%2d,%2d] ",
+ sct(c, 1, i, 0), sct(c, 1, i, 1));
+ if (c->sid_cnt > 2)
+ cli_printf("[%2d,%2d] ",
+ sct(c, 2, i, 0), sct(c, 2, i, 1));
+ if (c->sid_cnt > 3)
+ cli_printf("[%2d,%2d] ",
+ sct(c, 3, i, 0), sct(c, 3, i, 1));
+ cli_printf("\n");
+ }
+
+ cmap_free(c);
+
+ return 0;
+}
+
+static int
+huge_cmd(int argc __rte_unused, char **argv __rte_unused)
+{
+ if (system("cat /proc/meminfo | grep -i huge"))
+ return -1;
+ return 0;
+}
+
+#ifdef CLI_DEBUG_CMDS
+static int
+sizes_cmd(int argc, char **argv)
+{
+ if (argc > 1 && !strcmp(argv[1], "-?")) {
+ cli_usage();
+ return 0;
+ }
+
+ cli_printf(" sizeof(struct cli) %zu\n", sizeof(struct cli));
+ cli_printf(" sizeof(struct cli_node) %zu\n", sizeof(struct cli_node));
+ cli_printf(" sizeof(args_t) %zu\n", sizeof(args_t));
+ cli_printf(" Total number of Nodes %d\n", this_cli->nb_nodes);
+ cli_printf(" Number History lines %d\n", this_cli->nb_hist);
+ cli_printf(" CLI_DEFAULT_NB_NODES %d\n", CLI_DEFAULT_NB_NODES);
+ cli_printf(" CLI_DEFAULT_HIST_LINES %d\n", CLI_DEFAULT_HIST_LINES);
+ cli_printf(" CLI_MAX_SCRATCH_LENGTH %d\n", CLI_MAX_SCRATCH_LENGTH);
+ cli_printf(" CLI_MAX_PATH_LENGTH %d\n", CLI_MAX_PATH_LENGTH);
+ cli_printf(" CLI_NAME_LEN %d\n", CLI_NAME_LEN);
+ cli_printf(" CLI_MAX_ARGVS %d\n", CLI_MAX_ARGVS);
+ cli_printf(" CLI_MAX_BINS %d\n", CLI_MAX_BINS);
+
+ return 0;
+}
+#endif
+
+static int
+path_cmd(int argc __rte_unused, char **argv __rte_unused)
+{
+ int i;
+ char *str;
+
+ cli_printf(" Path = .:");
+ for (i = 1; i < CLI_MAX_BINS; i++) {
+ if (this_cli->bins[i] == NULL)
+ continue;
+ str = cli_path_string(this_cli->bins[i], NULL);
+
+ /* trim off the trailing '/' if needed */
+ if (strlen(str) > 1)
+ str[strlen(str) - 1] = '\0';
+
+ cli_printf("%s:", str);
+ }
+ cli_printf("\n");
+
+ return 0;
+}
+
+static const char *copyright =
+" BSD LICENSE\n"
+"\n"
+" Copyright(c) 2010-2017 Intel Corporation. All rights reserved.\n"
+" All rights reserved.\n"
+"\n"
+" Redistribution and use in source and binary forms, with or without\n"
+" modification, are permitted provided that the following conditions\n"
+" are met:\n"
+"\n"
+" * Redistributions of source code must retain the above copyright\n"
+" notice, this list of conditions and the following disclaimer.\n"
+" * Redistributions in binary form must reproduce the above copyright\n"
+" notice, this list of conditions and the following disclaimer in\n"
+" the documentation and/or other materials provided with the\n"
+" distribution.\n"
+" * Neither the name of Intel Corporation nor the names of its\n"
+" contributors may be used to endorse or promote products derived\n"
+" from this software without specific prior written permission.\n"
+"\n"
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+" \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+" OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
+
+static int
+copyright_file(struct cli_node *node, char *buff, int len, uint32_t flags)
+{
+
+ if (is_file_open(flags)) {
+ node->file_data = (char *)(uintptr_t)copyright;
+ node->file_size = strlen(copyright);
+ node->fflags = CLI_DATA_RDONLY;
+ if (is_file_eq(flags, (CLI_FILE_APPEND | CLI_FILE_WR)))
+ node->foffset = node->file_size;
+ return 0;
+ }
+ return cli_file_handler(node, buff, len, flags);
+}
+
+static int
+version_file(struct cli_node *node, char *buff, int len, uint32_t flags)
+{
+ const char *data = rte_version();
+
+ if (is_file_open(flags)) {
+ node->file_data = (char *)(uintptr_t)data;
+ node->file_size = strlen(data);
+ node->fflags = CLI_DATA_RDONLY;
+ if (is_file_eq(flags, (CLI_FILE_APPEND | CLI_FILE_WR)))
+ node->foffset = node->file_size;
+ return 0;
+ }
+ return cli_file_handler(node, buff, len, flags);
+}
+
+static int
+sleep_cmd(int argc __rte_unused, char **argv)
+{
+ uint32_t cnt = (atoi(argv[1]) * 4);
+
+ if (rte_get_timer_hz() == 0) {
+ cli_printf("rte_get_timer_hz() returned zero\n");
+ return 0;
+ }
+
+ if (cli_use_timers()) {
+ while (cnt--) {
+ rte_timer_manage();
+ rte_delay_ms(250);
+ }
+ } else {
+ while (cnt--)
+ rte_delay_ms(250);
+ }
+ return 0;
+}
+
+static int
+delay_cmd(int argc __rte_unused, char **argv)
+{
+ int ms = atoi(argv[1]);
+ int cnt = (ms / 1000) * 4;
+
+ while (cnt--) {
+ if (cli_use_timers())
+ rte_timer_manage();
+ rte_delay_ms(250);
+ ms -= 250;
+ }
+ if (ms > 0)
+ rte_delay_ms(ms);
+ return 0;
+}
+
+static struct cli_map cli_debug_map[] = {
+ { 10, "debug pci" },
+ { 20, "debug dev" },
+ { -1, NULL }
+};
+
+static int
+dbg_cmd(int argc, char **argv)
+{
+ struct cli_map *m;
+
+ m = cli_mapping(cli_debug_map, argc, argv);
+ if (!m)
+ return -1;
+ switch (m->index) {
+ case 10:
+#if RTE_VERSION < RTE_VERSION_NUM(17, 5, 0, 0)
+ rte_eal_pci_dump(stdout);
+#else
+ rte_pci_dump(stdout);
+#endif
+ break;
+ case 20: rte_eal_devargs_dump(stdout); break;
+ default:
+ cli_help_show_group("debug");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+mkdir_cmd(int argc, char **argv)
+{
+ if (argc != 2) {
+ cli_printf("Must have at least one path/driectory\n");
+ return -1;
+ }
+
+ if (!cli_add_dir(argv[1], get_cwd()))
+ return -1;
+ return 0;
+}
+
+static int
+rm_cmd(int argc, char **argv)
+{
+ struct cli_node *node;
+
+ if (argc != 2) {
+ cli_printf("usage: rm [dir|file|command]\n");
+ return -1;
+ }
+
+ if (!cli_find_node(argv[1], &node)) {
+ cli_printf("Unable to find: %s\n", argv[1]);
+ return -1;
+ }
+
+ return cli_remove_node(node);
+}
+
+static char *
+ver_cmd(const char *val __rte_unused)
+{
+ return (char *)(uintptr_t)rte_version();
+}
+
+static struct cli_map cli_env_map[] = {
+ { 10, "env" },
+ { 20, "env get %s" },
+ { 30, "env set %s %s" },
+ { 40, "env del %s" },
+ { -1, NULL }
+};
+
+static int
+env_cmd(int argc, char **argv)
+{
+ struct cli_map *m;
+
+ m = cli_mapping(cli_env_map, argc, argv);
+ if (!m)
+ return -1;
+ switch (m->index) {
+ case 10: cli_env_show(this_cli->env); break;
+ case 20:
+ cli_printf(" \"%s\" = \"%s\"\n", argv[2],
+ cli_env_get(this_cli->env, argv[2]));
+ break;
+ case 30: cli_env_set(this_cli->env, argv[2], argv[3]); break;
+ case 40: cli_env_del(this_cli->env, argv[2]); break;
+ default:
+ cli_help_show_group("env");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+script_cmd(int argc, char **argv)
+{
+ int i;
+
+ if (argc <= 1)
+ return -1;
+
+ for(i = 1; i < argc; i++)
+ if (cli_execute_cmdfile(argv[1]))
+ return -1;
+ return 0;
+}
+
+static int
+echo_cmd(int argc, char **argv)
+{
+ int i;
+
+ for(i = 1; i < argc; i++)
+ cli_printf("%s ",argv[i]);
+ cli_printf("\n");
+ return 0;
+}
+
+static int
+version_cmd(int argc __rte_unused, char **argv __rte_unused)
+{
+ cli_printf("Version: %s\n", rte_version());
+ return 0;
+}
+
+static struct cli_tree cli_default_tree[] = {
+c_file("copyright", copyright_file, "DPDK copyright information"),
+c_file("dpdk-version", version_file, "DPDK version"),
+c_dir("/sbin"),
+
+c_cmd("delay", delay_cmd, "delay a number of milliseconds"),
+c_cmd("sleep", sleep_cmd, "delay a number of seconds"),
+c_cmd("chelp", chelp_cmd, "CLI help - display information for DPDK"),
+c_cmd("mkdir", mkdir_cmd, "create a directory"),
+c_cmd("rm", rm_cmd, "remove a file or directory"),
+c_cmd("ls", ls_cmd, "ls [-lr] <dir> # list current directory"),
+c_cmd("cd", cd_cmd, "cd <dir> # change working directory"),
+c_cmd("pwd", pwd_cmd, "pwd # display current working directory"),
+c_cmd("screen.clear", scrn_cmd, "screen.clear # clear the screen"),
+c_cmd("quit", quit_cmd, "quit # quit the application"),
+c_cmd("history", hist_cmd, "history # display the current history"),
+c_cmd("more", more_cmd, "more <file> # display a file content"),
+#ifdef CLI_DEBUG_CMDS
+c_cmd("sizes", sizes_cmd, "sizes # display some internal sizes"),
+#endif
+c_cmd("cmap", core_cmd, "cmap # display the core mapping"),
+c_cmd("hugepages", huge_cmd, "hugepages # display hugepage info"),
+c_cmd("path", path_cmd, "display the execution path for commands"),
+c_cmd("debug", dbg_cmd, "debug commands for pci and dev"),
+c_cmd("env", env_cmd, "Show/del/get/set environment variables"),
+c_cmd("script", script_cmd, "load and process cli command files"),
+c_cmd("echo", echo_cmd, "simple echo a string to the screen"),
+c_cmd("version", version_cmd,"Display version information"),
+
+/* The following are environment variables */
+c_str("SHELL", NULL, "CLI shell"),
+c_str("DPDK_VER", ver_cmd, ""),
+c_end()
+};
+
+int
+cli_default_tree_init(void)
+{
+ int ret = 0;
+
+ /* Add the list of commands/dirs in cli_cmds.c file */
+ if ((ret = cli_add_tree(NULL, cli_default_tree)) == 0)
+ ret = cli_add_bin_path("/sbin");
+
+ if (ret)
+ RTE_LOG(ERR, EAL, "Unable to add commands or directoies\n");
+
+ return ret;
+}
diff --git a/lib/librte_cli/cli_cmds.h b/lib/librte_cli/cli_cmds.h
new file mode 100644
index 0000000..37d0e1d
--- /dev/null
+++ b/lib/librte_cli/cli_cmds.h
@@ -0,0 +1,61 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_CMDS_H_
+#define _CLI_CMDS_H_
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* Add the default set of directories and commands
+*
+* @note Uses a thread variable called this_cli
+*
+* @return
+* 0 is ok, -1 is error
+*/
+int cli_default_tree_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_CMDS_H_ */
diff --git a/lib/librte_cli/cli_common.h b/lib/librte_cli/cli_common.h
new file mode 100644
index 0000000..59c2de5
--- /dev/null
+++ b/lib/librte_cli/cli_common.h
@@ -0,0 +1,90 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_COMMON_H_
+#define _CLI_COMMON_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <cli_scrn.h>
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef RTE_ASSERT
+#define RTE_ASSERT RTE_VERIFY
+#endif
+
+/**
+ * CLI printf like routine to write on the console.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param va_args
+ * va_args for the rest of the printf ouput.
+ * @return
+ * N/A
+ */
+
+static inline void
+__attribute__((format(printf, 1, 2)))
+cli_printf(const char *fmt, ...)
+{
+ va_list vaList;
+
+ va_start(vaList, fmt);
+ vfprintf(this_scrn->fd_out, fmt, vaList);
+ va_end(vaList);
+
+ fflush(this_scrn->fd_out);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_HELP_H_ */
diff --git a/lib/librte_cli/cli_env.c b/lib/librte_cli/cli_env.c
new file mode 100644
index 0000000..5c82f9c
--- /dev/null
+++ b/lib/librte_cli/cli_env.c
@@ -0,0 +1,246 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/* Created by: Keith Wiles @ intel */
+#include <stdlib.h>
+
+#include "cli.h"
+#include "cli_env.h"
+
+static int
+env_free(struct cli_env *env, struct env_node *n)
+{
+ if (!env || !n)
+ return -1;
+
+ TAILQ_REMOVE(&env->head, n, next);
+
+ free((char *)(uintptr_t)n->var);
+ free((char *)(uintptr_t)n->val);
+ free(n);
+ env->count--;
+
+ return 0;
+}
+
+struct cli_env *
+cli_env_create(void)
+{
+ struct cli_env *env;
+
+ env = (struct cli_env *)malloc(sizeof(struct cli_env));
+ if (!env)
+ return NULL;
+ memset(env, '\0', sizeof(struct cli_env));
+
+ TAILQ_INIT(&env->head);
+
+ return env;
+}
+
+void
+cli_env_destroy(struct cli_env *env)
+{
+ struct env_node *n;
+
+ if (!env)
+ return;
+
+ while (!TAILQ_EMPTY(&env->head)) {
+ n = TAILQ_FIRST(&env->head);
+ env_free(env, n);
+ }
+ free(env);
+}
+
+static struct env_node *
+find_env(struct cli_env *env, const char *var)
+{
+ struct env_node *n;
+
+ TAILQ_FOREACH(n, &env->head, next) {
+ if (!strcmp(var, n->var))
+ return n;
+ }
+ return NULL;
+}
+
+static struct env_node *
+__env_set(struct cli_env *env, const char *var, const char *val)
+{
+ struct env_node *n;
+
+ if (!env || !var)
+ return NULL;
+
+ n = find_env(env, var);
+ if (n) {
+ free((char *)(uintptr_t)n->val);
+ n->var = strdup(val);
+ return n;
+ }
+
+ n = (struct env_node *)malloc(sizeof(struct env_node));
+ if (!n)
+ return NULL;
+
+ n->var = strdup(var);
+ n->val = strdup(val);
+
+ TAILQ_INSERT_TAIL(&env->head, n, next);
+ env->count++;
+
+ return n;
+}
+
+int
+cli_env_set(struct cli_env *env, const char *var, const char *val)
+{
+ return (__env_set(env, var, val) == NULL)? -1 : 0;
+}
+
+int
+cli_env_string(struct cli_env *env, const char *var,
+ cli_sfunc_t sfunc, const char *val)
+{
+ struct env_node *n;
+
+ n = __env_set(env, var, val);
+ if (!n)
+ return -1;
+ n->sfunc = sfunc;
+ return 0;
+}
+
+const char *
+cli_env_get(struct cli_env *env, const char *var)
+{
+ struct env_node *n;
+
+ n = find_env(env, var);
+ if (!n)
+ return NULL;
+
+ return (n->sfunc)? n->sfunc(n->val) : n->val;
+}
+
+int
+cli_env_del(struct cli_env *env, const char *var)
+{
+ return env_free(env, find_env(env, var));
+}
+
+/* strings to be substituted are of the form ${foo} or $(foo) */
+void
+cli_env_substitution(struct cli_env *env, char *line, int sz)
+{
+ char *p, *s, *e, *t, *tmp;
+ const char *v;
+
+ if (!env || !line || sz <= 0)
+ return;
+
+ /* Allocate string space on the stack */
+ tmp = alloca(sz + 1);
+ if (!tmp)
+ return;
+
+ memset(tmp, '\0', sz + 1);
+
+ /* Determine the end of the string */
+ e = line + sz;
+
+ for (p = line, t = tmp; (p[0] != '\0') && (p < e); p++) {
+ /* Look for the '$' then the open bracket */
+ if (p[0] != '$')
+ goto next;
+
+ /* find opening bracket */
+ if ((p[1] != '{') && (p[1] != '('))
+ goto next;
+
+ /* find closing bracket */
+ s = strchr(p, (p[1] == '{') ? '}' : ')');
+ if (!s)
+ goto next;
+
+ /* terminate the variable string */
+ *s = '\0';
+
+ v = cli_env_get(env, &p[2]);
+ if (!v)
+ v = "oops!";
+
+ memcpy(t, v, strlen(v));
+ t += strlen(v);
+ p = s; /* Point 'p' past the variable */
+ continue;
+next:
+ *t++ = *p;
+ }
+ *t = '\0';
+
+ snprintf(line, sz, "%s", tmp);
+}
+
+int
+cli_env_get_all(struct cli_env *env, struct env_node **list, int max_size)
+{
+ struct env_node *node;
+ int n = 0;
+
+ if (!env)
+ return 0;
+
+ TAILQ_FOREACH(node, &env->head, next) {
+ list[n++] = node;
+ if (n == max_size)
+ break;
+ }
+
+ return n;
+}
+
+void
+cli_env_show(struct cli_env *env)
+{
+ struct env_node *node;
+
+ TAILQ_FOREACH(node, &env->head, next) {
+ if (node->sfunc)
+ cli_printf(" \"%s\" = \"%s\"\n",
+ node->var, node->sfunc(node->val));
+ else
+ cli_printf(" \"%s\" = \"%s\"\n",
+ node->var, node->val);
+ }
+} \ No newline at end of file
diff --git a/lib/librte_cli/cli_env.h b/lib/librte_cli/cli_env.h
new file mode 100644
index 0000000..5048b38
--- /dev/null
+++ b/lib/librte_cli/cli_env.h
@@ -0,0 +1,189 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/* Create by: Keith Wiles @ intel.com */
+
+#include <sys/queue.h>
+
+#ifndef _CLI_ENV_H_
+#define _CLI_ENV_H_
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+struct cli;
+
+typedef char * (*cli_sfunc_t)(const char *str);
+ /**< CLI function pointer type for a Environment node */
+
+struct env_node {
+ TAILQ_ENTRY(env_node) next;
+ const char *var;
+ const char *val;
+ cli_sfunc_t sfunc;
+};
+
+struct cli_env {
+ TAILQ_HEAD(, env_node) head; /**< link list of vars */
+ int count;
+};
+
+/**
+ * Create a environment for the cli
+ *
+ * @return
+ * NULL on error or cli_env pointer
+ */
+struct cli_env *cli_env_create(void);
+
+/**
+ * Delete the environment for the CLI
+ *
+ * @param cli
+ * The pointer to the enviroment structure
+ */
+void cli_env_destroy(struct cli_env *env);
+
+/**
+ * Set a environment variable for the CLI
+ *
+ * @param env
+ * The cli_env pointer
+ * @param var
+ * Pointer to the variable name const string
+ * @param val
+ * Pointer to the string assigned to the variable
+ * @return
+ * 0 is OK was added or replaced or -1 if not valid
+ */
+int cli_env_set(struct cli_env *env, const char *var, const char *val);
+
+/**
+ * Set a environment variable for the CLI with a function pointer
+ *
+ * @param env
+ * The cli_env pointer
+ * @param var
+ * Pointer to the variable name const string.
+ * @param sfunc
+ * Pointer to function (optional)
+ * @param val
+ * Pointer to the string assigned to the variable
+ * @return
+ * 0 is OK was added or replaced or -1 if not valid
+ */
+int cli_env_string(struct cli_env *env, const char *var,
+ cli_sfunc_t sfunc, const char *val);
+
+/**
+ * Get the environment variable from the cli
+ *
+ * @param env
+ * The cli_env pointer
+ * @param var
+ * The const string variable name
+ * @return
+ * NULL if not found or the const string
+ */
+const char *cli_env_get(struct cli_env *env, const char *var);
+
+/**
+ * Remove the environment variable from the cli
+ *
+ * @param env
+ * The cli_env pointer
+ * @param var
+ * The const string variable name
+ * @return
+ * 0 is OK or -1 if not found.
+ */
+int cli_env_del(struct cli_env *env, const char *var);
+
+/**
+ * Do enviroment variable subsitution on the line.
+ *
+ * @param env
+ * Pointer to the enviroment structure
+ * @param line
+ * Pointer to the line to parse
+ * @param sz
+ * Number of total characters the line can hold.
+ * @return
+ * N/A
+ */
+void cli_env_substitution(struct cli_env *env, char *line, int sz);
+
+/**
+ * Get the number of variables in the environment
+ *
+ * @param env
+ * Pointer to environment structure
+ * @return
+ * Number of environment variables
+ */
+static inline int
+cli_env_count(struct cli_env *env)
+{
+ return env->count;
+}
+
+/**
+ * Get all of the environment variables
+ *
+ * @param env
+ * Pointer to environment list
+ * @param list
+ * Array of env_node pointers to be returned
+ * @param max_size
+ * Max size of the list array
+ */
+int cli_env_get_all(struct cli_env *env, struct env_node **list, int max_size);
+
+/**
+ * Show all enviroment variable
+ *
+ * @param env
+ * Pointer to the cli_env structure.
+ */
+void cli_env_show(struct cli_env *env);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_ENV_H_ */
diff --git a/lib/librte_cli/cli_file.c b/lib/librte_cli/cli_file.c
new file mode 100644
index 0000000..0da43bc
--- /dev/null
+++ b/lib/librte_cli/cli_file.c
@@ -0,0 +1,301 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cli.h"
+#include "cli_file.h"
+
+struct cli_node *
+cli_file_open(const char *path, const char *type)
+{
+ struct cli_node *node;
+ uint32_t flags = 0;
+
+ if (!path)
+ return NULL;
+
+ if (!cli_find_node(path, &node))
+ return NULL;
+
+ if (!is_file(node))
+ return NULL;
+
+ if (type && strlen(type)) {
+ if (strchr(type, 'r'))
+ file_set(flags, CLI_FILE_RD);
+ if (strchr(type, 'w'))
+ file_set(flags, CLI_FILE_WR);
+ if (strchr(type, '+'))
+ file_set(flags, CLI_FILE_APPEND);
+ } else
+ file_set(flags, CLI_FILE_RD);
+
+ file_set(flags, CLI_FILE_OPEN);
+
+ if (node->ffunc(node, NULL, 0, flags))
+ return NULL;
+
+ return node;
+}
+
+int
+cli_file_close(struct cli_node *node)
+{
+ uint32_t flags = CLI_FILE_CLOSE;
+
+ if (!node)
+ return -1;
+ return node->ffunc(node, NULL, 0, flags);
+}
+
+int
+cli_file_read(struct cli_node *node, char *buff, int len)
+{
+ uint32_t flags = CLI_FILE_RD;
+
+ if (!node || !is_file(node))
+ return -1;
+ return node->ffunc(node, buff, len, flags);
+}
+
+int
+cli_file_write(struct cli_node *node, char *buff, int len)
+{
+ uint32_t flags = CLI_FILE_WR;
+
+ if (!node || !is_file(node))
+ return -1;
+ if (is_data_rdonly(node->fflags))
+ return -1;
+ return node->ffunc(node, buff, len, flags);
+}
+
+int
+cli_file_seek(struct cli_node *node, int offset, uint32_t whence)
+{
+ if (!node || !is_file(node))
+ return -1;
+
+ switch (whence) {
+ case CLI_SEEK_SET:
+ case CLI_SEEK_CUR:
+ case CLI_SEEK_END:
+ break;
+ default:
+ return -1;
+ }
+ return node->ffunc(node, NULL, offset, whence);
+}
+
+int
+cli_readline(struct cli_node *node, char *buff, int len)
+{
+ int i, n;
+ char c;
+
+ if (!node || !buff || !is_file(node))
+ return -1;
+ /* Needs to be optimized for performance ??? */
+ for (i = 0, c = '\0'; i < len && c != '\n'; i++) {
+ n = cli_file_read(node, &c, 1);
+ if (n <= 0)
+ break;
+ buff[i] = c;
+ }
+ buff[i] = '\0';
+ return i;
+}
+
+/* Add generic function for handling files */
+int
+cli_file_handler(struct cli_node *node, char *buff, int len, uint32_t opt)
+{
+ char *p;
+
+ if (!node || !is_file(node))
+ return -1;
+
+ if (opt & (CLI_SEEK_SET | CLI_SEEK_CUR | CLI_SEEK_END)) {
+ size_t saved = node->foffset;
+
+ if (is_seek_set(opt)) {
+ if (len < 0)
+ return -1;
+ node->foffset = len;
+ } else if (is_seek_cur(opt)) {
+ if (len < 0) {
+ len = abs(len);
+ if ((size_t)len > node->file_size)
+ node->foffset = 0;
+ else
+ node->foffset -= len;
+ } else
+ node->foffset += len;
+ } else if (is_seek_end(opt)) {
+ if (len < 0) {
+ len = abs(len);
+ if ((size_t)len > node->file_size)
+ node->foffset = 0;
+ else
+ node->foffset = node->file_size - len;
+ } else
+ node->foffset = node->file_size + len;
+ }
+
+ if (node->foffset > node->file_size) {
+ if (!(node->fflags & CLI_FILE_APPEND)) {
+ node->foffset = saved;
+ if (node->fflags & (CLI_FREE_DATA |
+ CLI_DATA_EXPAND)) {
+ char *data;
+ data = realloc(node->file_data,
+ node->foffset);
+ if (!data)
+ return -1;
+ node->file_data = data;
+ node->file_size = node->foffset;
+ } else /* TODO: add code to expand the file */
+ return -1;
+ } else {
+ node->foffset = saved;
+ return -1;
+ }
+ }
+ } else if (is_seek_cur(opt))
+ node->foffset += len;
+ else if (is_seek_end(opt))
+ node->foffset += len;
+ else if (is_file_close(opt)) {
+ if (node->file_data && (node->fflags & CLI_FREE_DATA)) {
+ free(node->file_data);
+ node->file_data = NULL;
+ node->file_size = 0;
+ }
+ node->foffset = 0;
+ } else if (is_file_open(opt)) {
+ if (is_file_append(opt)) {
+ node->fflags |= CLI_FILE_APPEND;
+ node->foffset = node->file_size;
+ }
+ if (is_file_wr(opt))
+ node->fflags |= CLI_FILE_WR;
+ } else if (is_file_rd(opt)) {
+ if (len <= 0)
+ return 0;
+
+ len = RTE_MIN(len, (int)(node->file_size - node->foffset));
+
+ p = node->file_data + node->foffset;
+
+ memcpy(buff, p, len);
+
+ node->foffset += len;
+ } else if (is_file_wr(opt)) {
+ if (!is_data_rdonly(node->fflags))
+ return -1;
+ if (len <= 0)
+ return 0;
+ if ((node->foffset + len) < node->file_size) {
+ p = node->file_data + node->foffset;
+ memcpy(p, buff, len);
+ node->foffset += len;
+ } else {
+ p = realloc(node->file_data, (node->foffset + len));
+ node->file_data = p;
+ node->file_size = node->foffset + len;
+ node->foffset += len;
+ }
+ }
+
+ return len;
+}
+
+struct cli_node *
+cli_file_create(const char *path, const char *type)
+{
+ struct cli_node *node, *parent;
+ char *file, *mypath;
+ char *data = NULL;
+
+ node = cli_file_open(path, type);
+ if (node)
+ return node;
+
+ mypath = alloca(strlen(path) + 1);
+
+ strcpy(mypath, path);
+
+ file = basename(mypath);
+
+ data = malloc(CLI_FILE_SIZE);
+ if (data) {
+ parent = cli_last_node_in_path(path);
+ if (parent) {
+ node = cli_add_file(file, parent, cli_file_handler, "");
+ if (node) {
+ node->file_data = data;
+ node->file_size = CLI_FILE_SIZE;
+ node->fflags = CLI_FREE_DATA;
+ if (strchr(type, 'r') && !strchr(type, 'w'))
+ node->fflags |= CLI_DATA_RDONLY;
+ node->foffset = 0;
+ node->fflags = 0;
+ node->fstate = 0;
+ return node;
+ }
+ }
+ }
+
+ free(data);
+ return NULL;
+}
+
+int
+cli_system(char *p)
+{
+ char buf[256];
+ size_t n, tot = 0;
+ FILE *f;
+
+ f = popen(p, "r");
+ if (!f)
+ return -1;
+
+ while ((n = fread(buf, 1, sizeof(buf), f)) > 0) {
+ cli_write(buf, n);
+ tot += n;
+ }
+
+ pclose(f);
+
+ return n;
+}
diff --git a/lib/librte_cli/cli_file.h b/lib/librte_cli/cli_file.h
new file mode 100644
index 0000000..7417985
--- /dev/null
+++ b/lib/librte_cli/cli_file.h
@@ -0,0 +1,273 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_FILE_H_
+#define _CLI_FILE_H_
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+
+#include "cli.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CLI_FILE_SIZE 1024
+
+enum {
+ /* File operations opt */
+ CLI_FILE_RD = 0x0001, /** Do a read on a file */
+ CLI_FILE_WR = 0x0002, /** Do a write on a file */
+ CLI_FILE_APPEND = 0x0004, /** Append to a file */
+ CLI_FILE_OPEN = 0x0008, /** Open a file */
+ CLI_FILE_CLOSE = 0x0010, /** Close a file */
+ CLI_FILE_CREATE = 0x0020, /** Create a file */
+
+ /* File seek operations */
+ CLI_SEEK_SET = 0x0100, /** Set file pointer to a given offset */
+ CLI_SEEK_CUR = 0x0200, /** Seek from current file pointer */
+ CLI_SEEK_END = 0x0400, /** Seek from end of file */
+
+ /* File information in cli_node.fflags */
+ CLI_DATA_RDONLY = 0x1000, /** file is read only */
+ CLI_FREE_DATA = 0x2000, /** File data needs to be freed */
+ CLI_DATA_EXPAND = 0x4000 /** File is expandable */
+};
+
+#define file_set(f, v) do { (f) |= (v); } while((0))
+#define file_clr(f, v) do { (f) &= ~(v); } while((0))
+
+static inline int
+is_file_set(uint32_t opt, uint32_t cmpflags)
+{
+ return opt & cmpflags;
+}
+
+static inline int
+is_file_rd(uint32_t opt)
+{
+ return is_file_set(opt, CLI_FILE_RD);
+}
+
+static inline int
+is_file_wr(uint32_t opt)
+{
+ return is_file_set(opt, CLI_FILE_WR);
+}
+
+static inline int
+is_file_append(uint32_t opt)
+{
+ return is_file_set(opt, CLI_FILE_APPEND);
+}
+
+static inline int
+is_file_open(uint32_t opt)
+{
+ return is_file_set(opt, CLI_FILE_OPEN);
+}
+
+static inline int
+is_file_close(uint32_t opt)
+{
+ return is_file_set(opt, CLI_FILE_CLOSE);
+}
+
+static inline int
+is_file_create(uint32_t opt)
+{
+ return is_file_set(opt, CLI_FILE_CREATE);
+}
+
+static inline int
+is_data_rdonly(uint32_t flags)
+{
+ return is_file_set(flags, CLI_DATA_RDONLY);
+}
+
+static inline int
+is_file_eq(uint32_t opt, uint32_t cmpflags)
+{
+ return ((opt & cmpflags) == cmpflags);
+}
+
+static inline int
+is_seek_set(uint32_t opt)
+{
+ return is_file_set(opt, CLI_SEEK_SET);
+}
+
+static inline int
+is_seek_cur(uint32_t opt)
+{
+ return is_file_set(opt, CLI_SEEK_CUR);
+}
+
+static inline int
+is_seek_end(uint32_t opt)
+{
+ return is_file_set(opt, CLI_SEEK_END);
+}
+
+/**
+ * Open a file.
+ *
+ * @param path
+ * Path string for file
+ * @param type
+ * Type of open string r, w, and/or + characters
+ * @return
+ * Node pointer or NULL on error
+ */
+struct cli_node *cli_file_open(const char *path, const char *type);
+
+/**
+ * Close a file
+ *
+ * @param node
+ * Pointer to file node
+ * @return
+ * 0 on OK and -1 on error
+ */
+int cli_file_close(struct cli_node *node);
+
+/**
+ * read data from a file
+ *
+ * @param node
+ * Pointer to file node
+ * @param buff
+ * Pointer to place to put the data
+ * @param len
+ * Max Number of bytes to read
+ * @return
+ * Number of bytes read and -1 on error
+ */
+int cli_file_read(struct cli_node *node, char * buff, int len);
+
+/**
+ * write data to a file
+ *
+ * @param node
+ * Pointer to file node
+ * @param buff
+ * Pointer to place to get the data
+ * @param len
+ * Max Number of bytes to write
+ * @return
+ * Number of bytes written and -1 on error
+ */
+int cli_file_write(struct cli_node *node, char * buff, int len);
+
+/**
+ * write data to a file
+ *
+ * @param node
+ * Pointer to file node
+ * @param offset
+ * Offset to move in file
+ * @param whence
+ * Type of seek operation CLI_SEEK_SET, CLI_SEEK_CUR and CLI_SEEK_END
+ * @return
+ * Offset in file after seek and -1 on error
+ */
+int cli_file_seek(struct cli_node *node, int offset, uint32_t whence);
+
+/**
+ * write data to a file
+ *
+ * @param node
+ * Pointer to file node
+ * @param buff
+ * place to put the line data.
+ * @param len
+ * Max buff size
+ * @return
+ * Number of bytes read not including the newline
+ */
+int cli_readline(struct cli_node *node, char *buff, int len);
+
+/**
+ * create a data file in memory will be lost at reset.
+ *
+ * @param path
+ * Path string for file
+ * @param type
+ * Type of open string r, w, and/or + characters
+ * @return
+ * Node pointer or NULL on error
+ */
+struct cli_node *cli_file_create(const char *path, const char *type);
+
+/**
+ * Generic file function for basic file handling
+ *
+ * @param node
+ * Pointer to file node
+ * @param buff
+ * place to put the line data.
+ * @param len
+ * Max buff size
+ * @param opt
+ * Flags for file handling
+ * @return
+ * Number of bytes read not including the newline
+ */
+int cli_file_handler(struct cli_node *node,
+ char *buff, int len, uint32_t opt);
+
+/**
+ * Generic file function for basic file handling
+ *
+ * @param node
+ * Pointer to file node
+ * @param buff
+ * place to put the line data.
+ * @param len
+ * Max buff size
+ * @param opt
+ * Flags for file handling
+ * @return
+ * Number of bytes read not including the newline
+ */
+int cli_system(char *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_FILE_H_ */
diff --git a/lib/librte_cli/cli_gapbuf.c b/lib/librte_cli/cli_gapbuf.c
new file mode 100644
index 0000000..117ffc6
--- /dev/null
+++ b/lib/librte_cli/cli_gapbuf.c
@@ -0,0 +1,181 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/* inspired by an email/code written by: Joseph H. Allen, 9/10/89 */
+
+#include <rte_memcpy.h>
+
+#include "cli.h"
+
+/* Copy the gap buffer data into a user supplied buffer */
+uint32_t
+gb_copy_to_buf(struct gapbuf *gb, char *dst, uint32_t size)
+{
+ uint32_t cnt, tcnt = 0;
+
+ RTE_ASSERT(gb != NULL);
+ RTE_ASSERT(dst != NULL);
+
+ /* Only copy the request size or data size */
+ size = RTE_MIN(size, gb_data_size(gb));
+
+ if (size) {
+ /* Move data before the gap */
+ cnt = gb->gap - gb->buf;/* Could be zero */
+ rte_memcpy(dst, gb->buf, cnt);
+ dst += cnt;
+ tcnt += cnt;
+
+ /* Move data after the gap */
+ cnt = gb->ebuf - gb->egap; /* Could be zero */
+ rte_memcpy(dst, gb->egap, cnt);
+ dst += cnt;
+ tcnt += cnt;
+ }
+
+ /* Force a NULL terminated string */
+ *dst = '\0';
+
+ return tcnt;
+}
+
+int
+gb_reset_buf(struct gapbuf *gb)
+{
+ int size;
+
+ RTE_ASSERT(gb != NULL);
+
+ size = gb_buf_size(gb);
+ memset(gb->buf, ' ', size);
+
+ gb->point = gb->buf;
+ gb->gap = gb->buf;
+ gb->egap = gb->buf + size;
+ gb->ebuf = gb->egap;
+
+ return 0;
+}
+
+/* release the buffer and allocate a new buffer with correct offsets */
+int
+gb_init_buf(struct gapbuf *gb, int size)
+{
+ RTE_ASSERT(gb != NULL);
+
+ free(gb->buf);
+
+ gb->buf = malloc(size);
+ if (!gb->buf)
+ return -1;
+
+ gb->ebuf = gb->buf + size;
+
+ return gb_reset_buf(gb);
+}
+
+/* Create the gap buffer structure and init the pointers */
+struct gapbuf *
+gb_create(void)
+{
+ struct gapbuf *gb;
+
+ gb = malloc(sizeof(struct gapbuf));
+ if (!gb)
+ return NULL;
+
+ memset(gb, '\0', sizeof(struct gapbuf));
+
+ gb_init_buf(gb, GB_DEFAULT_GAP_SIZE);
+
+ return gb;
+}
+
+/* Release the gap buffer data and memory */
+void
+gb_destroy(struct gapbuf *gb)
+{
+ if (gb) {
+ free(gb->buf);
+ free(gb);
+ }
+}
+
+/* Dump out the gap buffer and pointers in a readable format */
+void
+gb_dump(struct gapbuf *gb, const char *msg)
+{
+#ifdef CLI_DEBUG_ENABLED
+ char *p;
+ uint32_t i;
+
+ if (msg)
+ fprintf(stderr, "\n%s Gap: buf_size %u, gap_size %u\n",
+ msg, gb_buf_size(gb), gb_gap_size(gb));
+ else
+ fprintf(stderr, "\nGap: buf_size %u, gap_size %u\n",
+ gb_buf_size(gb), gb_gap_size(gb));
+
+ fprintf(stderr, " buf %p, ", gb->buf);
+ fprintf(stderr, "gap %p, ", gb->gap);
+ fprintf(stderr, "point %p, ", gb->point);
+ fprintf(stderr, "egap %p, ", gb->egap);
+ fprintf(stderr, "ebuf %p\n", gb->ebuf);
+
+ fprintf(stderr, " ");
+ for (i = 0, p = gb->buf; p < gb->ebuf; i++, p++)
+ fprintf(stderr, "%c", "0123456789"[i % 10]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<");
+ for (p = gb->buf; p < gb->ebuf; p++)
+ fprintf(stderr, "%c", ((*p >= ' ') && (*p <= '~')) ? *p : '.');
+ fprintf(stderr, ">\n ");
+ for (p = gb->buf; p <= gb->ebuf; p++) {
+ if ((p == gb->gap) && (p == gb->egap))
+ fprintf(stderr, "*");
+ else if (p == gb->gap)
+ fprintf(stderr, "[");
+ else if (p == gb->egap)
+ fprintf(stderr, "]");
+ else
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "\n ");
+ for (p = gb->buf; p <= gb->ebuf; p++)
+ fprintf(stderr, "%c", (p == gb->point) ? '^' : ' ');
+ fprintf(stderr, "\n");
+ cli_display_line();
+#else
+ (void)gb;
+ (void)msg;
+#endif
+}
diff --git a/lib/librte_cli/cli_gapbuf.h b/lib/librte_cli/cli_gapbuf.h
new file mode 100644
index 0000000..7695342
--- /dev/null
+++ b/lib/librte_cli/cli_gapbuf.h
@@ -0,0 +1,715 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/* inspired by an email/code written by: Joseph H. Allen, 9/10/89 */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_debug.h>
+
+#ifndef _CLI_GAPBUF_H_
+#define _CLI_GAPBUF_H_
+
+/**
+ * @file
+ * CLI Gap Buffer support
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GB_DEFAULT_GAP_SIZE 8
+
+struct gapbuf {
+ char *buf; /** Pointer to start of buffer */
+ char *ebuf; /** Pointer to end of buffer */
+ char *point; /** Pointer to point in the buffer */
+ char *gap; /** Pointer to the start of the gap */
+ char *egap; /** Pointer to the end of the gap */
+};
+
+/**
+ * Create the Gap Buffer structure
+ *
+ * @return
+ * NULL on error or pointer to struct gapbuf
+ */
+struct gapbuf *gb_create(void);
+
+/**
+ * Destroy
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * N/A
+ */
+void gb_destroy(struct gapbuf *gb);
+
+/**
+ * Allocate buffer and initialize, if buffer exist free and reallocate.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param size
+ * Init the gap buffer to the size given
+ * @return
+ * 0 is OK or Error
+ */
+int gb_init_buf(struct gapbuf *gb, int size);
+
+/**
+ * Reset the gap buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * 0 is OK or Error
+ */
+int gb_reset_buf(struct gapbuf *gb);
+
+/**
+ * Copy the buffer data into a given buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param dst
+ * Location to copy the data into
+ * @param size
+ * Total number of bytes to copy
+ * @return
+ * Number of bytes copied into the buffer
+ */
+uint32_t gb_copy_to_buf(struct gapbuf *gb, char *dst, uint32_t size);
+
+/**
+ * Print out a debug list of the Gap buffer and pointers
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param msg
+ * Message to print out befor dump of buffer data
+ * @return
+ * N/A
+ */
+void gb_dump(struct gapbuf *gb, const char * msg);
+
+/********************************************************/
+
+/**
+ * Return the number of bytes total in the buffer includes gap size
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * The number of bytes in the buffer
+ */
+static inline uint32_t
+gb_buf_size(struct gapbuf *gb)
+{
+ return gb->ebuf - gb->buf;
+}
+
+/**
+ * Return the gap size in bytes
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Number of bytes in the gap.
+ */
+static inline uint32_t
+gb_gap_size(struct gapbuf *gb)
+{
+ return gb->egap - gb->gap;
+}
+
+/**
+ * Number of data bytes
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Number of data bytes
+ */
+static inline uint32_t
+gb_data_size(struct gapbuf *gb)
+{
+ return (gb->ebuf - gb->buf) - (gb->egap - gb->gap);
+}
+
+/**
+ * Return the start of the buffer address
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * The pointer to the start of the buffer
+ */
+static inline char *
+gb_start_of_buf(struct gapbuf *gb)
+{
+ return gb->buf;
+}
+
+/**
+ * Return the pointer to the gap start
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Pointer to the gap start location
+ */
+static inline char *
+gb_start_of_gap(struct gapbuf *gb)
+{
+ return gb->gap;
+}
+
+/**
+ * Return the pointer to the end of the gap
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * The end of the gap pointer
+ */
+static inline char *
+gb_end_of_gap(struct gapbuf *gb)
+{
+ return gb->egap;
+}
+
+/**
+ * Return the pointer to the end of the buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * End of buffer pointer
+ */
+static inline char *
+gb_end_of_buf(struct gapbuf *gb)
+{
+ return gb->ebuf;
+}
+
+/**
+ * Return the point location
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Pointer to point
+ */
+static inline char *
+gb_point_at(struct gapbuf *gb)
+{
+ return gb->point;
+}
+
+/**
+ * Is point at start of buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * true if point is at start of buffer
+ */
+static inline int
+gb_point_at_start(struct gapbuf *gb)
+{
+ return (gb->point == gb->buf);
+}
+
+/**
+ * is point at the end of buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * true if the point is at the end of buffer
+ */
+static inline int
+gb_point_at_end(struct gapbuf *gb)
+{
+ return (gb->ebuf == gb->point);
+}
+
+/**
+ * is point at start of gap
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * true if the point is at the gap start
+ */
+static inline int
+gb_point_at_gap(struct gapbuf *gb)
+{
+ return (gb->gap == gb->point);
+}
+
+/**
+ * Set point to a givewn index into the buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param idx
+ * Index into the buffer to put point
+ * @return
+ * N/A
+ */
+static inline void
+gb_set_point(struct gapbuf *gb, int idx)
+{
+ if (idx == -1) {
+ gb->point = gb->ebuf;
+ return;
+ }
+ gb->point = gb->buf + idx;
+ if (gb->point > gb->gap)
+ gb->point += gb->egap - gb->gap;
+}
+
+/**
+ * Get offset of point
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Offset of point from start of buffer
+ */
+static inline int
+gb_point_offset(struct gapbuf *gb)
+{
+ if (gb->point > gb->egap)
+ return (gb->point - gb->buf) - (gb->egap - gb->gap);
+ else
+ return gb->point - gb->buf;
+}
+
+/**
+ * Return true if point is at end of buffer data.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * True if end of buffer data
+ */
+static inline int
+gb_eof(struct gapbuf *gb)
+{
+ return (gb->point == gb->gap)?
+ (gb->egap == gb->ebuf) : (gb->point == gb->ebuf);
+}
+
+/********************************************************/
+
+/**
+ * Move the gap to the location of the point.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * N/A
+ */
+static inline void
+gb_move_gap_to_point(struct gapbuf *gb)
+{
+ if (gb->point == gb->gap)
+ return;
+
+ if (gb->point == gb->egap)
+ gb->point = gb->gap;
+ else {
+ int cnt;
+
+ if (gb->point < gb->gap) {
+ cnt = gb->gap - gb->point;
+ memmove(gb->egap - cnt, gb->point, cnt);
+ gb->egap -= cnt;
+ gb->gap = gb->point;
+ } else if (gb->point > gb->egap) {
+ cnt = gb->point - gb->egap;
+ memmove(gb->gap, gb->egap, cnt);
+ gb->gap += cnt;
+ gb->egap = gb->point;
+ gb->point = gb->gap;
+ } else { /* This case when point is between gap and egap. */
+ cnt = gb->point - gb->gap;
+ memmove(gb->gap, gb->egap, cnt);
+ gb->egap += cnt;
+ gb->gap += cnt;
+ gb->point = gb->gap;
+ }
+ }
+}
+
+/**
+ * Expand the buffer by the given bytes.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param more
+ * The number of bytes to increase the buffer
+ * @return
+ * N/A
+ */
+static inline void
+gb_expand_buf(struct gapbuf *gb, uint32_t more)
+{
+ if (((gb->ebuf - gb->buf) + more) > gb_buf_size(gb)) {
+ char *old = gb->buf;
+
+ more = (gb->ebuf - gb->buf) + more + GB_DEFAULT_GAP_SIZE;
+
+ gb->buf = (char *)realloc(gb->buf, more);
+ if (gb->buf == NULL)
+ rte_panic("realloc(%d) in %s failed\n", more, __func__);
+
+ gb->point += (gb->buf - old);
+ gb->ebuf += (gb->buf - old);
+ gb->gap += (gb->buf - old);
+ gb->egap += (gb->buf - old);
+ }
+}
+
+/**
+ * Expand the Gap by the size given.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param size
+ * Increase the gap by the number of bytes given
+ * @return
+ * N/A
+ */
+static inline void
+gb_expand_gap(struct gapbuf *gb, uint32_t size)
+{
+ if (size > gb_gap_size(gb)) {
+ size += GB_DEFAULT_GAP_SIZE;
+
+ gb_expand_buf(gb, size);
+
+ memmove(gb->egap + size, gb->egap, gb->ebuf - gb->egap);
+
+ gb->egap += size;
+ gb->ebuf += size;
+ }
+}
+
+/**
+ * Get the byte at the point location.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Byte at point
+ */
+static inline char
+gb_get(struct gapbuf *gb)
+{
+ if (gb->point == gb->gap)
+ gb->point = gb->egap;
+
+ return *gb->point;
+}
+
+/**
+ * Get the byte at the point - 1 location.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Byte at point
+ */
+static inline char
+gb_get_prev(struct gapbuf *gb)
+{
+ if (gb->point == gb->egap)
+ gb->point = gb->gap;
+
+ if (gb->point == gb->buf) {
+ if (gb->point == gb->gap)
+ return '\0';
+ else
+ return *gb->point;
+ }
+
+ return *(gb->point - 1);
+}
+
+/**
+ * Get the byte at the point + 1 location.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Byte at point
+ */
+static inline char
+gb_get_next(struct gapbuf *gb)
+{
+ if (gb->point == gb->gap)
+ gb->point = gb->egap;
+
+ if (gb->point == gb->ebuf)
+ return *gb->point;
+
+ return *(gb->point + 1);
+}
+
+/**
+ * Put character at point
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param c
+ * The character to put at point
+ * @return
+ * N/A
+ */
+static inline void
+gb_put(struct gapbuf *gb, char c)
+{
+ if (gb->point == gb->gap)
+ gb->point = gb->egap;
+
+ if (gb->point == gb->ebuf) {
+ gb_expand_buf(gb, 1);
+ gb->ebuf++;
+ }
+
+ *gb->point = c;
+}
+
+/**
+ * Get the byte at the point location and advance point
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Byte at point
+ */
+static inline char
+gb_getc(struct gapbuf *gb)
+{
+ if (gb->point == gb->gap) {
+ gb->point = gb->egap + 1;
+ return *gb->egap;
+ }
+
+ return *(gb->point++);
+}
+
+/**
+ * Move point left and return character at point.
+ *
+ * fmrgetc() (point == ehole ? *(point = hole - 1) : *(--point))
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Character at point
+ */
+static inline char
+gb_getc_prev(struct gapbuf *gb)
+{
+ if (gb->point == gb->egap)
+ gb->point = gb->gap;
+
+ return *(--gb->point);
+}
+
+/**
+ * Put character at point and advance point
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param c
+ * The character to put at point
+ * @return
+ * N/A
+ */
+static inline void
+gb_putc(struct gapbuf *gb, char c)
+{
+ gb_move_gap_to_point(gb);
+
+ if (gb->point == gb->ebuf) {
+ gb_expand_buf(gb, 1);
+ gb->ebuf++;
+ }
+ *(gb->gap++) = c;
+ gb->point++;
+}
+
+/**
+ * Insert the character at point and move point.
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param c
+ * The character to add to buffer
+ * @return
+ * N/A
+ */
+static inline void
+gb_insert(struct gapbuf *gb, char c)
+{
+ if (gb->point != gb->gap)
+ gb_move_gap_to_point(gb);
+
+ if (gb->gap == gb->egap)
+ gb_expand_gap(gb, 1);
+
+ gb_putc(gb, c);
+}
+
+/**
+ * Delete the character(s) at point
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param cnt
+ * Number of characters to delete at point.
+ * @return
+ * N/A
+ */
+static inline void
+gb_del(struct gapbuf *gb, int cnt)
+{
+ if (gb->point != gb->gap)
+ gb_move_gap_to_point(gb);
+
+ gb->egap += cnt;
+}
+
+/**
+ * Insert a string at point and move point
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @param str
+ * String to put at point
+ * @param
+ * Size of the string to insert at point
+ * @return
+ * N/AS
+ */
+static inline void
+gb_str_insert(struct gapbuf *gb, char *str, uint32_t size)
+{
+ gb_move_gap_to_point(gb);
+
+ if (size > gb_gap_size(gb))
+ gb_expand_gap(gb, size);
+
+ do {
+ gb_putc(gb, *str++);
+ } while(--size);
+}
+
+/********************************************************/
+
+/**
+ * Left size of the data in the gap buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Number of bytes in the left size of gap buffer
+ */
+static inline uint32_t
+gb_left_data_size(struct gapbuf *gb)
+{
+ return gb->gap - gb->buf;
+}
+
+/**
+ * Right size of the data in the gap buffer
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * Number of bytes in the right size of gap buffer
+ */
+static inline uint32_t
+gb_right_data_size(struct gapbuf *gb)
+{
+ if (gb_eof(gb))
+ return 0;
+ return gb->ebuf - gb->egap;
+}
+
+/**
+ * Move point right one byte
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * N/A
+ */
+static inline void
+gb_move_right(struct gapbuf *gb)
+{
+ if (gb->point == gb->gap)
+ gb->point = gb->egap;
+ gb->point = ((gb->point + 1) > gb->ebuf)? gb->ebuf : (gb->point + 1);
+}
+
+/**
+ * Move point left one byte
+ *
+ * @param gb
+ * The gapbuf structure pointer.
+ * @return
+ * N/A
+ */
+static inline void
+gb_move_left(struct gapbuf *gb)
+{
+ if (gb->point == gb->egap)
+ gb->point = gb->gap;
+ gb->point = ((gb->point - 1) < gb->buf)? gb->buf : (gb->point - 1);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_GAPBUF_H_ */
diff --git a/lib/librte_cli/cli_help.c b/lib/librte_cli/cli_help.c
new file mode 100644
index 0000000..bb7addb
--- /dev/null
+++ b/lib/librte_cli/cli_help.c
@@ -0,0 +1,139 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cli.h"
+
+int
+cli_help_add(const char *group, struct cli_map *map, const char **help_data)
+{
+ struct help_node *node;
+
+ if (!group)
+ return -1;
+
+ node = malloc(sizeof(struct help_node));
+ if (!node)
+ return -1;
+
+ memset(node, 0, sizeof(struct help_node));
+
+ node->map = map;
+ node->help_data = help_data;
+ snprintf(node->group, sizeof(node->group), "%s", group);
+
+ TAILQ_INSERT_TAIL(&this_cli->help_nodes, node, next);
+
+ return 0;
+}
+
+static int
+_show_help_lines(const char **h, int allow_pause)
+{
+ int j;
+ char key;
+
+ for (j = 0; h[j] != NULL; j++) {
+ if (strcmp(h[j], CLI_HELP_PAUSE)) {
+ cli_printf("%s\n", h[j]);
+ continue;
+ }
+ if (allow_pause) {
+ key = cli_pause("\n Return to Continue or ESC:", NULL);
+ if ((key == vt100_escape) ||
+ (key == 'q') || (key == 'Q'))
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+_cli_help_title(const char *msg)
+{
+ scrn_pos(1,1);
+ scrn_cls();
+
+ if (msg)
+ scrn_cprintf(1, -1, "%s\n", msg);
+}
+
+int
+cli_help_show_all(const char *msg)
+{
+ struct help_node *n;
+
+ _cli_help_title(msg);
+
+ TAILQ_FOREACH(n, &this_cli->help_nodes, next) {
+ if (_show_help_lines(n->help_data, 1))
+ return -1;
+ _cli_help_title(msg);
+ }
+
+ return 0;
+}
+
+void
+cli_help_foreach(void (*func)(void *arg, const char **h), void *arg)
+{
+ struct help_node *n;
+
+ TAILQ_FOREACH(n, &this_cli->help_nodes, next) {
+ func(arg, n->help_data);
+ }
+}
+
+struct help_node *
+cli_help_find_group(const char *group)
+{
+ struct help_node *n;
+
+ TAILQ_FOREACH(n, &this_cli->help_nodes, next) {
+ if (!strcmp(group, n->group))
+ return n;
+ }
+ return NULL;
+}
+
+int
+cli_help_show_group(const char *group)
+{
+ struct help_node *n;
+
+ n = cli_help_find_group(group);
+ if (!n)
+ return -1;
+
+ return _show_help_lines(n->help_data, 0);
+}
diff --git a/lib/librte_cli/cli_help.h b/lib/librte_cli/cli_help.h
new file mode 100644
index 0000000..a120f7b
--- /dev/null
+++ b/lib/librte_cli/cli_help.h
@@ -0,0 +1,150 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_HELP_H_
+#define _CLI_HELP_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/queue.h>
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CLI_HELP_PAUSE "<<PauseOutput>>"
+#define CLI_HELP_NAME_LEN 32
+
+struct help_node {
+ TAILQ_ENTRY(help_node) next; /**< link list of help nodes */
+ char group[CLI_HELP_NAME_LEN];
+ struct cli_map *map;
+ const char **help_data;
+};
+
+/**
+ * Find the help group section defined by the group string.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param group
+ * The group string name to find.
+ * @return
+ * NULL if not found or pointer to struct cli_info.
+ */
+struct help_node *
+cli_help_find_group(const char *group);
+
+/**
+ * Show the map table entries
+ *
+ * @param msg
+ * Pointer to a message to print first.
+ * @return
+ * 0 on success or -1 on error
+ */
+int cli_help_show_all(const char *msg);
+
+/**
+ * Show the help message for the user.
+ *
+ * @note Uses thread variable this_cli.
+ *
+ * @param data
+ * Pointer to the cli_info structure.
+ */
+int cli_help_show_group( const char *group);
+
+/**
+ * Add help string group to a help structure
+ *
+ * @param
+ * The help pointer structure
+ * @param group
+ * The group name for this help list
+ * @param map
+ * The pointer to the MAP structure if present.
+ * @param help_data
+ * The array of string pointers for the help group
+ * @returns
+ * 0 on OK and -1 on error
+ */
+int cli_help_add(const char *group, struct cli_map *map, const char **hd);
+
+/**
+ * Find if the last item is a help request.
+ *
+ * @param argc
+ * Number of args in the argv list.
+ * @param argv
+ * List of strings to parser
+ * @return
+ * 1 if true or 0 if false
+ */
+static inline int
+is_help(int argc, char **argv)
+{
+ if (argc == 0)
+ return 0;
+
+ return !strcmp("-?", argv[argc - 1]) || !strcmp("?", argv[argc - 1]);
+}
+
+/**
+ * Iterate over the help messages calling a given function.
+ *
+ * @param func
+ * A function to call for all help lines.
+ * @param arg
+ * Argument pointer for function call.
+ * @return
+ * N/A
+ */
+void cli_help_foreach(void (*func)(void *arg, const char **h), void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_HELP_H_ */
diff --git a/lib/librte_cli/cli_history.c b/lib/librte_cli/cli_history.c
new file mode 100644
index 0000000..16aa053
--- /dev/null
+++ b/lib/librte_cli/cli_history.c
@@ -0,0 +1,261 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cli.h"
+
+struct cli_hist *
+cli_hist_alloc(void)
+{
+ struct cli *cli = this_cli;
+ struct cli_hist *hist = NULL;
+
+ if (!cli)
+ return hist;
+
+ if (!CIRCLEQ_EMPTY(&cli->free_hist)) {
+ hist = (struct cli_hist *)CIRCLEQ_FIRST(&cli->free_hist);
+ if (hist)
+ CIRCLEQ_REMOVE(&cli->free_hist, hist, next);
+ }
+ return hist;
+}
+
+void
+cli_hist_free(struct cli_hist *hist)
+{
+ struct cli *cli = this_cli;
+
+ if (!cli || !hist)
+ return;
+
+ free(hist->line);
+
+ hist->line = NULL;
+
+ CIRCLEQ_INSERT_TAIL(&cli->free_hist, hist, next);
+}
+
+void
+cli_history_add(char *line)
+{
+ struct cli *cli = this_cli;
+ struct cli_hist *h;
+
+ if (!cli || !cli->hist_mem || !line)
+ return;
+
+ /* Do not allow duplicate lines compared to the last line */
+ if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {
+ h = CIRCLEQ_LAST(&cli->hd_hist);
+ if (strcmp(h->line, line) == 0)
+ return;
+ }
+
+ h = cli_hist_alloc();
+ if (!h) {
+ h = CIRCLEQ_FIRST(&cli->hd_hist);
+
+ CIRCLEQ_REMOVE(&cli->hd_hist, h, next);
+ }
+
+ free(h->line);
+ h->line = strdup(line);
+ CIRCLEQ_INSERT_TAIL(&cli->hd_hist, h, next);
+}
+
+void
+cli_history_del(void)
+{
+ struct cli *cli = this_cli;
+ struct cli_hist *h;
+
+ if (!cli || !cli->hist_mem)
+ return;
+
+ if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {
+ h = CIRCLEQ_LAST(&cli->hd_hist);
+ CIRCLEQ_REMOVE(&cli->hd_hist, h, next);
+ }
+}
+
+char *
+cli_history_line(int lineno)
+{
+ struct cli *cli = this_cli;
+ struct cli_hist *h;
+ int i = 0;
+
+ if (!cli || !cli->hist_mem)
+ return NULL;
+
+ if (!CIRCLEQ_EMPTY(&cli->hd_hist))
+ CIRCLEQ_FOREACH(h, &cli->hd_hist, next) {
+ if (i++ == lineno)
+ return h->line;
+ }
+ return NULL;
+}
+
+char *
+cli_history_prev(void)
+{
+ struct cli *cli = this_cli;
+
+ if (!cli || !cli->hist_mem)
+ return NULL;
+
+ if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {
+ struct cli_hist *hist;
+
+ if (!cli->curr_hist)
+ cli->curr_hist = CIRCLEQ_FIRST(&cli->hd_hist);
+
+ hist = CIRCLEQ_LOOP_PREV(&cli->hd_hist, cli->curr_hist, next);
+ cli->curr_hist = hist;
+
+ return hist->line;
+ }
+ return NULL;
+}
+
+char *
+cli_history_next(void)
+{
+ struct cli *cli = this_cli;
+
+ if (!cli || !cli->hist_mem)
+ return NULL;
+
+ if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {
+ struct cli_hist *hist;
+
+ if (!cli->curr_hist)
+ cli->curr_hist = CIRCLEQ_LAST(&cli->hd_hist);
+
+ hist = CIRCLEQ_LOOP_NEXT(&cli->hd_hist, cli->curr_hist, next);
+ cli->curr_hist = hist;
+
+ return hist->line;
+ }
+ return NULL;
+}
+
+void
+cli_history_clear(void)
+{
+ struct cli *cli = this_cli;
+ struct cli_hist *h;
+
+ if (!cli || !cli->hist_mem)
+ return;
+
+ while (!CIRCLEQ_EMPTY(&cli->hd_hist)) {
+ h = CIRCLEQ_FIRST(&cli->hd_hist);
+ CIRCLEQ_REMOVE(&cli->hd_hist, h, next);
+ cli_hist_free(h);
+ }
+}
+
+void
+cli_history_delete(void)
+{
+ struct cli *cli = this_cli;
+
+ if (cli) {
+ cli_history_clear();
+
+ CIRCLEQ_INIT(&cli->hd_hist);
+
+ cli->hist_mem = NULL;
+ cli->nb_hist = 0;
+ }
+}
+
+int
+cli_set_history(uint32_t nb_hist)
+{
+ struct cli *cli = this_cli;
+ size_t size;
+
+ if (!cli)
+ return -1;
+
+ size = nb_hist * sizeof(struct cli_hist);
+
+ if (nb_hist == 0) {
+ cli_history_delete();
+ return 0;
+ }
+
+ if (nb_hist != cli->nb_hist)
+ cli_history_delete();
+
+ cli->nb_hist = nb_hist;
+
+ cli->hist_mem = malloc(size);
+ if (cli->hist_mem) {
+ uint32_t i;
+ struct cli_hist *hist = cli->hist_mem;
+
+ memset(cli->hist_mem, '\0', size);
+
+ /* Setup the history support is number of lines given */
+ for (i = 0; i < nb_hist; i++, hist++)
+ CIRCLEQ_INSERT_TAIL(&cli->free_hist, hist, next);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+void
+cli_history_reset(void)
+{
+ this_cli->curr_hist = CIRCLEQ_FIRST(&this_cli->hd_hist);
+}
+
+void
+cli_history_dump(void)
+{
+ struct cli *cli = this_cli;
+ struct cli_hist *h;
+ int i = 0;
+
+ if (!cli || !cli->hist_mem || CIRCLEQ_EMPTY(&cli->hd_hist))
+ return;
+
+ CIRCLEQ_FOREACH(h, &cli->hd_hist, next) {
+ printf("%4d: %s\n", i++, h->line);
+ }
+}
diff --git a/lib/librte_cli/cli_history.h b/lib/librte_cli/cli_history.h
new file mode 100644
index 0000000..1feabb5
--- /dev/null
+++ b/lib/librte_cli/cli_history.h
@@ -0,0 +1,191 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_HISTORY_H_
+#define _CLI_HISTORY_H_
+
+/**
+ * @file
+ * RTE Command line history
+ *
+ */
+
+#include "cli.h"
+#include <sys/queue.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ CLI_DEFAULT_HISTORY = -1, /** Use the default history count */
+ CLI_NO_HISTORY = 0, /** No history */
+};
+
+struct cli_hist {
+ CIRCLEQ_ENTRY(cli_hist) next; /** link list of history lines */
+ char *line; /** line is strdup of original line */
+};
+
+struct cli;
+
+/**
+* Create and allocate a history structure
+*
+* @note Uses the thread variable this_cli
+*
+* @return
+* pointer to history structure or NULL on error
+*/
+struct cli_hist *cli_hist_alloc(void);
+
+/**
+* Free a CLI history structure and other memory
+*
+* @note Uses the thread variable this_cli
+*
+* @param hist
+* Pointer to the history structure
+* @return
+* N/A
+*/
+void cli_hist_free(struct cli_hist *hist);
+
+/**
+* Add line to history at the end
+*
+* @note Uses the thread variable this_cli
+*
+* @param line
+* Pointer to string to add to the history list
+* @return
+* N/A
+*/
+void cli_history_add(char *line);
+
+/**
+* Delete a history entry
+*
+* @note Uses the thread variable this_cli
+*
+* @return
+* N/A
+*/
+void cli_history_del(void);
+
+/**
+* Return the history command from line number
+*
+* @note Uses the thread variable this_cli
+*
+* @param lineno
+* The line number of the command to return.
+* @return
+* Pointer to line or NULL on error
+*/
+char *cli_history_line(int lineno);
+
+/**
+* Clear all of the history lines from the list
+*
+* @note Uses the thread variable this_cli
+*
+* @return
+* N/A
+*/
+void cli_history_clear(void);
+
+/**
+* Delete the history lines and structure
+*
+* @note Uses the thread variable this_cli
+*
+* @return
+* N/A
+*/
+void cli_history_delete(void);
+
+/**
+* Set the number of lines max in the history list
+*
+* @param cli
+* Pointer to the allocated cli structure
+* @param nb_hist
+* Number of lines max in the history list
+* @return
+* 0 is ok, -1 is error
+*/
+int cli_set_history(uint32_t nb_hist);
+
+/**
+* Return the previous history line
+*
+* @note Uses the thread variable this_cli
+*
+* @return
+* pointer to the pervious history line wrap if needed
+*/
+char *cli_history_prev(void);
+
+/**
+* Return the next history line
+*
+* @param cli
+* Pointer to the allocated cli structure
+* @return
+* pointer to the next history line wrap if needed
+*/
+char * cli_history_next(void);
+
+/**
+* Reset the current history pointer to the last entry.
+*
+* @note Uses the thread variable this_cli
+*/
+void cli_history_reset(void);
+
+/**
+* Add the default set of directories and commands
+*
+* @note Uses the thread variable this_cli
+*
+* @return
+* 0 is ok, -1 is error
+*/
+void cli_history_dump(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_HISTORY_H_ */
diff --git a/lib/librte_cli/cli_lib.rst b/lib/librte_cli/cli_lib.rst
new file mode 100644
index 0000000..734eac9
--- /dev/null
+++ b/lib/librte_cli/cli_lib.rst
@@ -0,0 +1,632 @@
+.. BSD LICENSE
+ Copyright(c) 2017 Intel Corporation. All rights reserved.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CLI library guide
+=================
+
+CLI stands for "Command Line Interface".
+
+This chapter describes the CLI library which is a part of the Data Plane
+Development Kit (DPDK). The CLI is a workalike replacement for cmdline library
+in DPDK and has a simpler interface and programming model plus it is dynamic.
+
+The primary goal of CLI is to allow the developer to create commands quickly
+and with very little compile or runtime configuration. Using standard Unix*
+like constructs which are very familar to the developer. Allowing the developer
+to construct a set of commands for development or deployment of the application.
+
+The CLI design uses a directory like design instead of a single level command
+line interface. Allowing the developer to use a directory style solution to
+controlling a DPDK application. The directory style design is nothing new, but
+it does have some advantages over a single level command structure.
+
+One advantage allows the directory path for the command to be part of the
+information used in executing the command. The next advantage is creating
+directories to make a hierarchy of commands, plus allowing whole directroy
+trees to dynamicly come and go as required by the developer.
+
+Some of the advantages are:
+
+ * CLI has no global variable other then the single thread variable called *this_cli* which can only be accessed from the thread which created the CLI instance.
+ * CLI supports commands, files, aliases, directories.
+ - The alias command is just a string using a simple substitution support for other commands similar to the bash shell like alias commands.
+ - Files can be static or dynamic information, can be changed on the fly and saved for later. The file is backed with a simple function callback to allow the developer to update the content or not.
+ * Added support for color and cursor movement APIs similar to Pktgen if needed by the developer.
+ * It is a work alike replacement for cmdline library. Both cmdline and CLI can be used in the same application if care is taken.
+ * Uses a simple fake like directory layout for command and files. Allowing for command hierarchy as path to the command can allow for specific targets to be identified without having to state it on the command line.
+ * Has auto-complete for commands, similar to Unix/Linux autocomplete and provides support for command option help as well.
+ * Callback functions for commands are simply just argc/argv like functions.
+ - The CLI does not convert arguments for the user, it is up to the developer to decode the argv[] values.
+ - Most of the arguments converted in the current cmdline are difficult to use or not required as the developer just picks string type and does the conversion himself.
+ * Dynamically be able to add and remove commands, directories, files and aliases, does not need to be statically compiled into the application.
+ * No weird structures in the code and reduces the line count for testpmd from 12K to 4.5K lines. I convert testpmd to have both CMDLINE and CLI with a command line option.
+ * Two methods to parse command lines, first is the standard argc/argv method in the function.
+ - The second method is to use a map of strings with simple printf like formatting to detect which command line the user typed.
+ - An ID value it returned to the used to indicate which mapping string was found to make the command line to be used in a switch statement.
+ * Environment variable support using the **env** command or using an API.
+ * Central help support if needed (optional).
+
+Overview
+--------
+
+The CLI library is a simple set of APIs which allow the developer to quickly
+create a set of commands using a simple programming interface already familar
+to the developer.
+
+One of the big advantages of CLI over Cmdline is it is dynamic, which means
+nodes or items can be added and removed on the fly. Which allows adding
+new directories, file or commands as needed or removing these items at runtime.
+The CLI has no global modifiable variables except for the one global pointer
+which is a thread based variable. Allowing the developer to have multiple CLI
+instances running at the same time on different threads if needed.
+
+Another big advantage is the calling of the backend function to support a
+command is very familar to developers as it is basically just a argc/argv
+style command and the developer gets the complete command line. The function
+as access to the global thread variable called **this_cli** pointing to the
+struct cli variable.
+
+.. code--block:: c
+
+ /* Show command returns 0 on OK and -1 on error */
+ int show_cmd(int argc, char **argv);
+
+Mapping commands
+----------------
+
+One other big advantage is the use of MAP structures, to help identify commands
+quickly plus allowing the developer to define new versions of commands and
+be able to identify these new versions using a simple identifier value.
+
+The format of the struct cli_map is:
+
+.. code-block:: c
+
+ struct cli_map show_map[] = {
+ /* Index value, Mapping string */
+ { 10, "show" },
+ { 20, "show %s" },
+ { 30, "show %P stats" },
+ { 40, "show %P %|link|errors|missed stats" },
+ { 0, NULL}
+ }
+
+The map is just an array of struct cli_map entries with a unique index value
+and mapping string. The index value can be any value the developer wants. As
+the index value is used to identify the given map string.
+
+The map string is a special formatted string similar to sprintf(), but the
+format values for % is different. Please look at the cli_mapping() function
+docs for more information. The %s is for any string and %P is used to a portlist
+format e.g. 1-3,5-7,9 as used for DPDK command line notation.
+
+The above array is parsed to match the command line from the user. The first
+map string that matches the user input will be returned from the call to
+cli_mapping() function.
+
+Constant values are required in the command as in index 30 'stats'. The index
+40 is using a variable fixed set of strings option, which means one of these
+fixed strings must match in that position.
+
+Another advantage of CLI is how simple it is to add new directroies, files and
+commands for user development. To add a command a developer needs to add an
+entry to the cli_tree structure and create a function using the above
+prototype format.
+
+.. code-block:: c
+
+ struct cli_tree my_tree[] = {
+ c_dir("/bin"),
+ c_cmd("hello", hello_cmd, "simple hello world command"),
+ c_cmd("show", show_cmd, "Show system information"),
+ c_end()
+ };
+
+The cli_tree structure is made with unions and the c_dir(), c_cmd() and c_end()
+help initialize the structure easily for the developer. The help and show
+commands above use the simple argc/argv prototype above.
+
+Only two things are required to create a command a cli_tree entry and a function
+to call. Using the cli_map and other structures are optional to make adding
+simple commands quick and easy. The call the cli_create() command or one of its
+helper functions cli_create_XYZ(). If have a function per command then using the
+mapping structure is optional, unless you want to have CLI parse and map
+commands to the exact entries. If cli_map is not used then the developer needs
+to decode the argc/argv to determine the command requests.
+
+The argc/argv is exactly like the standard usage in a Unix* system, which allows
+for using getopt() and other standard functions. The Cmdline structures and
+text conversions were defined at compile time in most cases, but in CLI the
+command routine is passed the argc/argv information to convert the strings as
+needed. The cli variable being a thread Local Storage (TLS) all user routines
+can access **this_cli** to gain access to the CLI structure if required at all.
+
+Environment variables
+---------------------
+
+The user can also set environment variables using the **env** command. These
+variables are also parsed in the command line as direct substitutions.
+
+Another special file is a string file, which can be used as an environment
+variable. When the variable is asked for the variable asks a function to return
+the string. The value of the string normally a system value or a generated
+value. These types of environment variables can not be set from the command
+line as a function pointer needs to be given. The c_str() macro helps in
+setting up these environment variables via the cli_tree structure.
+
+The special file backed environment variable can be deleted, but can not be
+restored without a reboot or some other command puting that variable back into
+the environment.
+
+Environment variables are denoted by a $(foo) like syntax and are expanded at
+the time of execution each time the command line is executed. Which means
+history lines with environment variables will be expanded again.
+
+Simple Files
+------------
+
+The CLI system also has support for simple files along with alias like commands.
+These simple files are backed by a function call and the other commands can read
+these files to get constant data or generated data depending on how the backend
+function works.
+
+Alias commands
+--------------
+The alias commands are fixed strings which are executed instead of a function
+provided by the developer. If the user has more arguments these are appended
+to the alias string and processed as if typed on the command line. Also the
+environment variables are expanded at execution time.
+
+.. note::
+
+ The CLI library was designed to be used in production code and the Cmdline
+ was not validated to the same standard as other DPDK libraries. The goal
+ is to provide a production CLI design.
+
+The CLI library supports some of the features of the Cmdline library such as,
+completion, cut/paste and some other special bindings that make configuration
+and debug faster and easier.
+
+The CLI desin uses some very simple VT100 control strings for displaying data
+and accepting input from the user. Some of the control strings are used to
+clear the screen or line and position the cursor on a VT100 compatible terminal.
+The CLI screen code also supports basic color and many other VT100 commands.
+
+The example application also shows how the CLI application can be extended to
+handle a list of commands and user input.
+
+The example presents a simple command prompt **DPDK-cli:/>** similar to a Unix*
+shell command along with a directory like file system.
+
+Some of the **default** commands contained under /sbin directory are:
+
+ * **ls**: list the current or provided directory files/commands.
+ * **cd**: Change directory command.
+ * **pwd**: print out the current working directory.
+ * **history**: List the current command line history if enabled.
+ * **more**: A simple command to page contents of files.
+ * **help**: display a the help screen.
+ * **quit**: exit the CLI application, also **Ctrl-x** will exit as well.
+ * **mkdir**: add a directory to the current directory.
+ * **delay**: wait for a given number of microseconds.
+ * **sleep**: wait for a given number of seconds.
+ * **rm**: remove a directory, file or command. Removing a file will delete the data.
+ * **cls**: clear the screen and redisplay the prompt.
+ * **version**: Display the current DPDK version being used.
+ * **path**: display the current search path for executable commands.
+ * **cmap**: Display the current system core and socket information.
+ * **hugepages**: Display the current hugepage information.
+ * **sizes**: a collection system structure and buffer sizes for debugging.
+ * **copyright**: a file containing DPDK copyright information.
+ * **env**: a command show/set/modify the environment variables.
+
+ * **ll**: an alias command to display long ls listing **ls -l**
+ * **h**: alias command for **history**
+ * **hello**: a simple Hello World! command.
+ * **show**: has a number of commands using the map feature.
+
+Under the /data directory is:
+
+ * **pci**: a simple example file for displaying the **lspci** command in CLI.
+
+.. note::
+
+ To terminate the application, use **Ctrl-x** or the command **quit**.
+
+Auto completion
+---------------
+
+CLI does support auto completion at the file or directory level, meaning the
+arguments to commands are not expanded as was done in Cmdline code. The CLI
+auto completion works similar to the standard Unix* system by expanding
+commands and directory paths. In normal Unix* like commands the user needs to
+execute the command asking for help information.
+
+Special command features
+------------------------
+
+Using the '!' followed by a number from the history list of commands you can
+execute that command again. Or using the UP/Down arrows the user can quickly
+find and execute or modify a previous command in history.
+
+The user can also execute host level commands if enabled using the '@' prefix
+to a command line e.g. @ls or @lspci or ... line is passed to popen or system
+function to be executed and the output displayed on the console if any output.
+To disable set CONFIG_RTE_CLI_HOST_COMMANDS=n in configuration file.
+
+Compiling the Application
+-------------------------
+
+#. Go to example directory:
+
+.. code-block:: c
+
+ export RTE_SDK=/path/to/rte_sdk
+ cd ${RTE_SDK}/examples/cli
+
+#. Set the target (a default target is used if not specified). For example:
+
+.. code-block:: console
+
+ export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+Refer to the *DPDK Getting Started Guide* for possible RTE_TARGET values.
+
+#. Build the application:
+
+.. code-block:: console
+
+ make
+
+Running the Application
+-----------------------
+
+To run the application in linuxapp environment, issue the following command:
+
+.. code-block:: console
+
+ $ ./build/cli
+
+.. note::
+ The example cli application does not require to be run as superuser
+ as it does not startup DPDK by calling rte_eal_init() routine. Which means
+ it also does not use DPDK features except for a few routines not requiring
+ EAL initialization.
+
+Refer to the *DPDK Getting Started Guide* for general information on running applications
+and the Environment Abstraction Layer (EAL) options.
+
+Explanation
+-----------
+
+The following sections provide some explanation of the code.
+
+EAL Initialization and cmdline Start
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first task is the initialization of the Environment Abstraction Layer (EAL),
+if required for the application.
+
+.. code-block:: c
+
+ int
+ main(int argc, char **argv)
+ {
+ if (cli_create_with_tree(init_tree) ==0) {
+ cli_start(NULL, 0); /* NULL is some init message done only once */
+ /* 0 means do not use color themes */
+ cli_destroy();
+ }
+
+The cli_start() function returns when the user types **Ctrl-x** or uses the
+quit command in this case, the application exits. The cli_create() call takes
+four arguments and each has a default value if not provided. The API used here
+is the cli_create_with_tree(), which uses defaults for three of the arguments.
+
+.. code-block:: c
+
+ /**
+ * Create the CLI engine
+ *
+ * @param prompt_func
+ * Function pointer to call for displaying the prompt.
+ * @param tree_func
+ * The user supplied function to init the tree or can be NULL. If NULL then
+ * a default tree is initialized with basic commands.
+ * @param nb_entries
+ * Total number of commands, files, aliases and directories. If 0 then use
+ * the default number of nodes. If -1 then unlimited number of nodes.
+ * @param nb_hist
+ * The number of lines to keep in history. If zero then turn off history.
+ * If the value is CLI_DEFAULT_HISTORY use CLI_DEFAULT_HIST_LINES
+ * @return
+ * 0 on success or -1
+ */
+ int cli_create(cli_prompt_t prompt_func, cli_tree_t tree_func,
+ int nb_entries, uint32_t nb_hist);
+
+The cli_create_with_tree() has only one argument which is the structure to use
+in order to setup the initial directory structure. Also the wrapper function
+int cli_create_with_defaults(void) can be used as well.
+
+Consult the cli.h header file for the default values. Also the alias node is a
+special alias file to allow for aliasing a command to another command.
+
+The tree init routine is defined like:
+
+.. code-block:: c
+
+ static struct cli_tree my_tree[] = {
+ c_dir("/data"),
+ c_file("pci", pci_file, "display lspci information"),
+ c_dir("/bin"),
+ c_cmd("hello", hello_cmd, "Hello-World!!"),
+ c_alias("h", "history", "display history commands"),
+ c_alias("ll", "ls -l", "long directory listing alias"),
+ c_end()
+ };
+
+ static int
+ init_tree(void)
+ {
+ /*
+ * Root is created already and using system default cmds and dirs, the
+ * developer is not required to use the system default cmds/dirs.
+ */
+ if (cli_default_tree_init())
+ return -1;
+
+ /* Using NULL here to start at root directory */
+ if (cli_add_tree(NULL, my_tree))
+ return -1;
+
+ cli_help_add("Show", show_map, show_help);
+
+ return cli_add_bin_path("/bin");
+ }
+
+
+The above structure is used to create the tree structure at initialization
+time. The struct cli_tree or cli_tree_t typedef can be used to setup a new
+directory tree or agument the default tree.
+
+The elements are using a set of macros c_dir, c_file, c_cmd, c_alias and c_end.
+These macros help fill out the cli_tree_t structure for the given type of item.
+
+The developer can create his own tree structure with any commands that are
+needed and/or call the cli_default_tree_init() routine to get the default
+structure of commands. If the developer does not wish to call the default
+CLI routine, then he must call the cli_create_root() function first before
+adding other nodes. Other nodes can be added and removed at anytime.
+
+CLI Map command support
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The CLI command has two types of support to handle arguments normal argc/argv
+and the map system. As shown above the developer creates a directory tree and
+attaches a function to a command. The function takes the argc/argv as arguments
+and the developer can just parse the arguments to decode the command arguments.
+Sometimes you have multiple commands or different versions of a command being
+handled by a single routine, this is were the map support comes into play.
+
+The map support defines a set of struct cli_map map[]; to help detect the
+correct command from the user. In the list of cli_map structures a single
+structure contains two items a developer defined index value and a command
+strings. The index value is used on the function to identify the specific type
+of command found in the list. The string is a special printf like string to
+help identify the command typed by the user. One of the first things todo in
+the command routine is to call the cli_mapping() function passing in the CLI
+pointer and the argc/argv values.The two method can be used at the same time.
+
+The cli_mapping() command matches up the special format string with the values
+in the argc/argv array and returns the developer supplied index value or really
+the pointer the struct cli_map instance.
+
+Now the developer can use the cli_map.index value in a switch() statement to
+locate the command the user typed or if not found a return of -1.
+
+Example:
+
+.. code-block:: c
+
+ static int
+ hello_cmd(int argc, char **argv)
+ {
+ int i, opt;
+
+ optind = 1;
+ while((opt = getopt(argc, argv, "?")) != -1) {
+ switch(opt) {
+ case '?': cli_usage(); return 0;
+ default:
+ break;
+ }
+ }
+
+ cli_printf("Hello command said: Hello World!! ");
+ for(i = 1; i < argc; i++)
+ cli_printf("%s ", argv[i]);
+ cli_printf("\n");
+
+ return 0;
+ }
+
+ static int
+ pci_file(struct cli_node *node, char *buff, int len, uint32_t opt)
+ {
+ if (is_file_open(opt)) {
+ FILE *f;
+
+ if (node->file_data && (node->fflags & CLI_FREE_DATA))
+ free(node->file_data);
+
+ node->file_data = malloc(32 * 1024);
+ if (!node->file_data)
+ return -1;
+ node->file_size = 32 * 1024;
+ node->fflags = CLI_DATA_RDONLY | CLI_FREE_DATA;
+
+ f = popen("lspci", "r");
+ if (!f)
+ return -1;
+
+ node->file_size = fread(node->file_data, 1, node->file_size, f);
+
+ pclose(f);
+ return 0;
+ }
+ return cli_file_handler(node, buff, len, opt);
+ }
+
+ static struct cli_map show_map[] = {
+ { 10, "show %P" },
+ { 20, "show %P mac %m" },
+ { 30, "show %P vlan %d mac %m" },
+ { 40, "show %P %|vlan|mac" },
+ { -1, NULL }
+ };
+
+ static const char *show_help[] = {
+ "show <portlist>",
+ "show <portlist> mac <ether_addr>",
+ "show <portlist> vlan <vlanid> mac <ether_addr>",
+ "show <portlist> [vlan|mac]",
+ CLI_HELP_PAUSE,
+ NULL
+ };
+
+ static int
+ show_cmd(int argc, char **argv)
+ {
+ struct cli_map *m;
+ uint32_t portlist;
+ struct ether_addr mac;
+
+ m = cli_mapping(Show_info.map, argc, argv);
+ if (!m)
+ return -1;
+
+ switch(m->index) {
+ case 10:
+ rte_parse_portlist(argv[1], &portlist);
+ cli_printf(" Show Portlist: %08x\n", portlist);
+ break;
+ case 20:
+ rte_parse_portlist(argv[1], &portlist);
+ rte_ether_aton(argv[3], &mac);
+ cli_printf(" Show Portlist: %08x, MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ portlist,
+ mac.addr_bytes[0],
+ mac.addr_bytes[1],
+ mac.addr_bytes[2],
+ mac.addr_bytes[3],
+ mac.addr_bytes[4],
+ mac.addr_bytes[5]);
+ break;
+ case 30:
+ rte_parse_portlist(argv[1], &portlist);
+ rte_ether_aton(argv[5], &mac);
+ cli_printf(" Show Portlist: %08x vlan %d MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ portlist,
+ atoi(argv[3]),
+ mac.addr_bytes[0],
+ mac.addr_bytes[1],
+ mac.addr_bytes[2],
+ mac.addr_bytes[3],
+ mac.addr_bytes[4],
+ mac.addr_bytes[5]);
+ break;
+ case 40:
+ rte_parse_portlist(argv[1], &portlist);
+ rte_ether_aton("1234:4567:8901", &mac);
+ cli_printf(" Show Portlist: %08x %s: ",
+ portlist, argv[2]);
+ if (argv[2][0] == 'm')
+ cli_printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac.addr_bytes[0],
+ mac.addr_bytes[1],
+ mac.addr_bytes[2],
+ mac.addr_bytes[3],
+ mac.addr_bytes[4],
+ mac.addr_bytes[5]);
+ else
+ cli_printf("%d\n", 101);
+ break;
+ default:
+ cli_help_show_group("Show");
+ return -1;
+ }
+ return 0;
+ }
+
+ static struct cli_tree my_tree[] = {
+ c_dir("/data"),
+ c_file("pci", pci_file, "display lspci information"),
+ c_dir("/bin"),
+ c_cmd("show", show_cmd, "show mapping options"),
+ c_cmd("hello", hello_cmd, "Hello-World!!"),
+ c_alias("h", "history", "display history commands"),
+ c_alias("ll", "ls -l", "long directory listing alias"),
+ c_end()
+ };
+
+Here is the cli_tree for this example, note it has a lot more commands. The show_cmd
+or **show** command is located a number of lines down. The cli_tree creates in
+the **/bin** directory a number of commands and the show command is one of these. The
+show command has four different formats if you look at the **show_map[]** structure.
+
+The user types one of these commands and cli_mapping() function attempts to locate the
+correct entry in the list. You will also notice another structure called
+**show_help**, which is an array of strings giving a cleaner and longer help
+description of each of the commands.
+
+
+Understanding the CLI system
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The command line interface is defined as a fake directory tree with executables,
+directorys and files. The user uses shell like standard commands to move about
+the directory and execute commands. The CLI is not a powerful as the
+Bash shell, but has a number of similar concepts.
+
+Our fake directory tree has a '/' or root directory which is created when
+cli_create() is called along with the default sbin directory. The user starts out
+at the root directory '/' and is allowed to cd to other directories, which could
+contain more executables, aliases or directories. The max number of directory
+levels is limited to the number of nodes given at startup.
+
+The default directory tree starts out as just root (/) and a sbin directory.
+Also it contains a file called copyright in root, which can be displayed
+using the default 'more copyright' command.
+
+A number of default commands are predefined in the /sbin directory and are
+defined above. Other bin directories can be added to the system if needed,
+but a limit of CLI_MAX_BINS is defined in the cli.h header file.
+
diff --git a/lib/librte_cli/cli_map.c b/lib/librte_cli/cli_map.c
new file mode 100644
index 0000000..0437ac7
--- /dev/null
+++ b/lib/librte_cli/cli_map.c
@@ -0,0 +1,317 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_string_fns.h>
+
+#include "cli.h"
+#include "cli_string_fns.h"
+
+int
+cli_map_list_search(const char *fmt, char *item, int index)
+{
+ char *buf;
+ int size;
+ char *opts[CLI_MAX_ARGVS + 1];
+
+ size = strlen(fmt) + 1;
+ buf = alloca(size);
+ if (!buf)
+ return -1;
+
+ memset(buf, '\0', size);
+ memset(opts, '\0', sizeof(opts));
+
+ snprintf(buf, size, "%s", fmt);
+ rte_strtok(buf, " ", opts, CLI_MAX_ARGVS);
+
+ /* Skip the %| in the string options */
+ return rte_stropt(&opts[index][2], item, "|");
+}
+
+static int
+is_map_valid(const char *fmt, char *arg)
+{
+ int ret = 0;
+
+ if (strchr("%dDhHsn46mkPC|l", fmt[1]) == NULL)
+ return ret;
+
+ /* validate all of the characters matching the format */
+ do {
+ ret = 0;
+ switch (fmt[1]) {
+ case '%':
+ ret = 1;
+ break;
+ case 'd':
+ if (isdigit(*arg)) ret = 1;
+ break;
+ case 'D':
+ if (isdigit(*arg)) ret = 1;
+ break;
+ case 'h':
+ if (isxdigit(*arg)) ret = 1;
+ break;
+ case 'H':
+ if (isxdigit(*arg)) ret = 1;
+ break;
+ case 's':
+ if (isprint(*arg)) ret = 1;
+ break;
+ /* TODO: validate this is a valid IPv4 network address */
+ case 'n':
+ if (isdigit(*arg)) ret = 1;
+ break;
+ /* TODO: validate this is a valid IPv4 address */
+ case '4':
+ if (isdigit(*arg)) ret = 1;
+ break;
+ /* TODO: validate this is a valid IPv6 address */
+ case '6':
+ if (isdigit(*arg)) ret = 1;
+ break;
+ /* TODO: validate this is a valid MAC address */
+ case 'm':
+ if (isxdigit(*arg)) ret = 1;
+ break;
+ case 'k':
+ return 1;
+ /* list of ports or cores or the word all */
+ case 'P':
+ if (isdigit(*arg) || (*arg == 'a')) ret = 1;
+ break;
+ case 'C':
+ if (isdigit(*arg) || (*arg == 'a')) ret = 1;
+ break;
+ case '|':
+ return (rte_stropt(&fmt[1], arg, "|") == -1) ? 0 : 1;
+ case 'l':
+ ret = 1;
+ break;
+ default:
+ return 0;
+ }
+ arg++;
+ } while (*arg && (ret == 0));
+
+ return ret;
+}
+
+struct cli_map *
+cli_mapping(struct cli_map *maps, int argc, char **argv)
+{
+ int nb_args, i, j, ok;
+ const char *m;
+ char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS], *p;
+
+ memset(line, '\0', sizeof(line));
+ memset(map, '\0', sizeof(map));
+
+ p = line;
+ for (i = 0; (m = maps[i].fmt) != NULL; i++) {
+ strcpy(p, m);
+
+ nb_args = rte_strtok(p, " ", map, CLI_MAX_ARGVS);
+
+ /* display the cli MAP if present as some help */
+ if (!strcmp("-?", argv[argc - 1]) ||
+ !strcmp("?", argv[argc - 1])) {
+ cli_maps_show(maps, argc, argv);
+ return NULL;
+ }
+
+ if (nb_args != argc)
+ continue;
+
+ /* Scan the map entry looking for a valid match */
+ for (j = 0, ok = 1; ok && (j < argc); j++) {
+ if (map[j][0] == '%') {
+ /* Found a format '%' validate it */
+ if (!is_map_valid(map[j], argv[j]))
+ ok = 0;
+ /* a constant string match valid */
+ } else if (!rte_strmatch(map[j], argv[j]))
+ ok = 0;
+ }
+
+ if (ok)
+ return &maps[i];
+ }
+
+ return NULL;
+}
+
+static void
+decode_map(const char *fmt)
+{
+ char *argv[CLI_MAX_ARGVS + 1];
+ char line[CLI_MAX_PATH_LENGTH + 1];
+ int n, i;
+
+ memset(argv, '\0', sizeof(argv));
+
+ strcpy(line, fmt);
+ if (fmt[0] != '%') {
+ cli_printf("%s ", fmt);
+ return;
+ }
+
+ switch (fmt[1]) {
+ case '%':
+ cli_printf("%% ");
+ break;
+ case 'd':
+ cli_printf("<32bit number> ");
+ break;
+ case 'D':
+ cli_printf("<64bit number> ");
+ break;
+ case 'h':
+ cli_printf("<32bit hex> ");
+ break;
+ case 'H':
+ cli_printf("<64bit hex> ");
+ break;
+ case 's':
+ cli_printf("<string> ");
+ break;
+ case '4':
+ cli_printf("<IPv4 Address> ");
+ break;
+ case '6':
+ cli_printf("<IPv6 Address> ");
+ break;
+ case 'm':
+ cli_printf("<MAC address> ");
+ break;
+ case 'k':
+ cli_printf("<kvargs> ");
+ break;
+ case 'P':
+ cli_printf("<portlist> ");
+ break;
+ case 'C':
+ cli_printf("<corelist> ");
+ break;
+ case '|':
+ cli_printf("[");
+ n = rte_strtok(&line[2], "|", argv, CLI_MAX_ARGVS);
+ for (i = 0; i < n; i++)
+ cli_printf("%s%s", argv[i], (i < (n - 1)) ? "|" : "");
+ cli_printf("] ");
+ break;
+ case 'l':
+ cli_printf("<list> ");
+ break;
+ default:
+ cli_printf("<unknown> ");
+ break;
+ }
+}
+
+void
+cli_map_show(struct cli_map *m)
+{
+ int i, nb_args;
+ char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS + 1], *p;
+
+ memset(line, '\0', sizeof(line));
+ memset(map, '\0', sizeof(map));
+
+ p = line;
+
+ strcpy(p, m->fmt);
+
+ nb_args = rte_strtok(p, " ", map, CLI_MAX_ARGVS);
+
+ cli_printf(" %s ", map[0]);
+ for (i = 1; i < nb_args; i++)
+ decode_map(map[i]);
+ cli_printf("\n");
+}
+
+void
+cli_maps_show(struct cli_map *maps, int argc, char **argv)
+{
+ struct cli_map *m;
+ char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS + 1];
+ int nb_args;
+
+ if (!argc)
+ return;
+
+ cli_printf("\nUsage:\n");
+ for (m = maps; m->fmt != NULL; m++) {
+ line[0] = '\0';
+ map[0] = NULL;
+
+ strcpy(line, m->fmt);
+
+ nb_args = rte_strtok(line, " ", map, CLI_MAX_ARGVS);
+
+ if (nb_args && !strcmp(argv[0], map[0]))
+ cli_map_show(m);
+ }
+}
+
+void
+cli_map_dump(struct cli_map *maps, int argc, char **argv)
+{
+ int i, nb_args;
+ struct cli_map *m;
+ char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS + 1], *p;
+
+ memset(line, '\0', sizeof(line));
+ memset(map, '\0', sizeof(map));
+
+ p = line;
+
+ m = cli_mapping(maps, argc, argv);
+ if (!m) {
+ cli_printf("Map for %d/", argc);
+ for (i = 0; i < argc; i++) {
+ cli_printf("<%s>", argv[i]);
+ if ((i + 1) < argc)
+ cli_printf(",");
+ }
+ cli_printf("\n");
+ }
+
+ strcpy(p, m->fmt);
+
+ nb_args = rte_strtok(p, " ", map, CLI_MAX_ARGVS);
+
+ cli_printf("%4d - %s == %s\n", m->index, argv[0], map[0]);
+ for (i = 1; i < argc && i < nb_args; i++)
+ cli_printf(" %s == %s\n", argv[i], map[i]);
+}
diff --git a/lib/librte_cli/cli_map.h b/lib/librte_cli/cli_map.h
new file mode 100644
index 0000000..d8ba2ba
--- /dev/null
+++ b/lib/librte_cli/cli_map.h
@@ -0,0 +1,134 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *
+ * String-related functions as replacement for libc equivalents
+ */
+
+#ifndef _CLI_MAP_H_
+#define _CLI_MAP_H_
+
+#include <netinet/in.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct cli_map {
+ int index;
+ const char *fmt;
+};
+
+/**
+ * Parse a string <list> looking for matching mapping.
+ *
+ * @param maps
+ * Array string points with mapping formats using a special % formats
+ * %d - decimal number 32bit
+ * %D - decimal number 64bit
+ * %h - hexadecimal number 32 bit number
+ * %H - hexadecimal number 64 bit number
+ * %P - Portlist
+ * %C - Corelist
+ * %s - string
+ * %m - MAC address format
+ * %4 - IPv4 address
+ * %6 - IPv6 address
+ * %k - kvargs list of <key>=<value>[,...] strings
+ * %l - list format, if constains space then quote the string first
+ * %|<fixed-list> - Fixed list of valid strings
+ * @param argc
+ * Number of arguments in <argv>
+ * @param argv
+ * Array of command line string pointers
+ * @return
+ * return pointer map entry or NULL if not found
+ */
+struct cli_map *cli_mapping(struct cli_map *maps,
+ int argc, char **argv);
+
+/**
+ * Dump out the map entry
+ *
+ * @param maps
+ * The cli_map structure pointer
+ * @return
+ * None
+ */
+void cli_map_show(struct cli_map *m);
+
+/**
+ * Show the map table entries
+ *
+ * @param maps
+ * The cli_map structure pointer
+ * @return
+ * None
+ */
+void cli_maps_show(struct cli_map *maps, int argc, char **argv);
+
+/**
+ * Dump out the map table entry matching the argc/argv
+ *
+ * @param maps
+ * The cli_map structure pointer
+ * @param argc
+ * Number of argumemts
+ * @param argv
+ * List of argument strings
+ * @return
+ * None
+ */
+void cli_map_dump(struct cli_map *maps, int argc, char **argv);
+
+/**
+ * Determine index value for the list item
+ *
+ * @param fmt
+ * cli_map format string
+ * @param item
+ * pointer to the item to search for in the list.
+ * @param
+ * index value for which list in format string to scan
+ * @return
+ * -1 on error or index into list selections.
+ */
+int cli_map_list_search(const char *fmt, char *item, int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CLI_MAP_H */
diff --git a/lib/librte_cli/cli_scrn.c b/lib/librte_cli/cli_scrn.c
new file mode 100644
index 0000000..b8c1e9b
--- /dev/null
+++ b/lib/librte_cli/cli_scrn.c
@@ -0,0 +1,209 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Created 2017 by Keith Wiles @ intel.com */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <rte_config.h>
+#include <rte_atomic.h>
+#include <rte_malloc.h>
+#include <rte_spinlock.h>
+
+#include "cli_scrn.h"
+
+RTE_DEFINE_PER_LCORE(struct cli_scrn *, scrn);
+
+void
+__attribute__((format(printf, 3, 4)))
+scrn_printf(int16_t r, int16_t c, const char *fmt, ...)
+{
+ va_list vaList;
+
+ if ( (r != 0) && (c != 0) )
+ scrn_pos(r, c);
+ va_start(vaList, fmt);
+ vfprintf(this_scrn->fd_out, fmt, vaList);
+ va_end(vaList);
+ fflush(this_scrn->fd_out);
+}
+
+void
+__attribute__((format(printf, 3, 4)))
+scrn_cprintf(int16_t r, int16_t ncols, const char *fmt, ...)
+{
+ va_list vaList;
+ char str[512];
+
+ if (ncols == -1)
+ ncols = this_scrn->ncols;
+
+ va_start(vaList, fmt);
+ vsnprintf(str, sizeof(str), fmt, vaList);
+ va_end(vaList);
+
+ scrn_pos(r, scrn_center_col(ncols, str));
+ scrn_puts("%s", str);
+}
+
+void
+__attribute__((format(printf, 4, 5)))
+scrn_fprintf(int16_t r, int16_t c, FILE *f, const char *fmt, ...)
+{
+ va_list vaList;
+
+ if ( (r != 0) && (c != 0) )
+ scrn_pos(r, c);
+ va_start(vaList, fmt);
+ vfprintf(f, fmt, vaList);
+ va_end(vaList);
+ fflush(f);
+}
+
+static void
+scrn_set_io(FILE *in, FILE *out)
+{
+ struct cli_scrn *scrn = this_scrn;
+
+ if (scrn) {
+ if (scrn->fd_in && (scrn->fd_in != stdin))
+ fclose(scrn->fd_in);
+
+ if (scrn->fd_out && (scrn->fd_out != stdout))
+ fclose(scrn->fd_in);
+
+ scrn->fd_in = in;
+ scrn->fd_out = out;
+ }
+}
+
+static int
+scrn_stdin_setup(void)
+{
+ struct cli_scrn *scrn = this_scrn;
+ struct termios term;
+
+ if (!scrn)
+ return -1;
+
+ scrn_set_io(stdin, stdout);
+
+ memset(&scrn->oldterm, 0, sizeof(term));
+ if (tcgetattr(fileno(scrn->fd_in), &scrn->oldterm) ||
+ tcgetattr(fileno(scrn->fd_in), &term)) {
+ printf("%s: setup failed for tty\n", __func__);
+ return -1;
+ }
+
+ term.c_lflag &= ~(ICANON | ECHO | ISIG | IEXTEN);
+
+ if (tcsetattr(fileno(scrn->fd_in), TCSANOW, &term)) {
+ printf("%s: failed to set tty\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+scrn_stdin_restore(void)
+{
+ struct cli_scrn *scrn = this_scrn;
+
+ if (!scrn)
+ return;
+
+ if (tcsetattr(fileno(scrn->fd_in), TCSANOW, &scrn->oldterm))
+ printf("%s: failed to set tty\n", __func__);
+
+ if (system("stty sane"))
+ printf("%s: system command failed\n", __func__);
+}
+
+int
+scrn_create(int scrn_type, int16_t nrows, int16_t ncols, int theme)
+{
+ struct cli_scrn *scrn = this_scrn;
+
+ if (!scrn) {
+ scrn = malloc(sizeof(struct cli_scrn));
+ if (!scrn)
+ return -1;
+ memset(scrn, 0, sizeof(struct cli_scrn));
+
+ this_scrn = scrn;
+ }
+
+ rte_atomic32_set(&scrn->pause, SCRN_SCRN_PAUSED);
+
+ scrn->nrows = nrows;
+ scrn->ncols = ncols;
+ scrn->theme = theme;
+ scrn->type = scrn_type;
+
+ if (scrn_type == SCRN_STDIN_TYPE) {
+ if (scrn_stdin_setup()) {
+ free(scrn);
+ return -1;
+ }
+ } else {
+ free(scrn);
+ return -1;
+ }
+
+ scrn_color(SCRN_DEFAULT_FG, SCRN_DEFAULT_BG, SCRN_OFF);
+
+ scrn_erase(nrows);
+
+ return 0;
+}
+
+int
+scrn_create_with_defaults(int theme)
+{
+ return scrn_create(SCRN_STDIN_TYPE,
+ SCRN_DEFAULT_ROWS, SCRN_DEFAULT_COLS,
+ (theme)? SCRN_THEME_ON : SCRN_THEME_OFF);
+}
+
+void
+scrn_destroy(void)
+{
+ if (this_scrn && (this_scrn->type == SCRN_STDIN_TYPE))
+ scrn_stdin_restore();
+}
diff --git a/lib/librte_cli/cli_scrn.h b/lib/librte_cli/cli_scrn.h
new file mode 100644
index 0000000..762f55a
--- /dev/null
+++ b/lib/librte_cli/cli_scrn.h
@@ -0,0 +1,360 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Created 2017 by Keith Wiles @ intel.com */
+
+#ifndef __CLI_SCRN_H_
+#define __CLI_SCRN_H_
+
+#include <termios.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file
+ * RTE simple cursor and color support for VT100 using ANSI color escape codes.
+ *
+ ***/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <rte_atomic.h>
+#include <rte_per_lcore.h>
+
+/** scrn version number */
+#define SCRN_VERSION "2.0.0"
+
+/* Add more defines for new types */
+#define SCRN_STDIN_TYPE 0
+
+#define SCRN_DEFAULT_ROWS 44
+#define SCRN_DEFAULT_COLS 132
+
+/** Structure to hold information about the screen and control access. */
+struct cli_scrn {
+ rte_atomic32_t pause; /**< Pause the update of the screen. */
+ rte_atomic32_t state; /**< Screen state on or off */
+ uint16_t nrows; /**< Max number of rows. */
+ uint16_t ncols; /**< Max number of columns. */
+ uint16_t theme; /**< Current theme state on or off */
+ uint16_t type; /**< screen I/O type */
+ struct termios oldterm; /**< Old terminal setup information */
+ FILE *fd_out; /**< File descriptor for output */
+ FILE *fd_in; /**< File descriptor for input */
+};
+
+RTE_DECLARE_PER_LCORE(struct cli_scrn *, scrn);
+#define this_scrn RTE_PER_LCORE(scrn)
+
+/** Enable or disable the screen from being updated */
+enum { SCRN_SCRN_RUNNING = 0, SCRN_SCRN_PAUSED = 1 };
+
+/** Enable or disable the theme or color options */
+enum { SCRN_THEME_OFF = 0, SCRN_THEME_ON = 1 };
+
+/** ANSI color codes zero based, need to add 30 or 40 for foreground or
+ background color code */
+typedef enum {
+ SCRN_BLACK = 0, SCRN_RED = 1, SCRN_GREEN = 2, SCRN_YELLOW = 3,
+ SCRN_BLUE = 4, SCRN_MAGENTA = 5, SCRN_CYAN = 6,SCRN_WHITE = 7,
+ SCRN_RGB = 8, SCRN_DEFAULT_FG = 9, SCRN_DEFAULT_BG = 9,
+ SCRN_NO_CHANGE = 98, SCRN_UNKNOWN_COLOR = 99
+} scrn_color_e;
+
+/** ANSI color codes zero based for attributes per color */
+typedef enum {
+ SCRN_OFF = 0, SCRN_BOLD = 1, SCRN_UNDERSCORE = 4, SCRN_BLINK = 5,
+ SCRN_REVERSE = 7, SCRN_CONCEALED = 8, SCRN_NO_ATTR = 98,
+ SCRN_UNKNOWN_ATTR = 99
+} scrn_attr_e;
+
+/** A single byte to hold port of a Red/Green/Blue color value */
+typedef uint8_t cli_rgb_t;
+
+static inline void
+__attribute__((format(printf, 1, 2)))
+scrn_puts(const char *fmt, ...)
+{
+ struct cli_scrn *scrn = this_scrn;
+ FILE * f;
+ va_list vaList;
+
+ f = (!scrn || !scrn->fd_out)? stdout : scrn->fd_out;
+ va_start(vaList, fmt);
+ vfprintf(f, fmt, vaList);
+ va_end(vaList);
+ fflush(f);
+}
+
+void scrn_cprintf(int16_t r, int16_t ncols, const char *fmt, ...);
+void scrn_printf(int16_t r, int16_t c, const char *fmt, ...);
+void scrn_fprintf(int16_t r, int16_t c, FILE *f, const char *fmt, ...);
+
+#define _s(_x, _y) static __inline__ void _x { _y; }
+
+/** position cursor to row and column */
+_s(scrn_pos(int r, int c), scrn_puts("\033[%d;%dH", r, c))
+
+/** Move cursor to the top left of the screen */
+_s(scrn_top(void), scrn_puts("\033H"))
+
+/** Move cursor to the Home position */
+_s(scrn_home(void), scrn_puts("\033H"))
+
+/** Turn cursor off */
+_s(scrn_coff(void), scrn_puts("\033[?25l"))
+
+/** Turn cursor on */
+_s(scrn_con(void), scrn_puts("\033[?25h"))
+
+/** Hide cursor */
+_s(scrn_turn_on(void), scrn_puts("\033[?25h"))
+
+/** Display cursor */
+_s(scrn_turn_off(void), scrn_puts("\033[?25l"))
+
+/** Save current cursor position */
+_s(scrn_save(void), scrn_puts("\0337"))
+
+/** Restore the saved cursor position */
+_s(scrn_restore(void), scrn_puts("\0338"))
+
+/** Clear from cursor to end of line */
+_s(scrn_eol(void), scrn_puts("\033[K"))
+
+/** Clear from cursor to begining of line */
+_s(scrn_cbl(void), scrn_puts("\033[1K"))
+
+/** Clear entire line */
+_s(scrn_cel(void), scrn_puts("\033[2K"))
+
+/** Clear from cursor to end of screen */
+_s(scrn_clw(void), scrn_puts("\033[J"))
+
+/** Clear from cursor to begining of screen */
+_s(scrn_clb(void), scrn_puts("\033[1J"))
+
+/** Clear the screen, more cursor to home */
+_s(scrn_cls(void), scrn_puts("\033[2J"))
+
+/** Start reverse video */
+_s(scrn_reverse(void), scrn_puts("\033[7m"))
+
+/** Stop attribute like reverse and underscore */
+_s(scrn_normal(void), scrn_puts("\033[0m"))
+
+/** Scroll whole screen up r number of lines */
+_s(scrn_scroll(int r), scrn_puts("\033[%d;r", r))
+
+/** Scroll whole screen up r number of lines */
+_s(scrn_scroll_up(int r), scrn_puts("\033[%dS", r))
+
+/** Scroll whole screen down r number of lines */
+_s(scrn_scroll_down(int r), scrn_puts("\033[%dT", r))
+
+/** Move down nlines plus move to column 1 */
+_s(scrn_nlines(int r), scrn_puts("\033[%dE", r))
+
+/** Set window size, from to end of screen */
+_s(scrn_setw(int t), scrn_puts("\033[%d;r", t))
+
+/** Cursor postion report */
+_s(scrn_cpos(void), scrn_puts("\033[6n"))
+
+/* Report Cursor Position <ESC>[{ROW};{COLUMN}R
+ Generated by the device in response to a Query Cursor Position request;
+ reports current cursor position. */
+
+/** Return the version string */
+static __inline__ const char *
+scrn_version(void)
+{
+ return SCRN_VERSION;
+}
+
+/** Position the cursor to a line and clear the entire line */
+static __inline__ void
+scrn_clr_line(int r)
+{
+ scrn_pos(r, 0);
+ scrn_cel();
+}
+
+/** Position cursor to row/column and clear to end of line */
+static __inline__ void
+scrn_eol_pos(int r, int c)
+{
+ scrn_pos(r, c);
+ scrn_eol();
+}
+
+void __set_prompt(void);
+
+/** Stop screen from updating until resumed later */
+static __inline__ void
+scrn_pause(void)
+{
+ rte_atomic32_set(&this_scrn->pause, SCRN_SCRN_PAUSED);
+ __set_prompt();
+}
+
+/** Resume the screen from a pause */
+static __inline__ void
+scrn_resume(void)
+{
+ rte_atomic32_set(&this_scrn->pause, SCRN_SCRN_RUNNING);
+ __set_prompt();
+}
+
+/* Is the screen in the paused state */
+static __inline__ int
+scrn_is_paused(void)
+{
+ return rte_atomic32_read(&this_scrn->pause) == SCRN_SCRN_PAUSED;
+}
+
+/** Output a message of the current line centered */
+static __inline__ int
+scrn_center_col(int16_t ncols, const char *msg)
+{
+ int16_t s;
+
+ s = ((ncols / 2) - (strlen(msg) / 2));
+ return (s <= 0) ? 1 : s;
+}
+
+/** Erase the screen by scrolling it off the display, then put cursor at the
+ bottom */
+static __inline__ void
+scrn_erase(int16_t nrows)
+{
+ int16_t i, cnt;
+ const char *nl = "\n\n\n\n\n\n\n\n";
+
+ scrn_setw(1); /* Clear the window to full */
+ /* screen. */
+ scrn_pos(nrows + 1, 1); /* Put cursor on the last row. */
+
+ /* Scroll the screen to clear the screen and keep the previous information */
+ /* in scrollbar. */
+ for (i = 0, cnt = 0; i < (nrows / (int16_t)strlen(nl)); i++, cnt += strlen(nl))
+ scrn_printf(0, 0, "%s", nl);
+
+ /* Scroll the last set of rows. */
+ for (i = cnt; i < nrows; i++)
+ scrn_printf(0, 0, "\n");
+}
+
+/** Output a string at a row/column for a number of times */
+static __inline__ void
+scrn_repeat(int16_t r, int16_t c, const char *str, int cnt)
+{
+ int i;
+
+ scrn_pos(r, c);
+ for (i = 0; i < cnt; i++)
+ scrn_printf(0, 0, "%s", str);
+}
+
+/** Output a column of strings at a given starting row for a given number of
+ times */
+static __inline__ void
+scrn_col_repeat(int16_t r, int16_t c, const char *str, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ scrn_pos(r++, c);
+ scrn_printf(0, 0, "%s", str);
+ }
+}
+
+/** Set the foreground color + attribute at the current cursor position */
+static __inline__ void
+scrn_fgcolor(scrn_color_e color, scrn_attr_e attr)
+{
+ scrn_puts("\033[%d;%dm", attr, color + 30);
+}
+
+/** Set the background color + attribute at the current cursor position */
+static __inline__ void
+scrn_bgcolor(scrn_color_e color, scrn_attr_e attr)
+{
+ scrn_puts("\033[%d;%dm", attr, color + 40);
+}
+
+/** Set the foreground/background color + attribute at the current cursor
+ position */
+static __inline__ void
+scrn_fgbgcolor(scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)
+{
+ scrn_puts("\033[%d;%d;%dm", attr, fg + 30, bg + 40);
+}
+
+/** Main routine to set color for foreground and background nd attribute at the
+ current position */
+static __inline__ void
+scrn_color(scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)
+{
+
+ if ( (fg != SCRN_NO_CHANGE) && (bg != SCRN_NO_CHANGE) )
+ scrn_fgbgcolor(fg, bg, attr);
+ else if (fg == SCRN_NO_CHANGE)
+ scrn_bgcolor(bg, attr);
+ else if (bg == SCRN_NO_CHANGE)
+ scrn_fgcolor(fg, attr);
+}
+
+/** Setup for 256 RGB color methods. A routine to output RGB color codes if
+ supported */
+static __inline__ void
+scrn_rgb(uint8_t fg_bg, cli_rgb_t r, cli_rgb_t g, cli_rgb_t b)
+{
+ scrn_puts("\033[%d;2;%d;%d;%dm", fg_bg, r, g, b);
+}
+
+/** External functions used for positioning the cursor and outputing a string
+ like printf */
+int scrn_create(int scrn_type, int16_t nrows, int16_t ncols, int theme);
+int scrn_create_with_defaults(int theme);
+void scrn_destroy(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CLI_SCRN_H_ */
diff --git a/lib/librte_cli/cli_search.c b/lib/librte_cli/cli_search.c
new file mode 100644
index 0000000..34a613a
--- /dev/null
+++ b/lib/librte_cli/cli_search.c
@@ -0,0 +1,333 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_string_fns.h>
+
+#include "cli.h"
+#include "cli_string_fns.h"
+
+static int
+__count_nodes(struct cli_node *node,
+ uint32_t flags, args_t *args)
+{
+ if (flags & node->type)
+ args->arg1.u32[0]++;
+
+ return 0;
+}
+
+uint32_t
+cli_dir_item_count(struct cli_node *node, uint32_t types)
+{
+ args_t args;
+
+ if (!node || !is_directory(node))
+ return 0;
+
+ memset(&args, '\0', sizeof(args));
+
+ cli_scan_directory(node, __count_nodes, types, &args);
+
+ return args.arg1.u32[0];
+}
+
+uint32_t
+cli_path_item_count(uint32_t types)
+{
+ uint32_t cnt = 0, i;
+
+ /* Look in the current directory first for a command */
+ for (i = 0; i < CLI_MAX_BINS; i++)
+ cnt += cli_dir_item_count(this_cli->bins[i], types);
+
+ return cnt;
+}
+
+uint32_t
+cli_path_cmd_count(void)
+{
+ return cli_path_item_count(CLI_EXE_TYPE);
+}
+
+static uint32_t
+node_list_with_type(uint32_t flags, void **ret)
+{
+ struct cli_node *n, **nodes, *bin;
+ uint32_t cnt, i, k = 0;
+
+ cnt = cli_path_item_count(flags);
+
+ if (cnt) {
+ nodes = calloc(cnt + 1, sizeof(struct cli_node *));
+ if (!nodes)
+ return 0;
+
+ for (i = 0; i < CLI_MAX_BINS; i++) {
+ bin = this_cli->bins[i];
+ if (!bin)
+ continue;
+ /*
+ * Current directory could be a bin directory skip this bin
+ * directory as the cwd has already been searched. The cwd is
+ * the first entry in the bins list.
+ */
+ if ((i > 0) && (bin == get_cwd())) {
+ cli_printf("skip %s\n", bin->name);
+ continue;
+ }
+
+ TAILQ_FOREACH(n, &bin->items, next) {
+ if (n->type & flags)
+ nodes[k++] = n;
+ }
+ }
+ *ret = nodes;
+ }
+ return k;
+}
+
+static uint32_t
+dir_list_with_type(struct cli_node *dir, uint32_t flags, void **ret)
+{
+ struct cli_node *n, **nodes;
+ uint32_t cnt, k = 0;
+
+ cnt = cli_dir_item_count(dir, flags);
+
+ if (cnt) {
+ nodes = calloc(cnt + 1, sizeof(struct cli_node *));
+ if (!nodes)
+ return 0;
+
+ TAILQ_FOREACH(n, &dir->items, next) {
+ if (n->type & flags)
+ nodes[k++] = n;
+ }
+ *ret = nodes;
+ }
+ return k;
+}
+
+uint32_t
+cli_node_list_with_type(struct cli_node *node, uint32_t flags, void **ret)
+{
+ if (node)
+ return dir_list_with_type(node, flags, ret);
+ else
+ return node_list_with_type(flags, ret);
+}
+
+void
+cli_node_list_free(void *nodes)
+{
+ free(nodes);
+}
+
+int
+cli_scan_directory(struct cli_node *dir,
+ cli_scan_t func, uint32_t flags, args_t *args)
+{
+ struct cli_node *node;
+ int ret = 0;
+
+ if (!func)
+ return ret;
+
+ if (!dir)
+ dir = cli_root_node();
+
+ TAILQ_FOREACH(node, &dir->items, next) {
+ if (node->type & flags) {
+ ret = func(node, flags, args);
+ if (ret)
+ break;
+ }
+ }
+ return ret;
+}
+
+int
+cli_scan_path(const char *path, cli_scan_t func, uint32_t flags, args_t *args)
+{
+ struct cli_node *node;
+
+ if (cli_find_node(path, &node))
+ if (cli_scan_directory(node, func, flags, args))
+ return 1;
+ return 0;
+}
+
+struct cli_node *
+cli_search_dir(struct cli_node *dir, const char *name, uint32_t type)
+{
+ struct cli_node *node;
+
+ if (!name || (*name == '\0'))
+ return NULL;
+
+ if (!dir)
+ dir = get_cwd();
+ else if (!is_directory(dir))
+ return NULL;
+
+ /* Process the .. and . directories */
+ if (!strcmp(name, ".."))
+ return (dir->parent) ? dir->parent : NULL;
+ else if (!strcmp(name, "."))
+ return dir;
+
+ TAILQ_FOREACH(node, &dir->items, next) {
+ if (rte_strmatch(node->name, name) && (node->type & type))
+ return node;
+ }
+
+ return NULL;
+}
+
+int
+cli_find_node(const char *path, struct cli_node **ret)
+{
+ struct cli_node *node, *dir;
+ char *my_path = NULL;
+ char *argv[CLI_MAX_ARGVS + 1];
+ int n, i;
+
+ if (!path || (*path == '\0'))
+ return 0;
+
+ if (path[0] == '/' && path[1] == '\0') {
+ node = cli_root_node();
+ goto _leave;
+ }
+
+ /* Skip the leading '/' */
+ my_path = strdup((path[0] == '/') ? &path[1] : path);
+ if (!my_path)
+ return 0;
+
+ n = rte_strtok(my_path, "/", argv, CLI_MAX_ARGVS);
+
+ /* handle the special case of leading '/' */
+ if (path[0] == '/')
+ dir = this_cli->root.tqh_first;
+ else
+ dir = get_cwd();
+
+ /* Follow the directory path if present */
+ for (i = 0, node = NULL; i < n; i++) {
+ node = cli_search_dir(dir, argv[i], CLI_ALL_TYPE);
+
+ if (!node)
+ break;
+
+ /* follow the next directory */
+ if (is_directory(node) && (i < n))
+ dir = node;
+ else
+ break;
+ }
+
+ free(my_path);
+
+_leave:
+ if (ret)
+ *ret = node;
+
+ return (node) ? 1 : 0;
+}
+
+struct cli_node *
+cli_last_node_in_path(const char *path)
+{
+ struct cli_node *node, *dir;
+ char *my_path = NULL;
+ char *argv[CLI_MAX_ARGVS+1];
+ int n, i;
+
+ if (!path || (*path == '\0'))
+ return get_cwd();
+
+ if (path[0] == '/' && path[1] == '\0')
+ return cli_root_node();
+
+ /* Skip the leading '/' */
+ my_path = strdup((path[0] == '/') ? &path[1] : path);
+ if (!my_path)
+ return NULL;
+
+ n = rte_strtok(my_path, "/", argv, CLI_MAX_ARGVS);
+
+ /* handle the special case of leading '/' */
+ if (path[0] == '/')
+ dir = this_cli->root.tqh_first;
+ else
+ dir = get_cwd();
+
+ /* Follow the directory path if present */
+ for (i = 0, node = NULL; i < n; i++) {
+ node = cli_search_dir(dir, argv[i], CLI_ALL_TYPE);
+
+ if (!node)
+ break;
+
+ /* follow the next directory */
+ if (is_directory(node) && (i < n))
+ dir = node;
+ else
+ break;
+ }
+
+ free(my_path);
+
+ return dir;
+}
+
+struct cli_node *
+cli_find_cmd(const char *path)
+{
+ struct cli_node *cmd, *dir;
+ int i;
+
+ if (cli_find_node(path, &cmd))
+ return cmd;
+
+ for (i = 0; i < CLI_MAX_BINS; i++) {
+ if ((dir = this_cli->bins[i]) == NULL)
+ continue;
+
+ cmd = cli_search_dir(dir, path, CLI_EXE_TYPE);
+ if (cmd)
+ return cmd;
+ }
+ return NULL;
+}
diff --git a/lib/librte_cli/cli_search.h b/lib/librte_cli/cli_search.h
new file mode 100644
index 0000000..93434e8
--- /dev/null
+++ b/lib/librte_cli/cli_search.h
@@ -0,0 +1,237 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_SEARCH_H_
+#define _CLI_SEARCH_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef union {
+ void *voidp; /** void * value */
+ char chr[8]; /** 8 characters */
+ uint64_t u64; /** 64bit value */
+ uint32_t u32[2]; /** 2 32bit value */
+ uint16_t u16[4]; /** 4 16bit values */
+} arg_u; /** Union for argument word */
+
+typedef struct {
+ arg_u arg1; /* Argument Word 1 */
+ arg_u arg2; /* Argument Word 2 */
+ arg_u arg3; /* Argument Word 3 */
+ arg_u arg4; /* Argument Word 4 */
+} args_t; /* 32 bytes of arguments */
+
+struct cli;
+struct cli_node;
+
+typedef int (*cli_scan_t)(struct cli_node *node,
+ uint32_t flags, args_t *args);
+/** typedef of function passed in cli_scan_directory() */
+
+/**
+* Scan a directory and call the func with the node found.
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param files
+* Type of nodes to find
+* @param dir
+* Node pointer to directory to scan
+* @param func
+* cli_scan_t function pointer
+* @param arg
+* void * used by the function being called.
+* @return
+* 0 on success or -1 on error
+*/
+int cli_scan_directory(struct cli_node *dir,
+ cli_scan_t func, uint32_t flags, args_t *args);
+
+/**
+* Find a node by path
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param path
+* Path to node
+* @param ret
+* Pointer to pointer of a cli_node if found
+* @return
+* 1 if found else 0
+*/
+int cli_find_node( const char *path, struct cli_node **ret);
+
+/**
+* Search the local and bin directories for a command
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param path
+* String for the command to use
+* @return
+* Pointer to the command node or NULL
+*/
+struct cli_node *cli_find_cmd(const char *path);
+
+/**
+* Count the number of type(s) of nodes available in a given node
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param n
+* node or NULL for current working directory
+* @return
+* Number of nodes found of this type in the directory
+*/
+uint32_t cli_dir_item_count(struct cli_node *node, uint32_t types);
+
+/**
+* Count the number of commands in the execute path
+*
+* @note Uses a thread variable called this_cli.
+*
+* @return
+* Number of nodes found of this type in the directory
+*/
+uint32_t cli_path_cmd_count(void);
+
+/**
+* Return a list of nodes matching given information
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param node
+* Node to start search or use the path list.
+* @param flags
+* Type of nodes to return
+* @param ret
+* Pointer to an array of pointer for return value
+* @return
+* Number of nodes found of this type in the directory
+*/
+uint32_t cli_node_list_with_type(struct cli_node *node,
+ uint32_t flags, void **ret);
+
+/**
+* Free a node back to the free list
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param node
+* Pointer to the node to free
+* @return
+* N/A
+*/
+void cli_node_list_free(void *nodes);
+
+/**
+* Count the number of commands in the execute path
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param types
+* The number of nodes to count
+* @return
+* Number of nodes found of this type in the directory
+*/
+uint32_t cli_path_item_count(uint32_t types);
+
+/**
+* Find and return the last node in a give path string
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param path
+* Path string to scan
+* @return
+* Pointer to last node in path
+*/
+struct cli_node *cli_last_node_in_path(const char *path);
+
+/**
+* Scan a directory for a given string matching name
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param dir
+* Pointer to directory node to start with in scanning
+* @param name
+* String to match the nodes with
+* @param type
+* Type of nodes to include in scan.
+* @return
+* Number of nodes found of this type in the directory
+*/
+struct cli_node *cli_search_dir(struct cli_node *dir,
+ const char *name, uint32_t type);
+
+/**
+* Scan the directory given by the path
+*
+* @note Uses a thread variable called this_cli.
+*
+* @param path
+* The path string to use
+* @param func
+* The function to call when a match is found.
+* @param flags
+* Type of files to include in match
+* @param args
+* Arguments to include with function call.
+* @return
+* Number of nodes found of this type in the directory
+*/
+int cli_scan_path(const char *path, cli_scan_t func,
+ uint32_t flags, args_t *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_SEARCH_H_ */
diff --git a/lib/librte_cli/cli_string_fns.c b/lib/librte_cli/cli_string_fns.c
new file mode 100644
index 0000000..f32b546
--- /dev/null
+++ b/lib/librte_cli/cli_string_fns.c
@@ -0,0 +1,574 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_ethdev.h>
+
+#include "cli.h"
+#include "cli_search.h"
+#include "cli_string_fns.h"
+
+#define SIZE_OF_PORTLIST (sizeof(portlist_t) * 8)
+
+char *
+rte_strtrimset(char *str, const char *set)
+{
+
+ if (!str || !*str || !set || (strlen(set) != 2))
+ return NULL;
+
+ /* Find the beginning set character, while trimming white space */
+ while ((*str == set[0]) || isspace(*str))
+ str++;
+
+ if (*str) {
+ char *p = &str[strlen(str) - 1];
+
+ while ((p >= str) && (isspace(*p) || (*p == set[1])))
+ p--;
+
+ p[1] = '\0';
+ }
+
+ return str;
+}
+
+char *
+rte_strtrim(char *str)
+{
+ if (!str || !*str)
+ return str;
+
+ /* trim white space characters at the front */
+ while(isspace(*str))
+ str++;
+
+ /* Make sure the string is not empty */
+ if (*str) {
+ char *p = &str[strlen(str) - 1];
+
+ /* trim trailing white space characters */
+ while((p >= str) && isspace(*p))
+ p--;
+
+ p[1] = '\0';
+ }
+ return str;
+}
+
+int
+rte_strtok(char *str, const char *delim, char **entries, int maxtokens)
+{
+ int i = 0;
+ char *saved;
+
+ if (!str || !delim || !entries || !maxtokens)
+ return -1;
+
+ do {
+ entries[i] = rte_strtrim(strtok_r(str, delim, &saved));
+ str = NULL;
+ } while(entries[i] && (++i < maxtokens));
+
+ return i;
+}
+
+int
+rte_strqtok(char *str, const char *delim, char *argv[], int maxtokens)
+{
+ char *p, *start_of_word, *s;
+ int c, argc = 0;
+ enum { INIT, WORD, STRING_QUOTE, STRING_TICK } state = WORD;
+
+ if (!str || !delim || !argv || maxtokens == 0)
+ return -1;
+
+ /* Remove white space from start and end of string */
+ s = rte_strtrim(str);
+
+ start_of_word = s;
+ for (p = s; (argc < maxtokens) && (*p != '\0'); p++) {
+ c = (unsigned char)*p;
+
+ if (c == '\\') {
+ start_of_word = ++p;
+ continue;
+ }
+
+ switch (state) {
+ case INIT:
+ if (c == '"') {
+ state = STRING_QUOTE;
+ start_of_word = p + 1;
+ } else if (c == '\'') {
+ state = STRING_TICK;
+ start_of_word = p + 1;
+ } else if (!strchr(delim, c)) {
+ state = WORD;
+ start_of_word = p;
+ }
+ break;
+
+ case STRING_QUOTE:
+ if (c == '"') {
+ *p = 0;
+ argv[argc++] = start_of_word;
+ state = INIT;
+ }
+ break;
+
+ case STRING_TICK:
+ if (c == '\'') {
+ *p = 0;
+ argv[argc++] = start_of_word;
+ state = INIT;
+ }
+ break;
+
+ case WORD:
+ if (strchr(delim, c)) {
+ *p = 0;
+ argv[argc++] = start_of_word;
+ state = INIT;
+ start_of_word = p + 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ((state != INIT) && (argc < maxtokens))
+ argv[argc++] = start_of_word;
+
+ if ((argc == 0) && (p != str))
+ argv[argc++] = str;
+
+ return argc;
+}
+
+#ifndef _RTE_STRING_FNS_H_
+int
+rte_strsplit(char *string, int stringlen,
+ char **tokens, int maxtokens, char delim)
+{
+ char *s, d[3];
+
+ if (stringlen <= 0)
+ return 0;
+
+ s = alloca(stringlen + 1);
+ if (s) {
+ memcpy(s, string, stringlen);
+ s[stringlen] = '\0';
+
+ snprintf(d, sizeof(d), "%c", delim);
+
+ return rte_strtok(s, d, tokens, maxtokens);
+ }
+ return -1;
+}
+#endif
+
+int
+rte_stropt(const char *list, char *str, const char *delim)
+{
+ char *argv[STR_MAX_ARGVS + 1], *buf;
+ size_t n, i;
+
+ if (!list || !str || !delim)
+ return -1;
+
+ if ((list[0] == '%') && (list[1] == '|'))
+ list += 2;
+
+ if (!*list)
+ return -1;
+
+ n = strlen(list) + 2;
+
+ buf = alloca(n);
+ if (buf) {
+ snprintf(buf, n, "%s", list);
+
+ n = rte_strtok(buf, delim, argv, STR_MAX_ARGVS);
+
+ for (i = 0; i < n; i++)
+ if (rte_strmatch(argv[i], str))
+ return i;
+ }
+
+ return -1;
+}
+
+static inline void
+set_portlist_bits(size_t low, size_t high, uint64_t *map)
+{
+ do {
+ *map |= (1LL << low++);
+ } while (low <= high);
+}
+
+#define MAX_SPLIT 64
+/* portlist = N,N,N-M,N, ... */
+int
+rte_parse_portlist(const char *str, portlist_t *portlist)
+{
+ size_t ps, pe, n, i;
+ char *split[MAX_SPLIT], *s, *p;
+ uint64_t map = 0;
+
+ if (!str || !*str)
+ return -1;
+
+ if (!strcmp(str, "all")) {
+ if (portlist) {
+ uint32_t i;
+ for (i = 0; i < SIZE_OF_PORTLIST; i++)
+ *portlist |= (1LL << i);
+ }
+ return 0;
+ }
+
+ n = strlen(str);
+ s = alloca(n + 1);
+ if (!s)
+ return -1;
+
+ memcpy(s, str, n);
+ s[n] = '\0';
+
+ n = rte_strtok(s, ",", split, MAX_SPLIT);
+ if (!n)
+ return 0;
+
+ for(i = 0; i < n; i++) {
+ p = strchr(split[i], '-');
+
+ if (!p) {
+ ps = strtoul(split[i], NULL, 10);
+ pe = ps;
+ } else {
+ *p++ = '\0';
+ ps = strtoul(split[i], NULL, 10);
+ pe = strtoul(p, NULL, 10);
+ }
+
+ if ((ps > pe) || (pe >= (sizeof(map) * 8)))
+ return -1;
+
+ set_portlist_bits(ps, pe, &map);
+ }
+
+ if (portlist)
+ *portlist = map;
+
+ return 0;
+}
+
+/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our
+ * own. */
+static int
+isblank2(char c)
+{
+ if (c == ' ' || c == '\t' )
+ return 1;
+ return 0;
+}
+
+static int
+isendofline(char c)
+{
+ if (c == '\n' || c == '\r' )
+ return 1;
+ return 0;
+}
+
+static int
+iscomment(char c)
+{
+ if (c == '#')
+ return 1;
+ return 0;
+}
+
+static int
+rte_isendoftoken(char c)
+{
+ if (!c || iscomment(c) || isblank2(c) || isendofline(c))
+ return 1;
+ return 0;
+}
+
+/*
+ * Like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ * 1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+ static const char digits[] = "0123456789";
+ int saw_digit, octets, ch;
+ unsigned char tmp[RTE_INADDRSZ], *tp;
+
+ saw_digit = 0;
+ octets = 0;
+ *(tp = tmp) = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr(digits, ch)) != NULL) {
+ unsigned int new = *tp * 10 + (pch - digits);
+
+ if (new > 255)
+ return 0;
+ if (!saw_digit) {
+ if (++octets > 4)
+ return 0;
+ saw_digit = 1;
+ }
+ *tp = (unsigned char)new;
+ } else if (ch == '.' && saw_digit) {
+ if (octets == 4)
+ return 0;
+ *++tp = 0;
+ saw_digit = 0;
+ } else
+ return 0;
+ }
+ if (octets < 4)
+ return 0;
+
+ memcpy(dst, tmp, RTE_INADDRSZ);
+ return 1;
+}
+
+/*
+ * Convert presentation level address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ unsigned char tmp[RTE_IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0;
+ const char *xdigits = 0, *curtok = 0;
+ int ch = 0, saw_xdigit = 0, count_xdigit = 0;
+ unsigned int val = 0;
+ unsigned dbloct_count = 0;
+
+ memset((tp = tmp), '\0', RTE_IN6ADDRSZ);
+ endp = tp + RTE_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return 0;
+ curtok = src;
+ saw_xdigit = count_xdigit = 0;
+ val = 0;
+
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ if (count_xdigit >= 4)
+ return 0;
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return 0;
+ saw_xdigit = 1;
+ count_xdigit++;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return 0;
+ colonp = tp;
+ continue;
+ } else if (*src == '\0')
+ return 0;
+ if (tp + sizeof(int16_t) > endp)
+ return 0;
+ *tp++ = (unsigned char) ((val >> 8) & 0xff);
+ *tp++ = (unsigned char) (val & 0xff);
+ saw_xdigit = 0;
+ count_xdigit = 0;
+ val = 0;
+ dbloct_count++;
+ continue;
+ }
+ if (ch == '.' && ((tp + RTE_INADDRSZ) <= endp) &&
+ inet_pton4(curtok, tp) > 0) {
+ tp += RTE_INADDRSZ;
+ saw_xdigit = 0;
+ dbloct_count += 2;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return 0;
+ }
+ if (saw_xdigit) {
+ if (tp + sizeof(int16_t) > endp)
+ return 0;
+ *tp++ = (unsigned char) ((val >> 8) & 0xff);
+ *tp++ = (unsigned char) (val & 0xff);
+ dbloct_count++;
+ }
+ if (colonp != NULL) {
+ /* if we already have 8 double octets, having a colon means error */
+ if (dbloct_count == 8)
+ return 0;
+
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[-i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return 0;
+ memcpy(dst, tmp, RTE_IN6ADDRSZ);
+ return 1;
+}
+
+/*
+ * Convert from presentation format (which usually means ASCII printable)
+ * to network format (which is usually some kind of binary format).
+ * @return:
+ * 1 if the address was valid for the specified address family
+ * 0 if the address wasn't valid (`dst' is untouched in this case)
+ * -1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ * Paul Vixie, 1996.
+ */
+static int
+inet_pton(int af, const char *src, void *dst)
+{
+ switch (af) {
+ case AF_INET:
+ return inet_pton4(src, dst);
+ case AF_INET6:
+ return inet_pton6(src, dst);
+ default:
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+ /* NOTREACHED */
+}
+
+int
+rte_atoip(const char *buf, int flags, void *res, unsigned ressize)
+{
+ unsigned int token_len = 0;
+ char ip_str[INET6_ADDRSTRLEN+4+1]; /* '+4' is for prefixlen (if any) */
+ struct rte_ipaddr ipaddr;
+ char *prefix, *prefix_end;
+ long prefixlen = 0;
+
+ if (res && ressize < sizeof(struct rte_ipaddr))
+ return -1;
+
+ if (!buf || !*buf)
+ return -1;
+
+ while (!rte_isendoftoken(buf[token_len]))
+ token_len++;
+
+ /* if token is too big... */
+ if (token_len >= INET6_ADDRSTRLEN+4)
+ return -1;
+
+ snprintf(ip_str, token_len+1, "%s", buf);
+
+ /* convert the network prefix */
+ if (flags & RTE_IPADDR_NETWORK) {
+ prefix = strrchr(ip_str, '/');
+ if (prefix == NULL)
+ return -1;
+ *prefix = '\0';
+ prefix++;
+ errno = 0;
+ prefixlen = strtol(prefix, &prefix_end, 10);
+ if (errno || (*prefix_end != '\0')
+ || prefixlen < 0 || prefixlen > RTE_PREFIXMAX)
+ return -1;
+ ipaddr.prefixlen = prefixlen;
+ } else
+ ipaddr.prefixlen = 0;
+
+ /* convert the IP addr */
+ if ((flags & RTE_IPADDR_V4) &&
+ inet_pton(AF_INET, ip_str, &ipaddr.ipv4) == 1 &&
+ prefixlen <= RTE_V4PREFIXMAX) {
+ ipaddr.family = AF_INET;
+ if (res)
+ memcpy(res, &ipaddr, sizeof(ipaddr));
+ return token_len;
+ }
+
+ if ((flags & RTE_IPADDR_V6) &&
+ inet_pton(AF_INET6, ip_str, &ipaddr.ipv6) == 1) {
+ ipaddr.family = AF_INET6;
+ if (res)
+ memcpy(res, &ipaddr, sizeof(ipaddr));
+ return token_len;
+ }
+ return -1;
+
+}
diff --git a/lib/librte_cli/cli_string_fns.h b/lib/librte_cli/cli_string_fns.h
new file mode 100644
index 0000000..035723c
--- /dev/null
+++ b/lib/librte_cli/cli_string_fns.h
@@ -0,0 +1,314 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *
+ * String-related functions as replacement for libc equivalents
+ */
+
+#ifndef _CLI_STRING_FNS_H_
+#define _CLI_STRING_FNS_H_
+
+#include <stdio.h>
+#include <netinet/in.h>
+#include <rte_string_fns.h>
+#include <rte_ether.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RTE_IPADDR_V4 0x01
+#define RTE_IPADDR_V6 0x02
+#define RTE_IPADDR_NETWORK 0x04
+
+#define RTE_INADDRSZ 4
+#define RTE_IN6ADDRSZ 16
+#define RTE_PREFIXMAX 128
+#define RTE_V4PREFIXMAX 32
+
+struct rte_ipaddr {
+ uint8_t family;
+ union {
+ struct in_addr ipv4;
+ struct in6_addr ipv6;
+ };
+ unsigned int prefixlen; /* in case of network only */
+};
+
+enum {
+ STR_MAX_ARGVS = 64, /**< Max number of args to support */
+ STR_TOKEN_SIZE = 128,
+};
+
+typedef uint64_t portlist_t;
+
+#ifndef _RTE_STRING_FNS_H_
+/**
+ * Takes string <string> parameter and splits it at character <delim>
+ * up to maxtokens-1 times - to give <maxtokens> resulting tokens. Like
+ * strtok or strsep functions, this modifies its input string, by replacing
+ * instances of <delim> with '\\0'. All resultant tokens are returned in the
+ * <tokens> array which must have enough entries to hold <maxtokens>.
+ *
+ * @param string
+ * The input string to be split into tokens
+ * @param stringlen
+ * The max length of the input buffer
+ * @param tokens
+ * The array to hold the pointers to the tokens in the string
+ * @param maxtokens
+ * The number of elements in the tokens array. At most, <maxtokens>-1 splits
+ * of the string will be done.
+ * @param delim
+ * The character on which the split of the data will be done
+ * @return
+ * The number of tokens in the array.
+ */
+int
+rte_strsplit(char *string, int stringlen,
+ char **tokens, int maxtokens, char delim);
+#endif
+
+/**
+ * Trim a set of characters like "[]" or "{}" from the start and end of string.
+ *
+ * @param str
+ * A null terminated string to be trimmed.
+ * @param set
+ * The <set> string is a set of two character values to be removed from the
+ * <str>. Removes only one set at a time, if you have more then one set to
+ * remove then you must call the routine for each set. The <set> string must
+ * be two characters and can be any characters you
+ * want to call a set.
+ * @return
+ * Pointer to the trimmed string or NULL on error
+ */
+char *rte_strtrimset(char *str, const char *set);
+
+/**
+ * Remove leading and trailing white space from a string.
+ *
+ * @param str
+ * String to be trimmed, must be null terminated
+ * @return
+ * pointer to the trimmed string or NULL if <str> is Null or
+ * if string is empty then return pointer to <str>
+ */
+char *rte_strtrim(char *str);
+
+/**
+ * Parse a string into a argc/argv list using a set of delimiters, but does
+ * not handle quoted strings within the string being parsed.
+ *
+ * @param str
+ * String to be tokenized and will be modified, must be null terminated
+ * @param delim
+ * A null terminated list of delimitors
+ * @param entries
+ * A pointer to an array to place the token pointers
+ * @param max_entries
+ * Max number of tokens to be placed in <entries>
+ * @return
+ * The number of tokens in the <entries> array.
+ */
+int rte_strtok(char *str, const char *delim, char **entries, int maxtokens);
+
+/**
+ * Parse a string into a argc/argv list using a set of delimiters, but does
+ * handle quoted strings within the string being parsed
+ *
+ * @param str
+ * String to be tokenized and will be modified, null terminated
+ * @param delim
+ * A null terminated list of delimitors
+ * @param entries
+ * A pointer to an array to place the token pointers
+ * @param max_entries
+ * Max number of tokens to be placed in <entries>
+ * @return
+ * The number of tokens in the <entries> array.
+ */
+int rte_strqtok(char *str, const char *delim, char **entries, int maxtokens);
+
+/**
+ * Parse a string <list> looking for <str> using delim character.
+ *
+ * @param list
+ * A string list of options with delim character between them.
+ * @param str
+ * String to search for in <list>
+ * @param delim
+ * A character string to use as a delim values
+ * @return
+ * The index in the list of option strings, -1 if not found
+ */
+int rte_stropt(const char *list, char *str, const char *delim);
+
+/**
+ * Helper routine to compare two strings exactly
+ *
+ * @param s1
+ * Pointer to first string.
+ * @param s2
+ * Pointer to second string.
+ * @return
+ * 0 failed to compare and 1 strings are equal.
+ */
+static inline int
+rte_strmatch(const char * s1, const char * s2)
+{
+ if (!s1 || !s2)
+ return 0;
+
+ while((*s1 != '\0') && (*s2 != '\0')) {
+ if (*s1++ != *s2++)
+ return 0;
+ }
+ if (*s1 != *s2)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Count the number of <c> characters in a string <s>
+ *
+ * @param s
+ * Null terminated string to search
+ * @param c
+ * character to count
+ * @return
+ * Number of times the character is in string.
+ */
+static inline int
+rte_strcnt(char *s, char c)
+{
+ return (s == NULL || *s == '\0')
+ ? 0
+ : rte_strcnt(s + 1, c) + (*s == c);
+}
+
+/**
+ * Parse a portlist string into a mask or bitmap value.
+ *
+ * @param str
+ * String to parse
+ * @param portlist
+ * Pointer to uint64_t value for returned bitmap
+ * @return
+ * -1 on error or 0 on success.
+ */
+int rte_parse_portlist(const char *str, portlist_t *portlist);
+
+/**
+ * Convert a string Ethernet MAC address to the binary form
+ *
+ * @param a
+ * String containing the MAC address in two forms
+ * XX:XX:XX:XX:XX:XX or XXXX:XXXX:XXX
+ * @param e
+ * pointer to a struct ether_addr to place the return value. If the value
+ * is null then use a static location instead.
+ * @return
+ * Pointer to the struct ether_addr structure;
+ */
+static inline struct ether_addr *
+rte_ether_aton(const char *a, struct ether_addr *e)
+{
+ int i;
+ char *end;
+ unsigned long o[ETHER_ADDR_LEN];
+ static struct ether_addr ether_addr;
+
+ if (!e)
+ e = &ether_addr;
+
+ i = 0;
+ do {
+ errno = 0;
+ o[i] = strtoul(a, &end, 16);
+ if (errno != 0 || end == a || (end[0] != ':' && end[0] != 0))
+ return NULL;
+ a = end + 1;
+ } while (++i != sizeof (o) / sizeof (o[0]) && end[0] != 0);
+
+ /* Junk at the end of line */
+ if (end[0] != 0)
+ return NULL;
+
+ /* Support the format XX:XX:XX:XX:XX:XX */
+ if (i == ETHER_ADDR_LEN) {
+ while (i-- != 0) {
+ if (o[i] > UINT8_MAX)
+ return NULL;
+ e->addr_bytes[i] = (uint8_t)o[i];
+ }
+ /* Support the format XXXX:XXXX:XXXX */
+ } else if (i == ETHER_ADDR_LEN / 2) {
+ while (i-- != 0) {
+ if (o[i] > UINT16_MAX)
+ return NULL;
+ e->addr_bytes[i * 2] = (uint8_t)(o[i] >> 8);
+ e->addr_bytes[i * 2 + 1] = (uint8_t)(o[i] & 0xff);
+ }
+ /* unknown format */
+ } else
+ return NULL;
+
+ return e;
+}
+
+
+/**
+ * Convert an IPv4/v6 address into a binary value.
+ *
+ * @param buf
+ * Location of string to convert
+ * @param flags
+ * Set of flags for converting IPv4/v6 addresses and netmask.
+ * @param res
+ * Location to put the results
+ * @param ressize
+ * Length of res in bytes.
+ * @return
+ * 0 on OK and -1 on error
+ */
+int rte_atoip(const char *buf, int flags, void *res, unsigned ressize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_STRING_FNS_H */
diff --git a/lib/librte_cli/cli_vt100.c b/lib/librte_cli/cli_vt100.c
new file mode 100644
index 0000000..aa2465e
--- /dev/null
+++ b/lib/librte_cli/cli_vt100.c
@@ -0,0 +1,150 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cli.h"
+#include "cli_vt100.h"
+#include "cli_vt100_keys.h"
+
+static int
+vt100_find_cmd(char *buf, unsigned int size)
+{
+ struct vt100_cmds *cmd;
+ size_t cmdlen;
+ int i;
+
+ for (i = 0, cmd = vt100_cmd_list; cmd->str; cmd++, i++) {
+ cmdlen = strnlen(cmd->str, VT100_BUF_SIZE);
+ if ((size == cmdlen) && !strncmp(buf, cmd->str, cmdlen))
+ return i;
+ }
+
+ return VT100_DONE;
+}
+
+int
+vt100_parse_input(struct cli_vt100 *vt, uint8_t c)
+{
+ uint32_t size;
+
+ RTE_ASSERT(vt != NULL);
+
+ if ((vt->bufpos == VT100_INITIALIZE) ||
+ (vt->bufpos >= VT100_BUF_SIZE)) {
+ vt->state = VT100_INIT;
+ vt->bufpos = 0;
+ }
+
+ vt->buf[vt->bufpos++] = c;
+ size = vt->bufpos;
+
+ switch (vt->state) {
+ case VT100_INIT:
+ if (c == vt100_escape)
+ vt->state = VT100_ESCAPE;
+ else {
+ vt->bufpos = VT100_INITIALIZE;
+ return vt100_find_cmd(vt->buf, size);
+ }
+ break;
+
+ case VT100_ESCAPE:
+ if (c == vt100_open_square)
+ vt->state = VT100_ESCAPE_CSI;
+ else if (c >= '0' && c <= vt100_del) {
+ vt->bufpos = VT100_INITIALIZE;
+ return vt100_find_cmd(vt->buf, size);
+ }
+ break;
+
+ case VT100_ESCAPE_CSI:
+ if (c >= '@' && c <= '~') {
+ vt->bufpos = VT100_INITIALIZE;
+ return vt100_find_cmd(vt->buf, size);
+ }
+ break;
+
+ default:
+ vt->bufpos = VT100_INITIALIZE;
+ break;
+ }
+
+ return VT100_CONTINUE;
+}
+
+struct cli_vt100 *
+vt100_create(void)
+{
+ struct cli_vt100 *vt;
+
+ vt = calloc(1, sizeof(struct cli_vt100));
+ if (!vt)
+ return NULL;
+
+ vt->bufpos = -1;
+
+ return vt;
+}
+
+void
+vt100_destroy(struct cli_vt100 *vt)
+{
+ if (vt)
+ free(vt);
+}
diff --git a/lib/librte_cli/cli_vt100.h b/lib/librte_cli/cli_vt100.h
new file mode 100644
index 0000000..116dc86
--- /dev/null
+++ b/lib/librte_cli/cli_vt100.h
@@ -0,0 +1,290 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _VT100_H_
+#define _VT100_H_
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <cli_scrn.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VT100_INITIALIZE -1
+
+#define vt100_open_square '['
+#define vt100_escape 0x1b
+#define vt100_del 0x7f
+#define vt100_q_escape "\033"
+
+/* Key codes */
+#define vt100_up_arr "\033[A"
+#define vt100_down_arr "\033[B"
+#define vt100_right_arr "\033[C"
+#define vt100_left_arr "\033[D"
+#define vt100_word_left "\033b"
+#define vt100_word_right "\033f"
+#define vt100_suppr "\033[3~"
+#define vt100_tab "\011"
+
+/* Action codes for cli_vt100 */
+#define vt100_bell "\007"
+#define vt100_bs "\010"
+#define vt100_bs_clear "\b \b"
+#define vt100_crnl "\012\015"
+#define vt100_home "\033M\033E"
+
+/* cursor codes */
+#define vt100_pos "\033[%d;%dH"
+#define vt100_setw "\033[%d;r"
+#define vt100_save_cursor "\0337"
+#define vt100_restore_cursor "\0338"
+#define vt100_clear_right "\033[0K"
+#define vt100_clear_left "\033[1K"
+#define vt100_clear_down "\033[0J"
+#define vt100_clear_up "\033[1J"
+#define vt100_clear_line "\033[2K"
+#define vt100_clear_screen "\033[2J"
+#define vt100_pos_cursor "\033[%d;%dH"
+#define vt100_multi_right "\033\133%uC"
+#define vt100_multi_left "\033\133%uD"
+
+/* Result of parsing : it must be synchronized with
+ * vt100_commands[] in vt100_keys.c */
+enum {
+ VT100_INVALID_KEY = 0,
+ VT100_KEY_UP_ARR,
+ VT100_KEY_DOWN_ARR,
+ VT100_KEY_RIGHT_ARR,
+ VT100_KEY_LEFT_ARR,
+ VT100_KEY_BKSPACE,
+ VT100_KEY_RETURN,
+ VT100_KEY_CTRL_A,
+ VT100_KEY_CTRL_E,
+ VT100_KEY_CTRL_K,
+ VT100_KEY_CTRL_Y,
+ VT100_KEY_CTRL_C,
+ VT100_KEY_CTRL_F,
+ VT100_KEY_CTRL_B,
+ VT100_KEY_SUPPR,
+ VT100_KEY_TAB,
+ VT100_KEY_CTRL_D,
+ VT100_KEY_CTRL_L,
+ VT100_KEY_RETURN2,
+ VT100_KEY_META_BKSPACE,
+ VT100_KEY_WLEFT,
+ VT100_KEY_WRIGHT,
+ VT100_KEY_CTRL_W,
+ VT100_KEY_CTRL_P,
+ VT100_KEY_CTRL_N,
+ VT100_KEY_META_D,
+ VT100_KEY_CTRL_X,
+ VT100_MAX_KEYS
+};
+
+extern const char *vt100_commands[];
+
+enum vt100_parse_state {
+ VT100_INIT,
+ VT100_ESCAPE,
+ VT100_ESCAPE_CSI,
+ VT100_DONE = -1,
+ VT100_CONTINUE = -2
+};
+
+#define VT100_BUF_SIZE 8
+struct cli_vt100 {
+ int bufpos; /** Current offset into buffer */
+ char buf[VT100_BUF_SIZE]; /** cli_vt100 command buffer */
+ enum vt100_parse_state state; /** current cli_vt100 parser state */
+};
+
+/** A single byte to hold port of a Red/Green/Blue color value */
+typedef uint8_t scrn_rgb_t;
+
+/** Macro to reduce typing and screen clutter */
+#define cli_puts(...) scrn_puts(__VA_ARGS__)
+
+/** Set the foreground color + attribute at the current cursor position */
+static __inline__ void
+vt100_fgcolor(scrn_color_e color, scrn_attr_e attr)
+{
+ cli_puts("\033[%d;%dm", attr, color + 30);
+}
+
+/** Set the background color + attribute at the current cursor position */
+static __inline__ void
+vt100_bgcolor(scrn_color_e color, scrn_attr_e attr)
+{
+ cli_puts("\033[%d;%dm", attr, color + 40);
+}
+
+/** Set the foreground/background color + attribute at the current cursor position */
+static __inline__ void
+vt100_fgbgcolor(scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)
+{
+ cli_puts("\033[%d;%d;%dm", attr, fg + 30, bg + 40);
+}
+
+/**
+ * Main routine to set color for foreground and background and attribute at
+ * the current position.
+ */
+static __inline__ void
+vt100_color(scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)
+{
+ if ( (fg != SCRN_NO_CHANGE) && (bg != SCRN_NO_CHANGE))
+ vt100_fgbgcolor(fg, bg, attr);
+ else if (fg == SCRN_NO_CHANGE)
+ vt100_bgcolor(bg, attr);
+ else if (bg == SCRN_NO_CHANGE)
+ vt100_fgcolor(fg, attr);
+}
+
+/** Setup for 256 RGB color methods. A routine to output RGB color codes if supported */
+static __inline__ void
+vt100_rgb(uint8_t fg_bg, scrn_rgb_t r, scrn_rgb_t g, scrn_rgb_t b)
+{
+ cli_puts("\033[%d;2;%d;%d;%dm", fg_bg, r, g, b);
+}
+
+/** Set the foreground color + attribute at the current cursor position */
+static __inline__ int
+vt100_fgcolor_str(char *str, scrn_color_e color, scrn_attr_e attr)
+{
+ return snprintf(str, 16, "\033[%d;%dm", attr, color + 30);
+}
+
+/** Set the background color + attribute at the current cursor position */
+static __inline__ int
+vt100_bgcolor_str(char *str, scrn_color_e color, scrn_attr_e attr)
+{
+ return snprintf(str, 16, "\033[%d;%dm", attr, color + 40);
+}
+
+/** Set the foreground/background color + attribute at the current cursor position */
+static __inline__ int
+vt100_fgbgcolor_str(char *str, scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)
+{
+ return snprintf(str, 16, "\033[%d;%d;%dm", attr, fg + 30, bg + 40);
+}
+
+/**
+ * Main routine to set color for foreground and background and attribute at
+ * the current position.
+ */
+static __inline__ int
+vt100_color_str(char *str, scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)
+{
+ if ( (fg != SCRN_NO_CHANGE) && (bg != SCRN_NO_CHANGE))
+ return vt100_fgbgcolor_str(str, fg, bg, attr);
+ else if (fg == SCRN_NO_CHANGE)
+ return vt100_bgcolor_str(str, bg, attr);
+ else if (bg == SCRN_NO_CHANGE)
+ return vt100_fgcolor_str(str, fg, attr);
+ else
+ return 0;
+}
+
+/** Setup for 256 RGB color methods. A routine to output RGB color codes if supported */
+static __inline__ int
+vt100_rgb_str(char *str, uint8_t fg_bg, scrn_rgb_t r, scrn_rgb_t g, scrn_rgb_t b)
+{
+ return snprintf(str, 16, "\033[%d;2;%d;%d;%dm", fg_bg, r, g, b);
+}
+
+/**
+ * Create the cli_vt100 structure
+ *
+ * @return
+ * Pointer to cli_vt100 structure or NULL on error
+ */
+struct cli_vt100 *vt100_create(void);
+
+/**
+ * Destroy the cli_vt100 structure
+ *
+ * @param
+ * The pointer to the cli_vt100 structure.
+ */
+void vt100_destroy(struct cli_vt100 *vt);
+
+/**
+ * Input a new character.
+ *
+ * @param vt
+ * The pointer to the cli_vt100 structure
+ * @param c
+ * The character to parse for cli_vt100 commands
+ * @return
+ * -1 if the character is not part of a control sequence
+ * -2 if c is not the last char of a control sequence
+ * Else the index in vt100_commands[]
+ */
+int vt100_parse_input(struct cli_vt100 *vt, uint8_t c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VT100_H_ */
diff --git a/lib/librte_cli/cli_vt100_keys.c b/lib/librte_cli/cli_vt100_keys.c
new file mode 100644
index 0000000..1a87531
--- /dev/null
+++ b/lib/librte_cli/cli_vt100_keys.c
@@ -0,0 +1,304 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cli.h"
+#include "cli_vt100_keys.h"
+#include "cli_auto_complete.h"
+
+static inline void
+key_ctrl_x(void)
+{
+ this_cli->quit_flag = 1;
+}
+
+static inline void
+key_up_arr(void)
+{
+ char *line;
+
+ line = cli_history_prev();
+ if (line) {
+ gb_reset_buf(this_cli->gb);
+ cli_clear_line(-1);
+ gb_str_insert(this_cli->gb, line, strlen(line));
+ cli_display_line();
+ }
+}
+
+static inline void
+key_down_arr(void)
+{
+ char *line;
+
+ line = cli_history_next();
+ if (line) {
+ gb_reset_buf(this_cli->gb);
+ cli_clear_line(-1);
+ gb_str_insert(this_cli->gb, line, strlen(line));
+ cli_display_line();
+ }
+}
+
+static inline void
+key_right_arr(void)
+{
+ if (!gb_eof(this_cli->gb)) {
+ cli_write(vt100_right_arr, -1);
+ gb_move_right(this_cli->gb);
+ }
+}
+
+static inline void
+key_left_arr(void)
+{
+ if (!gb_point_at_start(this_cli->gb)) {
+ cli_write(vt100_left_arr, -1);
+ gb_move_left(this_cli->gb);
+ }
+}
+
+static inline void
+key_backspace(void)
+{
+ struct gapbuf *gb = this_cli->gb;
+
+ if (!gb_point_at_start(gb)) {
+ cli_cursor_left();
+ cli_save_cursor();
+
+ gb_move_left(gb);
+
+ gb_del(gb, 1);
+
+ cli_display_right();
+ cli_write(" ", 1);
+
+ cli_restore_cursor();
+ }
+}
+
+static inline void
+key_return(void)
+{
+ cli_write("\n", 1);
+
+ cli_execute();
+
+ /* Init buffer must be after execute of command */
+ gb_reset_buf(this_cli->gb);
+
+ /* Found quit command */
+ if (!this_cli->quit_flag)
+ this_cli->prompt(0);
+}
+
+static inline void
+key_ctrl_a(void)
+{
+ cli_printf(vt100_multi_left, gb_left_data_size(this_cli->gb));
+ gb_set_point(this_cli->gb, 0);
+}
+
+static inline void
+key_ctrl_e(void)
+{
+ cli_printf(vt100_multi_right, gb_right_data_size(this_cli->gb));
+ gb_set_point(this_cli->gb, -1);
+}
+
+static inline void
+key_ctrl_k(void)
+{
+ struct cli *cli = this_cli;
+
+ cli_clear_to_eol();
+ gb_move_gap_to_point(cli->gb);
+ free(cli->kill);
+ if (gb_right_data_size(cli->gb))
+ cli->kill = strndup(gb_end_of_gap(cli->gb),
+ gb_right_data_size(cli->gb) + 1);
+ gb_del(cli->gb, gb_right_data_size(cli->gb));
+}
+
+static inline void
+key_ctrl_y(void)
+{
+ struct cli *cli = this_cli;
+
+ /* Yank and put are Not supported yet */
+ if (cli->kill) {
+ gb_str_insert(cli->gb, cli->kill, strlen(cli->kill));
+ cli_clear_line(-1);
+ cli_display_line();
+ }
+}
+
+static inline void
+key_ctrl_c(void)
+{
+ gb_reset_buf(this_cli->gb);
+ cli_clear_line(-1);
+ this_cli->prompt(0);
+}
+
+static inline void
+key_ctrl_f(void)
+{
+ key_right_arr();
+}
+
+static inline void
+key_ctrl_b(void)
+{
+ key_left_arr();
+}
+
+static inline void
+key_suppr(void)
+{
+ gb_del(this_cli->gb, 1);
+ cli_display_right();
+ cli_clear_to_eol();
+}
+
+static inline void
+key_tab(void)
+{
+ cli_auto_complete();
+}
+
+static inline void
+key_ctrl_d(void)
+{
+ gb_dump(this_cli->gb, NULL);
+}
+
+static inline void
+key_ctrl_l(void)
+{
+ cli_clear_screen();
+ cli_clear_line(-1);
+ cli_display_line();
+}
+
+static inline void
+key_return2(void)
+{
+ key_return();
+}
+
+static inline void
+key_meta_backspace(void)
+{
+}
+
+/* meta+b or command+b or window+b or super+b */
+static inline void
+key_word_left(void)
+{
+ do {
+ key_left_arr();
+ if (gb_get_prev(this_cli->gb) == ' ')
+ break;
+ } while (!gb_point_at_start(this_cli->gb));
+}
+
+static inline void
+key_word_right(void)
+{
+ while (!gb_point_at_end(this_cli->gb)) {
+ key_right_arr();
+ if (gb_get(this_cli->gb) == ' ')
+ break;
+ }
+}
+
+static inline void
+key_ctrl_w(void)
+{
+ key_meta_backspace();
+}
+
+static inline void
+key_ctrl_p(void)
+{
+ key_up_arr();
+}
+
+static inline void
+key_ctrl_n(void)
+{
+ key_down_arr();
+}
+
+static inline void
+key_meta_d(void)
+{
+}
+
+static inline void
+key_invalid(void)
+{
+}
+
+/* Order must be maintained see cli_vt100.h */
+struct vt100_cmds vt100_cmd_list[] = {
+ { "Invalid", key_invalid },
+ { vt100_up_arr, key_up_arr }, /* Move cursor up one line */
+ { vt100_down_arr, key_down_arr }, /* Move cursor down on line */
+ { vt100_right_arr, key_right_arr }, /* Move cursor right */
+ { vt100_left_arr, key_left_arr }, /* Move cursor left */
+ { "\177", key_backspace }, /* Cursor Left + delete */
+ { "\n", key_return }, /* Execute command */
+ { "\001", key_ctrl_a }, /* Same as key_left_arr */
+ { "\005", key_ctrl_e }, /* Same as key_right_arr */
+ { "\013", key_ctrl_k }, /* Kill to end of line */
+ { "\031", key_ctrl_y }, /* Put the kill buffer not working */
+ { "\003", key_ctrl_c }, /* Reset line and start over */
+ { "\006", key_ctrl_f }, /* Same as key_right_arr */
+ { "\002", key_ctrl_b }, /* Same as key_left_arr */
+ { vt100_suppr, key_suppr }, /* delete 1 char from the left */
+ { vt100_tab, key_tab }, /* Auto complete */
+ { "\004", key_ctrl_d }, /* Debug output if enabled */
+ { "\014", key_ctrl_l }, /* redraw screen */
+ { "\r", key_return2 }, /* Same as key_return */
+ { "\033\177", key_meta_backspace }, /* Delete word left */
+ { vt100_word_left, key_word_left }, /* Word left */
+ { vt100_word_right, key_word_right }, /* Word right */
+ { "\027", key_ctrl_w }, /* Same as key_meta_backspace */
+ { "\020", key_ctrl_p }, /* Same as key_up_arr */
+ { "\016", key_ctrl_n }, /* Same as key_down_arr */
+ { "\033\144", key_meta_d }, /* Delete word right */
+ { "\030", key_ctrl_x }, /* Terminate application */
+ { NULL, NULL }
+};
diff --git a/lib/librte_cli/cli_vt100_keys.h b/lib/librte_cli/cli_vt100_keys.h
new file mode 100644
index 0000000..5baa6e1
--- /dev/null
+++ b/lib/librte_cli/cli_vt100_keys.h
@@ -0,0 +1,60 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CLI_VT100_KEYS_H_
+#define _CLI_VT100_KEYS_H_
+
+/**
+ * @file
+ * RTE Command line interface
+ *
+ */
+
+#include "cli.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct vt100_cmds {
+ const char *str;
+ void (*func)(void);
+};
+
+extern struct vt100_cmds vt100_cmd_list[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLI_VT100_KEYS_H_ */
diff --git a/lib/librte_cli/rte_cli_version.map b/lib/librte_cli/rte_cli_version.map
new file mode 100644
index 0000000..466e4c4
--- /dev/null
+++ b/lib/librte_cli/rte_cli_version.map
@@ -0,0 +1,6 @@
+DPDK_17.05 {
+ global: *;
+
+
+ local: *;
+};