diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index 25d5982..9533df1 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -224,7 +224,6 @@ struct context {
 	enum index prev; /**< Index of the last token seen. */
 	int next_num; /**< Number of entries in next[]. */
 	int args_num; /**< Number of entries in args[]. */
-	uint32_t reparse:1; /**< Start over from the beginning. */
 	uint32_t eol:1; /**< EOL has been detected. */
 	uint32_t last:1; /**< No more arguments. */
 	uint16_t port; /**< Current port ID (for completions). */
@@ -2612,7 +2611,6 @@ cmd_flow_context_init(struct context *ctx)
 	ctx->prev = ZERO;
 	ctx->next_num = 0;
 	ctx->args_num = 0;
-	ctx->reparse = 0;
 	ctx->eol = 0;
 	ctx->last = 0;
 	ctx->port = 0;
@@ -2633,9 +2631,6 @@ cmd_flow_parse(cmdline_parse_token_hdr_t *hdr, const char *src, void *result,
 	int i;
 
 	(void)hdr;
-	/* Restart as requested. */
-	if (ctx->reparse)
-		cmd_flow_context_init(ctx);
 	token = &token_list[ctx->curr];
 	/* Check argument length. */
 	ctx->eol = 0;
@@ -2711,8 +2706,6 @@ cmd_flow_complete_get_nb(cmdline_parse_token_hdr_t *hdr)
 	int i;
 
 	(void)hdr;
-	/* Tell cmd_flow_parse() that context must be reinitialized. */
-	ctx->reparse = 1;
 	/* Count number of tokens in current list. */
 	if (ctx->next_num)
 		list = ctx->next[ctx->next_num - 1];
@@ -2746,8 +2739,6 @@ cmd_flow_complete_get_elt(cmdline_parse_token_hdr_t *hdr, int index,
 	int i;
 
 	(void)hdr;
-	/* Tell cmd_flow_parse() that context must be reinitialized. */
-	ctx->reparse = 1;
 	/* Count number of tokens in current list. */
 	if (ctx->next_num)
 		list = ctx->next[ctx->next_num - 1];
@@ -2782,8 +2773,6 @@ cmd_flow_get_help(cmdline_parse_token_hdr_t *hdr, char *dst, unsigned int size)
 	const struct token *token = &token_list[ctx->prev];
 
 	(void)hdr;
-	/* Tell cmd_flow_parse() that context must be reinitialized. */
-	ctx->reparse = 1;
 	if (!size)
 		return -1;
 	/* Set token type and update global help with details. */
@@ -2809,12 +2798,12 @@ static struct cmdline_token_hdr cmd_flow_token_hdr = {
 /** Populate the next dynamic token. */
 static void
 cmd_flow_tok(cmdline_parse_token_hdr_t **hdr,
-	     cmdline_parse_token_hdr_t *(*hdrs)[])
+	     cmdline_parse_token_hdr_t **hdr_inst)
 {
 	struct context *ctx = &cmd_flow_context;
 
 	/* Always reinitialize context before requesting the first token. */
-	if (!(hdr - *hdrs))
+	if (!(hdr_inst - cmd_flow.tokens))
 		cmd_flow_context_init(ctx);
 	/* Return NULL when no more tokens are expected. */
 	if (!ctx->next_num && ctx->curr) {
diff --git a/lib/librte_cmdline/cmdline_parse.c b/lib/librte_cmdline/cmdline_parse.c
index 67e452d..56491ea 100644
--- a/lib/librte_cmdline/cmdline_parse.c
+++ b/lib/librte_cmdline/cmdline_parse.c
@@ -141,28 +141,17 @@ nb_common_chars(const char * s1, const char * s2)
 
 /** Retrieve either static or dynamic token at a given index. */
 static cmdline_parse_token_hdr_t *
-get_token(cmdline_parse_inst_t *inst,
-	  unsigned int index,
-	  cmdline_parse_token_hdr_t
-		*(*dyn_tokens)[CMDLINE_PARSE_DYNAMIC_TOKENS])
+get_token(cmdline_parse_inst_t *inst, unsigned int index)
 {
+	cmdline_parse_token_hdr_t *token_p;
+
 	/* check presence of static tokens first */
 	if (inst->tokens[0] || !inst->f)
 		return inst->tokens[index];
-	/* for dynamic tokens, make sure index does not overflow */
-	if (index >= CMDLINE_PARSE_DYNAMIC_TOKENS - 1)
-		return NULL;
-	/* in case token is already generated, return it */
-	if ((*dyn_tokens)[index])
-		return (*dyn_tokens)[index];
-	/* generate token */
-	inst->f(&(*dyn_tokens)[index], NULL, dyn_tokens);
-	/* return immediately if there are no more tokens to expect */
-	if (!(*dyn_tokens)[index])
-		return NULL;
-	/* terminate list with a NULL entry */
-	(*dyn_tokens)[index + 1] = NULL;
-	return (*dyn_tokens)[index];
+	/* generate dynamic token */
+	token_p = NULL;
+	inst->f(&token_p, NULL, &inst->tokens[index]);
+	return token_p;
 }
 
 /**
@@ -172,22 +161,20 @@ get_token(cmdline_parse_inst_t *inst,
  */
 static int
 match_inst(cmdline_parse_inst_t *inst, const char *buf,
-	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size,
-	   cmdline_parse_token_hdr_t
-		*(*dyn_tokens)[CMDLINE_PARSE_DYNAMIC_TOKENS])
+	   unsigned int nb_match_token, void *resbuf, unsigned resbuf_size)
 {
-	unsigned int token_num=0;
 	cmdline_parse_token_hdr_t * token_p;
 	unsigned int i=0;
 	int n = 0;
 	struct cmdline_token_hdr token_hdr;
 
-	token_p = get_token(inst, token_num, dyn_tokens);
-	if (token_p)
+	/* check if we match all tokens of inst */
+	while (!nb_match_token || i < nb_match_token) {
+		token_p = get_token(inst, i);
+		if (!token_p)
+			break;
 		memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
-	/* check if we match all tokens of inst */
-	while (token_p && (!nb_match_token || i<nb_match_token)) {
 		debug_printf("TK\n");
 		/* skip spaces */
 		while (isblank2(*buf)) {
@@ -222,11 +209,6 @@ match_inst(cmdline_parse_inst_t *inst, const char *buf,
 		debug_printf("TK parsed (len=%d)\n", n);
 		i++;
 		buf += n;
-
-		token_num ++;
-		token_p = get_token(inst, token_num, dyn_tokens);
-		if (token_p)
-			memcpy(&token_hdr, token_p, sizeof(token_hdr));
 	}
 
 	/* does not match */
@@ -270,7 +252,6 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 		char buf[CMDLINE_PARSE_RESULT_BUFSIZE];
 		long double align; /* strong alignment constraint for buf */
 	} result, tmp_result;
-	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	void (*f)(void *, struct cmdline *, void *) = NULL;
 	void *data = NULL;
 	int comment = 0;
@@ -327,13 +308,12 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 
 	/* parse it !! */
 	inst = ctx[inst_num];
-	dyn_tokens[0] = NULL;
 	while (inst) {
 		debug_printf("INST %d\n", inst_num);
 
 		/* fully parsed */
 		tok = match_inst(inst, buf, 0, tmp_result.buf,
-				 sizeof(tmp_result.buf), &dyn_tokens);
+				 sizeof(tmp_result.buf));
 
 		if (tok > 0) /* we matched at least one token */
 			err = CMDLINE_PARSE_BAD_ARGS;
@@ -365,7 +345,6 @@ cmdline_parse(struct cmdline *cl, const char * buf)
 
 		inst_num ++;
 		inst = ctx[inst_num];
-		dyn_tokens[0] = NULL;
 	}
 
 	/* call func */
@@ -392,7 +371,6 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 	cmdline_parse_token_hdr_t *token_p;
 	struct cmdline_token_hdr token_hdr;
 	char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE];
-	cmdline_parse_token_hdr_t *dyn_tokens[CMDLINE_PARSE_DYNAMIC_TOKENS];
 	unsigned int partial_tok_len;
 	int comp_len = -1;
 	int tmp_len = -1;
@@ -432,16 +410,14 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 		nb_non_completable = 0;
 
 		inst = ctx[inst_num];
-		dyn_tokens[0] = NULL;
 		while (inst) {
 			/* parse the first tokens of the inst */
 			if (nb_token &&
-			    match_inst(inst, buf, nb_token, NULL, 0,
-				       &dyn_tokens))
+			    match_inst(inst, buf, nb_token, NULL, 0))
 				goto next;
 
 			debug_printf("instruction match\n");
-			token_p = get_token(inst, nb_token, &dyn_tokens);
+			token_p = get_token(inst, nb_token);
 			if (token_p)
 				memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
@@ -495,7 +471,6 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 			debug_printf("next\n");
 			inst_num ++;
 			inst = ctx[inst_num];
-			dyn_tokens[0] = NULL;
 		}
 
 		debug_printf("total choices %d for this completion\n",
@@ -528,16 +503,15 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 
 	inst_num = 0;
 	inst = ctx[inst_num];
-	dyn_tokens[0] = NULL;
 	while (inst) {
 		/* we need to redo it */
 		inst = ctx[inst_num];
 
 		if (nb_token &&
-		    match_inst(inst, buf, nb_token, NULL, 0, &dyn_tokens))
+		    match_inst(inst, buf, nb_token, NULL, 0))
 			goto next2;
 
-		token_p = get_token(inst, nb_token, &dyn_tokens);
+		token_p = get_token(inst, nb_token);
 		if (token_p)
 			memcpy(&token_hdr, token_p, sizeof(token_hdr));
 
@@ -610,7 +584,6 @@ cmdline_complete(struct cmdline *cl, const char *buf, int *state,
 	next2:
 		inst_num ++;
 		inst = ctx[inst_num];
-		dyn_tokens[0] = NULL;
 	}
 	return 0;
 }
diff --git a/lib/librte_cmdline/cmdline_parse.h b/lib/librte_cmdline/cmdline_parse.h
index 65b18d4..13e086f 100644
--- a/lib/librte_cmdline/cmdline_parse.h
+++ b/lib/librte_cmdline/cmdline_parse.h
@@ -83,9 +83,6 @@ extern "C" {
 /* maximum buffer size for parsed result */
 #define CMDLINE_PARSE_RESULT_BUFSIZE 8192
 
-/* maximum number of dynamic tokens */
-#define CMDLINE_PARSE_DYNAMIC_TOKENS 128
-
 /**
  * Stores a pointer to the ops struct, and the offset: the place to
  * write the parsed result in the destination structure.
@@ -137,20 +134,53 @@ struct cmdline;
  * When no tokens are defined (tokens[0] == NULL), they are retrieved
  * dynamically by calling f() as follows:
  *
- *  f((struct cmdline_token_hdr **)&token_hdr,
- *    NULL,
- *    (struct cmdline_token_hdr *[])tokens));
+ * @code
+ *
+ * f((struct cmdline_token_hdr **)&token_p,
+ *   NULL,
+ *   (struct cmdline_token_hdr **)&inst->tokens[num]);
+ *
+ * @endcode
  *
  * The address of the resulting token is expected at the location pointed by
  * the first argument. Can be set to NULL to end the list.
  *
  * The cmdline argument (struct cmdline *) is always NULL.
  *
- * The last argument points to the NULL-terminated list of dynamic tokens
- * defined so far. Since token_hdr points to an index of that list, the
- * current index can be derived as follows:
+ * The last argument points to the inst->tokens[] entry to retrieve, which
+ * is not necessarily inside allocated memory and should neither be read nor
+ * written. Its sole purpose is to deduce the token entry index of interest
+ * as described in the example below.
+ *
+ * Note about constraints:
+ *
+ * - Only the address of these tokens is dynamic, their storage should be
+ *   static like normal tokens.
+ * - Dynamic token lists that need to maintain an internal context (e.g. in
+ *   order to determine the next token) must store it statically also. This
+ *   context must be reinitialized when the first token is requested, that
+ *   is, when &inst->tokens[0] is provided as the third argument.
+ * - Dynamic token lists must be NULL-terminated to generate usable
+ *   commands.
+ *
+ * @code
+ *
+ * // Assuming first and third arguments are respectively named "token_p"
+ * // and "token":
+ *
+ * int index = token - inst->tokens;
+ *
+ * if (!index) {
+ *     [...] // Clean up internal context if any.
+ * }
+ * [...] // Then set up dyn_token according to index.
+ *
+ * if (no_more_tokens)
+ *     *token_p = NULL;
+ * else
+ *     *token_p = &dyn_token;
  *
- *  int index = token_hdr - &(*tokens)[0];
+ * @endcode
  */
 struct cmdline_inst {
 	/* f(parsed_struct, data) */
