Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli.h	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli.h	(revision 4299)
@@ -34,5 +34,34 @@
  * @defgroup PJLIB_UTIL_CLI Command Line Interface Framework
  * @{
- *
+ * A CLI framework features an interface for defining command specification, 
+ * parsing, and executing a command. 
+ * It also features an interface to communicate with various front-ends, 
+ * such as console, telnet.
+ *
+\verbatim
+| vid help                  Show this help screen                             |
+| vid enable|disable        Enable or disable video in next offer/answer      |
+| vid call add              Add video stream for current call                 |
+| vid call cap N ID         Set capture dev ID for stream #N in current call  |
+| disable_codec g711|g722   Show this help screen                             |
+<CMD name='vid' id='0' desc="">
+       <CMD name='help' id='0' desc='' />
+       <CMD name='enable' id='0' desc='' />
+       <CMD name='disable' id='0' desc='' />
+       <CMD name='call' id='0' desc='' >
+                <CMD name='add' id='101' desc='...' />
+                <CMD name='cap' id='102' desc='...' >
+                   <ARG name='streamno' type='int' desc='...' id='1'/>
+                   <ARG name='devid' type='int' optional='1' id='2'/>
+                </CMD>
+       </CMD>
+</CMD>
+<CMD name='disable_codec' id=0 desc="">
+	<ARG name='codec_list' type='choice' id='3'>
+	    <CHOICE value='g711'/>
+	    <CHOICE value='g722'/>
+	</ARG>
+</CMD>
+\endverbatim 
  */
 
@@ -50,23 +79,4 @@
 
 /**
- * Reserved command id constants.
- */
-typedef enum pj_cli_std_cmd_id
-{
-    /**
-     * Constant to indicate an invalid command id.
-     */
-    PJ_CLI_INVALID_CMD_ID = -1,
-
-    /**
-     * A special command id to indicate that a command id denotes
-     * a command group.
-     */
-    PJ_CLI_CMD_ID_GROUP = -2
-
-} pj_cli_std_cmd_id;
-
-
-/**
  * This describes the parameters to be specified when creating a CLI
  * application with pj_cli_create(). Application MUST initialize this
@@ -96,46 +106,8 @@
 } pj_cli_cfg;
 
-
-/**
- * This describes the type of an argument (pj_cli_arg_spec).
- */
-typedef enum pj_cli_arg_type
-{
-    /**
-     * Unformatted string.
-     */
-    PJ_CLI_ARG_TEXT,
-
-    /**
-     * An integral number.
-     */
-    PJ_CLI_ARG_INT
-
-} pj_cli_arg_type;
-
-/**
- * This structure describe the specification of a command argument.
- */
-typedef struct pj_cli_arg_spec
-{
-    /**
-     * Argument name.
-     */
-    pj_str_t name;
-
-    /**
-     * Helpful description of the argument. This text will be used when
-     * displaying help texts for the command/argument.
-     */
-    pj_str_t desc;
-
-    /**
-     * Argument type, which will be used for rendering the argument and
-     * to perform basic validation against an input value.
-     */
-    pj_cli_arg_type type;
-
-} pj_cli_arg_spec;
-
+/**
+ * Type of argument id.
+ */
+typedef int pj_cli_arg_id;
 
 /**
@@ -153,4 +125,9 @@
  */
 typedef struct pj_cli_front_end pj_cli_front_end;
+
+/**
+ * Forward declaration for CLI argument spec structure.
+ */
+typedef struct pj_cli_arg_spec pj_cli_arg_spec;
 
 /**
@@ -173,104 +150,33 @@
 } pj_cli_cmd_val;
 
-
-
-/**
- * This specifies the callback type for command handlers, which will be
- * executed when the specified command is invoked.
- *
- * @param sess      The CLI session where the command is invoked.
- * @param cmd_val   The command that is specified by the user.
- *
- * @return          Return the status of the command execution.
- */
-typedef pj_status_t (*pj_cli_cmd_handler)(pj_cli_cmd_val *cval);
-
-/**
- * This structure describes the full specification of a CLI command. A CLI
- * command mainly consists of the name of the command, zero or more arguments,
- * and a callback function to be called to execute the command.
- *
- * Application can create this specification by forming an XML document and
- * calling pj_cli_create_cmd_from_xml() to instantiate the spec. A sample XML
- * document containing a command spec is as follows:
- *
- \verbatim
-  <CMD name='makecall' id='101' sc='m,mc' desc='Make outgoing call'>
-      <ARGS>
-	  <ARG name='target' type='text' desc='The destination'/>
-      </ARGS>
-  </CMD>
- \endverbatim
-
- */
-struct pj_cli_cmd_spec
+/**
+ * This structure contains the hints information for the end user. 
+ * This structure could contain either command or argument information.
+ * The front-end will format the information and present it to the user.
+ */
+typedef struct pj_cli_hint_info
 {
     /**
-     * To make list of child cmds.
-     */
-    PJ_DECL_LIST_MEMBER(struct pj_cli_cmd_spec);
-
-    /**
-     * Command ID assigned to this command by the application during command
-     * creation. If this value is PJ_CLI_CMD_ID_GROUP (-2), then this is
-     * a command group and it can't be executed.
-     */
-    pj_cli_cmd_id id;
-
-    /**
-     * The command name.
+     * The hint value.
      */
     pj_str_t name;
 
     /**
-     * The full description of the command.
+     * The hint type.
+     */
+    pj_str_t type;
+
+    /**
+     * Helpful description of the hint value. 
      */
     pj_str_t desc;
 
-    /**
-     * Number of optional shortcuts
-     */
-    unsigned sc_cnt;
-
-    /**
-     * Optional array of shortcuts, if any. Shortcut is a short name version
-     * of the command. If the command doesn't have any shortcuts, this
-     * will be initialized to NULL.
-     */
-    pj_str_t *sc;
-
-    /**
-     * The command handler, to be executed when a command matching this command
-     * specification is invoked by the end user. The value may be NULL if this
-     * is a command group.
-     */
-    pj_cli_cmd_handler handler;
-
-    /**
-     * Number of arguments.
-     */
-    unsigned arg_cnt;
-
-    /**
-     * Array of arguments.
-     */
-    pj_cli_arg_spec *arg;
-
-    /**
-     * Child commands, if any. A command will only have subcommands if it is
-     * a group. If the command doesn't have subcommands, this field will be
-     * initialized with NULL.
-     */
-    pj_cli_cmd_spec *sub_cmd;
-};
-
-
-/**
- * This contains extra parameters to be specified when calling pj_cli_exec().
+} pj_cli_hint_info;
+
+/**
+ * This structure contains extra information returned by pj_cli_sess_exec()/
+ * pj_cli_sess_parse().
  * Upon return from the function, various other fields in this structure will
  * be set by the function.
- *
- * Application must call pj_cli_exec_info_default() to initialize this
- * structure with its default values.
  */
 typedef struct pj_cli_exec_info
@@ -296,14 +202,103 @@
 
     /**
-     * If pj_cli_exec() fails because an argument is missing (the function
-     * returned PJ_CLI_EMISSINGARG error), this field will be set to the
-     * index of the missing argument. This is useful to give more helpful
-     * error info to the end user, or to facilitate a more interactive
-     * input display.
-     */
-    int arg_idx;
+     * The number of hint elements
+     **/
+    unsigned hint_cnt;
+
+    /**
+     * If pj_cli_sess_parse() fails because of a missing argument or ambigous 
+     * command/argument, the function returned PJ_CLI_EMISSINGARG or 
+     * PJ_CLI_EAMBIGUOUS error. 
+     * This field will contain the hint information. This is useful to give 
+     * helpful information to the end_user.
+     */
+    pj_cli_hint_info hint[PJ_CLI_MAX_HINTS];
 
 } pj_cli_exec_info;
 
+/**
+ * This structure contains the information returned from the dynamic 
+ * argument callback.
+ */
+typedef struct pj_cli_arg_choice_val
+{
+    /**
+     * The argument choice value
+     */
+    pj_str_t value;
+
+    /**
+     * Helpful description of the choice value. This text will be used when
+     * displaying the help texts for the choice value
+     */
+    pj_str_t desc;
+
+} pj_cli_arg_choice_val;
+
+/**
+ * This structure contains the parameters for pj_cli_arg_get_dyn_choice_val
+ */
+typedef struct pj_cli_dyn_choice_param
+{
+    /**
+     * The session on which the command was executed on.
+     */
+    pj_cli_sess *sess;
+
+    /**
+     * The command being processed.
+     */
+    pj_cli_cmd_spec *cmd;
+
+    /**
+     * The argument id.
+     */
+    pj_cli_arg_id arg_id;
+
+    /**
+     * The maximum number of values that the choice can hold.
+     */
+    unsigned max_cnt;
+
+    /**
+     * The pool to allocate memory from.
+     */
+    pj_pool_t *pool;
+
+    /**
+     * The choice values count.
+     */
+    unsigned cnt;
+
+    /**
+     * Array containing the valid choice values.
+     */
+    pj_cli_arg_choice_val choice[PJ_CLI_MAX_CHOICE_VAL];
+} pj_cli_dyn_choice_param;
+
+/**
+ * This specifies the callback type for argument handlers, which will be
+ * called to get the valid values of the choice type arguments.
+ */
+typedef void (*pj_cli_arg_get_dyn_choice_val) (pj_cli_dyn_choice_param *param);
+
+/**
+ * This specifies the function to get the id of the specified command
+ * 
+ * @param cmd		The specified command.
+ *
+ * @return		The command id
+ */
+PJ_DECL(pj_cli_cmd_id) pj_cli_get_cmd_id(const pj_cli_cmd_spec *cmd);
+
+/**
+ * This specifies the callback type for command handlers, which will be
+ * executed when the specified command is invoked.
+ *
+ * @param cmd_val   The command that is specified by the user.
+ *
+ * @return          Return the status of the command execution.
+ */
+typedef pj_status_t (*pj_cli_cmd_handler)(pj_cli_cmd_val *cval);
 
 /**
@@ -313,11 +308,4 @@
  */
 PJ_DECL(void) pj_cli_cfg_default(pj_cli_cfg *param);
-
-/**
- * Initialize pj_cli_exec_info with its default values.
- *
- * @param param		The param to be initialized.
- */
-PJ_DECL(void) pj_cli_exec_info_default(pj_cli_exec_info *param);
 
 /**
@@ -336,4 +324,15 @@
 
 /**
+ * Write a log message to the specific CLI session. 
+ *
+ * @param sess		The CLI active session.
+ * @param buffer        The message itself.
+ * @param len 	        Length of this message.
+ */
+PJ_DECL(void) pj_cli_sess_write_msg(pj_cli_sess *sess,                               
+				    const char *buffer,
+				    int len);
+
+/**
  * Create a new CLI application instance.
  *
@@ -345,5 +344,4 @@
 PJ_DECL(pj_status_t) pj_cli_create(pj_cli_cfg *cfg,
                                    pj_cli_t **p_cli);
-
 /**
  * Get the internal parameter of the CLI instance.
@@ -354,5 +352,4 @@
  */
 PJ_DECL(pj_cli_cfg*) pj_cli_get_param(pj_cli_t *cli);
-
 
 /**
@@ -361,5 +358,5 @@
  * application.
  *
- * See also pj_cli_end_session() to end a session instead of quitting the
+ * See also pj_cli_sess_end_session() to end a session instead of quitting the
  * whole application.
  *
@@ -371,5 +368,4 @@
 PJ_DECL(void) pj_cli_quit(pj_cli_t *cli, pj_cli_sess *req,
 			  pj_bool_t restart);
-
 /**
  * Check if application shutdown or restart has been requested.
@@ -380,5 +376,4 @@
  */
 PJ_DECL(pj_bool_t) pj_cli_is_quitting(pj_cli_t *cli);
-
 
 /**
@@ -392,5 +387,4 @@
 PJ_DECL(pj_bool_t) pj_cli_is_restarting(pj_cli_t *cli);
 
-
 /**
  * Destroy a CLI application instance. This would also close all sessions
@@ -401,5 +395,4 @@
 PJ_DECL(void) pj_cli_destroy(pj_cli_t *cli);
 
-
 /**
  * End the specified session, and destroy it to release all resources used
@@ -410,5 +403,5 @@
  * @param sess		The CLI session to be destroyed.
  */
-PJ_DECL(void) pj_cli_end_session(pj_cli_sess *sess);
+PJ_DECL(void) pj_cli_sess_end_session(pj_cli_sess *sess);
 
 /**
@@ -435,4 +428,6 @@
  * @param p_cmd		Optional pointer to store the newly created
  * 			specification.
+ * @param get_choice	Function handler for the argument. Specify this for 
+ *			dynamic choice type arguments.
  *
  * @return		PJ_SUCCESS on success, or the appropriate error code.
@@ -442,5 +437,12 @@
                                              const pj_str_t *xml,
                                              pj_cli_cmd_handler handler,
-                                             pj_cli_cmd_spec *p_cmd);
+                                             pj_cli_cmd_spec **p_cmd, 
+					     pj_cli_arg_get_dyn_choice_val get_choice);
+/**
+ * Initialize pj_cli_exec_info with its default values.
+ *
+ * @param param		The param to be initialized.
+ */
+PJ_DECL(void) pj_cli_exec_info_default(pj_cli_exec_info *param);
 
 /**
@@ -465,4 +467,5 @@
  * @param cmdline	The command line string to be parsed.
  * @param val		Structure to store the parsing result.
+ * @param pool		The pool to allocate memory from.
  * @param info		Additional info to be returned regarding the parsing.
  *
@@ -472,4 +475,5 @@
  *			  - PJ_EINVAL: invalid parameter to this function.
  *			  - PJ_ENOTFOUND: command is not found.
+ *			  - PJ_CLI_EAMBIGUOUS: command/argument is ambiguous.
  *			  - PJ_CLI_EMISSINGARG: missing argument.
  *			  - PJ_CLI_EINVARG: invalid command argument.
@@ -478,8 +482,9 @@
  *			      application to end it's main loop.
  */
-PJ_DECL(pj_status_t) pj_cli_parse(pj_cli_sess *sess,
-				  char *cmdline,
-				  pj_cli_cmd_val *val,
-				  pj_cli_exec_info *info);
+PJ_DECL(pj_status_t) pj_cli_sess_parse(pj_cli_sess *sess,
+				       char *cmdline,
+				       pj_cli_cmd_val *val,
+				       pj_pool_t *pool,
+				       pj_cli_exec_info *info);
 
 /**
@@ -487,13 +492,13 @@
  * the appropriate command and verify whether the string matches the command
  * specifications. If matches, the command will be executed, and the return
- * value of the command will be set in the \a cmd_ret field of the \a eparam
+ * value of the command will be set in the \a cmd_ret field of the \a info
  * argument, if specified.
  *
- * Please also see pj_cli_parse() for more info regarding the cmdline format.
+ * Please also see pj_cli_sess_parse() for more info regarding the cmdline 
+ * format.
  *
  * @param sess		The CLI session.
- * @param cmdline	The command line string to be executed. See the
- * 			description of pj_cli_parse() API for more info
- * 			regarding the cmdline format.
+ * @param cmdline	The command line string to be executed. 
+ * @param pool		The pool to allocate memory from.
  * @param info		Optional pointer to receive additional information
  * 			related to the execution of the command (such as
@@ -504,10 +509,10 @@
  * 			of the handler itself will be returned in \a info
  * 			argument, if specified). Please see the return value
- * 			of pj_cli_parse() for possible return values.
- */
-pj_status_t pj_cli_exec(pj_cli_sess *sess,
-                        char *cmdline,
-                        pj_cli_exec_info *info);
-
+ * 			of pj_cli_sess_parse() for possible return values.
+ */
+PJ_DECL(pj_status_t) pj_cli_sess_exec(pj_cli_sess *sess,
+				      char *cmdline,
+				      pj_pool_t *pool,
+				      pj_cli_exec_info *info);
 
 /**
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_console.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_console.h	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_console.h	(revision 4299)
@@ -51,4 +51,11 @@
     int log_level;
 
+    /**
+     * Specify text message as a prompt string to user.
+     *
+     * Default: empty
+     */
+    pj_str_t prompt_str;
+
 } pj_cli_console_cfg;
 
@@ -83,11 +90,5 @@
 
 /**
- * Retrieve a cmdline from console stdin. Application should use this
- * instead of the standard gets() or fgets(), since unlike these functions,
- * this is able to end the blocking when pj_cli_quit() is called by other
- * session. This function requires a thread support.
- *
- * Note that this function would also remove the trailing newlines from the
- * input, if any.
+ * Retrieve a cmdline from console stdin and process the input accordingly.
  *
  * @param sess		The CLI session.
@@ -97,7 +98,5 @@
  * @return		PJ_SUCCESS if an input was read
  */
-PJ_DECL(pj_status_t) pj_cli_console_readline(pj_cli_sess *sess,
-					     char *buf,
-					     unsigned maxlen);
+PJ_DECL(pj_status_t) pj_cli_console_process(pj_cli_sess *sess);
 
 /**
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_telnet.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_telnet.h	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_telnet.h	(revision 4299)
@@ -79,4 +79,11 @@
     pj_str_t welcome_msg;
 
+    /**
+     * Specify text message as a prompt string to user.
+     *
+     * Default: empty
+     */
+    pj_str_t prompt_str;
+
 } pj_cli_telnet_cfg;
 
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/config.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/config.h	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/config.h	(revision 4299)
@@ -304,4 +304,12 @@
 
 /**
+ * Maximum number of hints.
+ * Default: 32
+ */
+#ifndef PJ_CLI_MAX_HINTS
+#   define PJ_CLI_MAX_HINTS		32
+#endif
+
+/**
  * Maximum short name version (shortcuts) for a command.
  * Default: 4
@@ -343,4 +351,19 @@
 #endif
 
+/**
+ * Maximum number of argument values of choice type.
+ * Default: 16
+ */
+#ifndef PJ_CLI_MAX_CHOICE_VAL
+#   define PJ_CLI_MAX_CHOICE_VAL  16
+#endif
+
+/**
+ * Maximum number of command history.
+ * Default: 16
+ */
+#ifndef PJ_CLI_MAX_CMD_HISTORY
+#   define PJ_CLI_MAX_CMD_HISTORY  16
+#endif
 
 /**
Index: /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/errno.h
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/errno.h	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/errno.h	(revision 4299)
@@ -394,5 +394,5 @@
  * @hideinitializer
  * End the current session. This is a special error code returned by
- * pj_cli_exec() to indicate that "exit" or equivalent command has been
+ * pj_cli_sess_exec() to indicate that "exit" or equivalent command has been
  * called to end the current session.
  */
@@ -426,4 +426,10 @@
 /**
  * @hideinitializer
+ * CLI command entered by user match with more than one command/argument 
+ * specification.
+ */
+#define PJ_CLI_EAMBIGUOUS	    (PJLIB_UTIL_ERRNO_START+207)/* 320207 */
+/**
+ * @hideinitializer
  * Telnet connection lost.
  */
Index: /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c	(revision 4299)
@@ -43,4 +43,74 @@
 #endif
 
+/**
+ * This structure describes the full specification of a CLI command. A CLI
+ * command mainly consists of the name of the command, zero or more arguments,
+ * and a callback function to be called to execute the command.
+ *
+ * Application can create this specification by forming an XML document and
+ * calling pj_cli_create_cmd_from_xml() to instantiate the spec. A sample XML
+ * document containing a command spec is as follows:
+ */
+struct pj_cli_cmd_spec
+{
+    /**
+     * To make list of child cmds.
+     */
+    PJ_DECL_LIST_MEMBER(struct pj_cli_cmd_spec);
+
+    /**
+     * Command ID assigned to this command by the application during command
+     * creation. If this value is PJ_CLI_CMD_ID_GROUP (-2), then this is
+     * a command group and it can't be executed.
+     */
+    pj_cli_cmd_id id;
+
+    /**
+     * The command name.
+     */
+    pj_str_t name;
+
+    /**
+     * The full description of the command.
+     */
+    pj_str_t desc;
+
+    /**
+     * Number of optional shortcuts
+     */
+    unsigned sc_cnt;
+
+    /**
+     * Optional array of shortcuts, if any. Shortcut is a short name version
+     * of the command. If the command doesn't have any shortcuts, this
+     * will be initialized to NULL.
+     */
+    pj_str_t *sc;
+
+    /**
+     * The command handler, to be executed when a command matching this command
+     * specification is invoked by the end user. The value may be NULL if this
+     * is a command group.
+     */
+    pj_cli_cmd_handler handler;
+
+    /**
+     * Number of arguments.
+     */
+    unsigned arg_cnt;
+
+    /**
+     * Array of arguments.
+     */
+    pj_cli_arg_spec *arg;
+
+    /**
+     * Child commands, if any. A command will only have subcommands if it is
+     * a group. If the command doesn't have subcommands, this field will be
+     * initialized with NULL.
+     */
+    pj_cli_cmd_spec *sub_cmd;
+};
+
 struct pj_cli_t
 {
@@ -55,4 +125,146 @@
 };
 
+/**
+ * Reserved command id constants.
+ */
+typedef enum pj_cli_std_cmd_id
+{
+    /**
+     * Constant to indicate an invalid command id.
+     */
+    PJ_CLI_INVALID_CMD_ID = -1,
+
+    /**
+     * A special command id to indicate that a command id denotes
+     * a command group.
+     */
+    PJ_CLI_CMD_ID_GROUP = -2
+
+} pj_cli_std_cmd_id;
+
+/**
+ * This describes the type of an argument (pj_cli_arg_spec).
+ */
+typedef enum pj_cli_arg_type
+{
+    /**
+     * Unformatted string.
+     */
+    PJ_CLI_ARG_TEXT,
+
+    /**
+     * An integral number.
+     */
+    PJ_CLI_ARG_INT,
+
+    /**
+     * Choice type
+    */
+    PJ_CLI_ARG_CHOICE
+
+} pj_cli_arg_type;
+
+static const struct
+{
+    const pj_str_t msg;
+} arg_type[] = 
+{
+    {"Text", 4},
+    {"Int", 3},
+    {"Choice", 6}
+};
+
+/**
+ * This structure describe the specification of a command argument.
+ */
+struct pj_cli_arg_spec
+{
+    /**
+     * Argument id
+     */
+    pj_cli_arg_id id;
+
+    /**
+     * Argument name.
+     */
+    pj_str_t name;
+
+    /**
+     * Helpful description of the argument. This text will be used when
+     * displaying help texts for the command/argument.
+     */
+    pj_str_t desc;
+
+    /**
+     * Argument type, which will be used for rendering the argument and
+     * to perform basic validation against an input value.
+     */
+    pj_cli_arg_type type;
+
+    /**
+     * Argument status
+     */
+    pj_bool_t optional;
+
+    /**
+     * Static Choice Values count
+     */
+    unsigned stat_choice_cnt; 
+
+    /**
+     * Static Choice Values
+     */
+    pj_cli_arg_choice_val *stat_choice_val; 
+
+    /**
+     * Argument callback to get the valid values
+     */
+    pj_cli_arg_get_dyn_choice_val get_dyn_choice;
+
+};
+
+/**
+ * This describe the parse mode of the command line
+ */
+typedef enum pj_cli_parse_mode {
+    PARSE_NONE,
+    PARSE_COMPLETION,	/* Complete the command line */
+    PARSE_NEXT_AVAIL,   /* Find the next available command line */
+    PARSE_EXEC		/* Exec the command line */
+} pj_cli_parse_mode;
+
+/** 
+ * This is used to get the matched command/argument from the 
+ * command/argument structure.
+ * 
+ * @param sess		The session on which the command is execute on.
+ * @param cmd		The active command.
+ * @param cmd_val	The command value to match.
+ * @param argc		The number of argument that the 
+ *			current active command have.
+ * @param pool		The memory pool to allocate memory.
+ * @param get_cmd	Set true to search matching command from sub command.
+ * @param parse_mode	The parse mode.
+ * @param info		The output information containing any hints for 
+ *			matching command/arg.
+ * @return		This function return the status of the 
+ *			matching process.Please see the return value
+ * 			of pj_cli_sess_parse() for possible return values.
+ */
+static pj_status_t get_available_cmds(pj_cli_sess *sess,
+				      pj_cli_cmd_spec *cmd, 
+				      pj_str_t *cmd_val,
+				      unsigned argc,
+				      pj_pool_t *pool,
+				      pj_bool_t get_cmd,
+				      pj_cli_parse_mode parse_mode,
+				      pj_cli_cmd_spec **p_cmd,
+				      pj_cli_exec_info *info);
+
+PJ_DEF(pj_cli_cmd_id) pj_cli_get_cmd_id(const pj_cli_cmd_spec *cmd)
+{
+    return cmd->id;
+}
+
 PJ_DEF(void) pj_cli_cfg_default(pj_cli_cfg *param)
 {
@@ -86,4 +298,17 @@
         fe = fe->next;
     }
+}
+
+PJ_DECL(void) pj_cli_sess_write_msg(pj_cli_sess *sess,                               
+				    const char *buffer,
+				    int len)
+{
+    struct pj_cli_front_end *fe;
+
+    pj_assert(sess);
+
+    fe = sess->fe;
+    if (fe->op && fe->op->on_write_log)
+        (*fe->op->on_write_log)(fe, 0, buffer, len);
 }
 
@@ -102,5 +327,5 @@
         return PJ_SUCCESS;
     case CLI_CMD_EXIT:
-        pj_cli_end_session(cval->sess);
+        pj_cli_sess_end_session(cval->sess);
         return PJ_CLI_EEXIT;
     default:
@@ -109,17 +334,84 @@
 }
 
+#define print_(arg) \
+    do { \
+        unsigned d = pj_log_get_decor(); \
+        pj_log_set_decor(0); \
+        PJ_LOG(1, arg); \
+        pj_log_set_decor(d); \
+    } while (0)
+
+/** 
+ *  Example to send the command structure to all cli session 
+ **/  
+PJ_DEF(void) pj_cli_log_command_struct(pj_cli_t *cli, pj_str_t *indent, pj_cli_cmd_spec *in_cmd)
+{
+    static const pj_str_t CMD_SIGN = {"+", 1};        
+    static const pj_str_t ARG_SIGN = {"-", 1};    
+
+    if (cli) {
+	pj_cli_cmd_spec *cmd;
+	pj_pool_t *pool = pj_pool_create(cli->cfg.pf, "log_cmd", 64, 64, NULL);
+	cmd = (in_cmd)?in_cmd:&cli->root;
+	if (pool) {
+	    pj_str_t modif_indent;	    
+	    pj_cli_cmd_spec *child_cmd;
+	    unsigned i;
+	    char *indent_data;
+	    pj_str_t *print_indent;
+
+	    if (!indent) {
+		print_indent = PJ_POOL_ALLOC_T(pool, pj_str_t);
+		pj_strdup2(pool, print_indent, "");
+	    } else {
+		print_indent = indent;	
+	    }
+	    indent_data = (char *)pj_pool_alloc(pool, print_indent->slen + 2);
+	    modif_indent.ptr = indent_data;
+	    modif_indent.slen = 0;
+
+	    if (&cli->root != cmd) {
+		print_(("", "%.*s%.*s%.*s\r\n", 
+		       (int)print_indent->slen, print_indent->ptr,
+		       (int)CMD_SIGN.slen, CMD_SIGN.ptr,
+		       (int)cmd->name.slen, cmd->name.ptr));
+	    }
+	    pj_strcpy(&modif_indent, print_indent);
+	    pj_strcat2(&modif_indent, "  ");	    
+
+	    //print child commands
+	    if (cmd->sub_cmd) {
+		child_cmd = cmd->sub_cmd->next;
+		while(child_cmd != cmd->sub_cmd) {
+		    pj_cli_log_command_struct(cli, &modif_indent, child_cmd);
+		    child_cmd = child_cmd->next;
+		}
+	    }
+
+	    //print argumen
+	    for (i=0; i<cmd->arg_cnt;++i) {
+		pj_cli_arg_spec *arg = &cmd->arg[i];
+		print_(("", "%.*s%.*s%.*s\r\n", 
+		       (int)modif_indent.slen, modif_indent.ptr,
+		       (int)ARG_SIGN.slen, ARG_SIGN.ptr,
+		       (int)arg->name.slen, arg->name.ptr));
+	    }
+	    pj_pool_release(pool);
+	}
+    }     
+}
+
 PJ_DEF(pj_status_t) pj_cli_create(pj_cli_cfg *cfg,
                                   pj_cli_t **p_cli)
 {
     pj_pool_t *pool;
-    pj_cli_t *cli;
+    pj_cli_t *cli;    
     unsigned i;
+    /* This is an example of the command structure */
     char* cmd_xmls[] = {
      "<CMD name='log' id='30000' sc='' desc='Change log level'>"
-     "  <ARGS>"
-     "    <ARG name='level' type='int' desc='Log level'/>"
-     "  </ARGS>"
-     "</CMD>",
-     "<CMD name='exit' id='30001' sc='' desc='Exit session'>"
+     "    <ARG name='level' type='int' optional='0' desc='Log level'/>"
+     "</CMD>",     
+     "<CMD name='exit' id='30001' sc='' desc='Exit session'>"     
      "</CMD>",
     };
@@ -146,10 +438,10 @@
         pj_str_t xml = pj_str(cmd_xmls[i]);
 
-        if (pj_cli_add_cmd_from_xml(cli, NULL, &xml, &cmd_handler, NULL) !=
+        if (pj_cli_add_cmd_from_xml(cli, NULL, &xml, &cmd_handler, NULL, NULL) !=
             PJ_SUCCESS)
             TRACE_((THIS_FILE, "Failed to add command #%d", i));
     }
 
-    *p_cli = cli;
+    *p_cli = cli;    
 
     return PJ_SUCCESS;
@@ -224,5 +516,5 @@
 }
 
-PJ_DEF(void) pj_cli_end_session(pj_cli_sess *sess)
+PJ_DEF(void) pj_cli_sess_end_session(pj_cli_sess *sess)
 {
     pj_assert(sess);
@@ -239,14 +531,110 @@
 }
 
-PJ_DEF(pj_status_t) pj_cli_add_cmd_from_xml(pj_cli_t *cli,
-					    pj_cli_cmd_spec *group,
-                                            const pj_str_t *xml,
-                                            pj_cli_cmd_handler handler,
-                                            pj_cli_cmd_spec *p_cmd)
-{
-    #define ERROR_(STATUS) \
-        do {status = STATUS; goto on_exit;} while(0)
-    pj_pool_t *pool;
-    pj_xml_node *root;
+/**
+ * This method is to parse and add the choice type 
+ * argument values to command structure.
+ **/
+static pj_status_t pj_cli_add_choice_node(pj_cli_t *cli,
+					  pj_xml_node *xml_node,
+					  pj_cli_arg_spec *arg,
+					  pj_cli_arg_get_dyn_choice_val get_choice)
+{
+    pj_xml_node *choice_node;
+    pj_xml_node *sub_node;
+    pj_cli_arg_choice_val choice_values[PJ_CLI_MAX_CHOICE_VAL];
+    pj_status_t status = PJ_SUCCESS;
+
+    sub_node = xml_node;
+    arg->type = PJ_CLI_ARG_CHOICE;
+    arg->get_dyn_choice = get_choice;						
+
+    choice_node = sub_node->node_head.next;
+    while (choice_node != (pj_xml_node*)&sub_node->node_head) {
+	pj_xml_attr *choice_attr;
+	pj_cli_arg_choice_val *choice_val = &choice_values[arg->stat_choice_cnt];		     
+	pj_bzero(choice_val, sizeof(*choice_val));
+
+	choice_attr = choice_node->attr_head.next;
+	while (choice_attr != &choice_node->attr_head) {
+	    if (!pj_stricmp2(&choice_attr->name, "value")) {
+		pj_strassign(&choice_val->value, &choice_attr->value);
+	    } else if (!pj_stricmp2(&choice_attr->name, "desc")) {
+		pj_strassign(&choice_val->desc, &choice_attr->value);
+	    }				
+	}			    
+	++(arg->stat_choice_cnt);
+	choice_node = choice_node->next;
+    }    
+    if (arg->stat_choice_cnt > 0) {
+        unsigned i;
+
+	arg->stat_choice_val = (pj_cli_arg_choice_val *)pj_pool_zalloc(cli->pool, 
+								       arg->stat_choice_cnt *
+								       sizeof(pj_cli_arg_choice_val));
+        for (i = 0; i < arg->stat_choice_cnt; i++) {
+	    pj_strdup(cli->pool, &arg->stat_choice_val[i].value, &choice_values[i].value);
+            pj_strdup(cli->pool, &arg->stat_choice_val[i].desc, &choice_values[i].desc);            
+        }
+    }
+    return status;
+}
+
+/**
+ * This method is to parse and add the argument attribute to command structure.
+ **/
+static pj_status_t pj_cli_add_arg_node(pj_cli_t *cli,
+				       pj_xml_node *xml_node,
+				       pj_cli_cmd_spec *cmd,
+				       pj_cli_arg_spec *arg,
+				       pj_cli_arg_get_dyn_choice_val get_choice)
+{    
+    pj_xml_attr *attr;
+    pj_status_t status = PJ_SUCCESS;
+    pj_xml_node *sub_node = xml_node;
+
+    if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS)
+	return PJ_CLI_ETOOMANYARGS;
+    
+    pj_bzero(arg, sizeof(*arg));
+    attr = sub_node->attr_head.next;
+    arg->optional = PJ_FALSE;
+    while (attr != &sub_node->attr_head) {	
+	if (!pj_stricmp2(&attr->name, "name")) {
+	    pj_strassign(&arg->name, &attr->value);
+	} else if (!pj_stricmp2(&attr->name, "id")) {
+	    arg->id = pj_strtol(&attr->value);
+	} else if (!pj_stricmp2(&attr->name, "type")) {
+	    if (!pj_stricmp2(&attr->value, "text")) {
+		arg->type = PJ_CLI_ARG_TEXT;
+	    } else if (!pj_stricmp2(&attr->value, "int")) {
+		arg->type = PJ_CLI_ARG_INT;
+	    } else if (!pj_stricmp2(&attr->value, "CHOICE")) {
+		/* Get choice value */
+		pj_cli_add_choice_node(cli, xml_node, arg, get_choice);
+	    } 
+	} else if (!pj_stricmp2(&attr->name, "desc")) {
+	    pj_strassign(&arg->desc, &attr->value);
+	} else if (!pj_stricmp2(&attr->name, "optional")) {
+	    if (!pj_strcmp2(&attr->value, "1")) {
+		arg->optional = PJ_TRUE;
+	    }
+	}
+	attr = attr->next;
+    }
+    cmd->arg_cnt++;
+    return status;
+}
+
+/**
+ * This method is to parse and add the command attribute to command structure.
+ **/
+static pj_status_t pj_cli_add_cmd_node(pj_cli_t *cli,				  
+				       pj_cli_cmd_spec *group,					 
+				       pj_xml_node *xml_node,
+				       pj_cli_cmd_handler handler,
+				       pj_cli_cmd_spec **p_cmd,
+				       pj_cli_arg_get_dyn_choice_val get_choice)
+{
+    pj_xml_node *root = xml_node;
     pj_xml_attr *attr;
     pj_xml_node *sub_node;
@@ -256,18 +644,6 @@
     pj_status_t status = PJ_SUCCESS;
 
-    PJ_ASSERT_RETURN(cli && xml, PJ_EINVAL);
-
-    /* Parse the xml */
-    pool = pj_pool_create(cli->cfg.pf, "xml", 1024, 1024, NULL);
-    if (!pool)
-        return PJ_ENOMEM;
-    root = pj_xml_parse(pool, xml->ptr, xml->slen);
-    if (!root) {
-	TRACE_((THIS_FILE, "Error: unable to parse XML"));
-	ERROR_(PJ_CLI_EBADXML);
-    }
-
     if (pj_stricmp2(&root->name, "CMD"))
-        ERROR_(PJ_EINVAL);
+        return PJ_EINVAL;
 
     /* Initialize the command spec */
@@ -279,13 +655,10 @@
         if (!pj_stricmp2(&attr->name, "name")) {
             pj_strltrim(&attr->value);
-            pj_strrtrim(&attr->value);
-            /* Check whether command with the specified name already exists */
             if (!attr->value.slen ||
                 pj_hash_get(cli->hash, attr->value.ptr, 
                             attr->value.slen, NULL))
             {
-                ERROR_(PJ_CLI_EBADNAME);
+                return PJ_CLI_EBADNAME;
             }
-
             pj_strdup(cli->pool, &cmd->name, &attr->value);
         } else if (!pj_stricmp2(&attr->name, "id")) {
@@ -324,5 +697,5 @@
             PJ_CATCH_ANY {
                 pj_scan_fini(&scanner);
-                ERROR_(PJ_GET_EXCEPTION());
+                return (PJ_GET_EXCEPTION());
             }
             PJ_END;
@@ -331,58 +704,27 @@
             pj_strdup(cli->pool, &cmd->desc, &attr->value);
         }
-
         attr = attr->next;
     }
 
-    /* Get the command arguments */
+    /* Get the command childs/arguments */
     sub_node = root->node_head.next;
     while (sub_node != (pj_xml_node*)&root->node_head) {
-        if (!pj_stricmp2(&sub_node->name, "ARGS")) {
-            pj_xml_node *arg_node;
-
-            arg_node = sub_node->node_head.next;
-            while (arg_node != (pj_xml_node*)&sub_node->node_head) {
-                if (!pj_stricmp2(&arg_node->name, "ARG")) {
-                    pj_cli_arg_spec *arg;
-
-                    if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS)
-                        ERROR_(PJ_CLI_ETOOMANYARGS);
-                    arg = &args[cmd->arg_cnt];
-                    pj_bzero(arg, sizeof(*arg));
-                    attr = arg_node->attr_head.next;
-                    while (attr != &arg_node->attr_head) {
-                        if (!pj_stricmp2(&attr->name, "name")) {
-                            pj_strassign(&arg->name, &attr->value);
-                        } else if (!pj_stricmp2(&attr->name, "type")) {
-                            if (!pj_stricmp2(&attr->value, "text")) {
-                                arg->type = PJ_CLI_ARG_TEXT;
-                            } else if (!pj_stricmp2(&attr->value, "int")) {
-                                arg->type = PJ_CLI_ARG_INT;
-                            }
-                        } else if (!pj_stricmp2(&attr->name, "desc")) {
-                            pj_strassign(&arg->desc, &attr->value);
-                        }
-
-                        attr = attr->next;
-                    }
-                    cmd->arg_cnt++;
-                }
-
-                arg_node = arg_node->next;
-            }
+	if (!pj_stricmp2(&sub_node->name, "CMD")) {
+	    status = pj_cli_add_cmd_node(cli, cmd, sub_node, handler, NULL, 
+					 get_choice);
+	    if (status != PJ_SUCCESS)
+		return status;
+	} else if (!pj_stricmp2(&sub_node->name, "ARG")) {
+	    /* Get argument attribute */
+	    status = pj_cli_add_arg_node(cli, sub_node, cmd, &args[cmd->arg_cnt], 
+					 get_choice);
+	    if (status != PJ_SUCCESS)
+		return status;
         }
         sub_node = sub_node->next;
     }
 
-    if (cmd->id == PJ_CLI_CMD_ID_GROUP) {
-        /* Command group shouldn't have any shortcuts nor arguments */
-        if (!cmd->sc_cnt || !cmd->arg_cnt)
-            ERROR_(PJ_EINVAL);
-        cmd->sub_cmd = PJ_POOL_ALLOC_T(cli->pool, struct pj_cli_cmd_spec);
-        pj_list_init(cmd->sub_cmd);
-    }
-
     if (!cmd->name.slen)
-        ERROR_(PJ_CLI_EBADNAME);
+        return PJ_CLI_EBADNAME;
 
     if (cmd->arg_cnt) {
@@ -395,4 +737,5 @@
             pj_strdup(cli->pool, &cmd->arg[i].desc, &args[i].desc);
             cmd->arg[i].type = args[i].type;
+	    cmd->arg[i].optional = args[i].optional;
         }
     }
@@ -414,28 +757,62 @@
     cmd->handler = handler;
 
-    if (group)
+    if (group) {
+	if (!group->sub_cmd) {
+	    group->sub_cmd = PJ_POOL_ALLOC_T(cli->pool, struct pj_cli_cmd_spec);
+	    pj_list_init(group->sub_cmd);
+	}
         pj_list_push_back(group->sub_cmd, cmd);
-    else
+    } else {
         pj_list_push_back(cli->root.sub_cmd, cmd);
+    }
 
     if (p_cmd)
-        p_cmd = cmd;
-
-on_exit:
+        *p_cmd = cmd;
+
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_cli_add_cmd_from_xml(pj_cli_t *cli,
+					    pj_cli_cmd_spec *group,
+                                            const pj_str_t *xml,
+                                            pj_cli_cmd_handler handler,
+                                            pj_cli_cmd_spec **p_cmd, 
+					    pj_cli_arg_get_dyn_choice_val get_choice)
+{ 
+    pj_pool_t *pool;
+    pj_xml_node *root;
+    pj_status_t status = PJ_SUCCESS;
+    
+    PJ_ASSERT_RETURN(cli && xml, PJ_EINVAL);
+
+    /* Parse the xml */
+    pool = pj_pool_create(cli->cfg.pf, "xml", 1024, 1024, NULL);
+    if (!pool)
+        return PJ_ENOMEM;
+
+    root = pj_xml_parse(pool, xml->ptr, xml->slen);
+    if (!root) {
+	TRACE_((THIS_FILE, "Error: unable to parse XML"));
+	pj_pool_release(pool);
+	return PJ_CLI_EBADXML;
+    }    
+    status = pj_cli_add_cmd_node(cli, group, root, handler, p_cmd, get_choice);
     pj_pool_release(pool);
-
     return status;
-#undef ERROR_
-}
-
-PJ_DEF(pj_status_t) pj_cli_parse(pj_cli_sess *sess,
-				 char *cmdline,
-				 pj_cli_cmd_val *val,
-				 pj_cli_exec_info *info)
-{
+}
+
+PJ_DEF(pj_status_t) pj_cli_sess_parse(pj_cli_sess *sess,
+				      char *cmdline,
+				      pj_cli_cmd_val *val,
+				      pj_pool_t *pool,
+				      pj_cli_exec_info *info)
+{    
     pj_scanner scanner;
     pj_str_t str;
-    int len;
-    pj_cli_exec_info einfo;
+    int len;    
+    pj_cli_cmd_spec *cmd;
+    pj_cli_cmd_spec *next_cmd;
+    pj_status_t status = PJ_SUCCESS;
+    pj_cli_parse_mode parse_mode = PARSE_NONE;    
 
     PJ_USE_EXCEPTION;
@@ -443,89 +820,138 @@
     PJ_ASSERT_RETURN(sess && cmdline && val, PJ_EINVAL);
 
-    if (!info)
-        info = &einfo;
+    PJ_UNUSED_ARG(pool);
+
+    str.slen = 0;
     pj_cli_exec_info_default(info);
 
-    /* Parse the command line. */
+    /* Set the parse mode based on the latest char. */
     len = pj_ansi_strlen(cmdline);
-    if (len > 0 && cmdline[len - 1] == '\n') {
+    if (len > 0 && cmdline[len - 1] == '\r') {
         cmdline[--len] = 0;
-        if (len > 0 && cmdline[len - 1] == '\r')
-            cmdline[--len] = 0;
-    }
-    pj_scan_init(&scanner, cmdline, len, PJ_SCAN_AUTOSKIP_WS, 
-                 &on_syntax_error);
-    PJ_TRY {
-        pj_scan_get_until_chr(&scanner, " \t\r\n", &str);
-
-        /* Find the command from the hash table */
-        val->cmd = pj_hash_get(sess->fe->cli->hash, str.ptr, 
-                               str.slen, NULL);
-
-        /* Error, command not found */
-        if (!val->cmd)
-            PJ_THROW(PJ_ENOTFOUND);
-
-        info->cmd_id = val->cmd->id;
-
-        /* Parse the command arguments */
-        val->argc = 1;
-        pj_strassign(&val->argv[0], &str);
-        while (!pj_scan_is_eof(&scanner)) {
-            if (*scanner.curptr == '\'' || *scanner.curptr == '"' ||
-                *scanner.curptr == '[' || *scanner.curptr == '{')
-            {
-                pj_scan_get_quotes(&scanner, "'\"[{", "'\"]}", 4, &str);
-                /* Remove the quotes */
-                str.ptr++;
-                str.slen -= 2;
-            } else {
-                pj_scan_get_until_chr(&scanner, " \t\r\n", &str);
-            }
-            if (val->argc == PJ_CLI_MAX_ARGS)
-                PJ_THROW(PJ_CLI_ETOOMANYARGS);
-            pj_strassign(&val->argv[val->argc++], &str);
-        }
-        
-        if (!pj_scan_is_eof(&scanner)) {
-            PJ_THROW(PJ_CLI_EINVARG);
-        }
-    }
-    PJ_CATCH_ANY {
-        pj_scan_fini(&scanner);
-        return PJ_GET_EXCEPTION();
-    }
-    PJ_END;
-
-    if ((val->argc - 1) < (int)val->cmd->arg_cnt) {
-        info->arg_idx = val->argc;
-        return PJ_CLI_EMISSINGARG;
-    } else if ((val->argc - 1) > (int)val->cmd->arg_cnt) {
-        info->arg_idx = val->cmd->arg_cnt + 1;
-        return PJ_CLI_ETOOMANYARGS;
-    }
-
+	parse_mode = PARSE_EXEC;
+    } else if (len > 0 && 
+	       (cmdline[len - 1] == '\t' || cmdline[len - 1] == '?')) {
+
+	cmdline[--len] = 0;
+	if (len == 0) {
+	    parse_mode = PARSE_NEXT_AVAIL;
+	} else {
+	    if (cmdline[len - 1] == ' ') 
+		parse_mode = PARSE_NEXT_AVAIL;
+	    else 
+		parse_mode = PARSE_COMPLETION;
+	}
+    }
+    val->argc = 0;
+    info->err_pos = 0;
+    cmd = &sess->fe->cli->root;
+    if (len > 0) {
+	pj_scan_init(&scanner, cmdline, len, PJ_SCAN_AUTOSKIP_WS, 
+		     &on_syntax_error);
+	PJ_TRY {
+	    val->argc = 0;	    
+	    while (!pj_scan_is_eof(&scanner)) {
+		info->err_pos = scanner.curptr - scanner.begin;
+		if (*scanner.curptr == '\'' || *scanner.curptr == '"' ||
+		    *scanner.curptr == '[' || *scanner.curptr == '{')
+		{
+		    pj_scan_get_quotes(&scanner, "'\"[{", "'\"]}", 4, &str);
+		    /* Remove the quotes */
+		    str.ptr++;
+		    str.slen -= 2;
+		} else {
+		    pj_scan_get_until_chr(&scanner, " \t\r\n", &str);
+		}
+		++val->argc;
+		if (val->argc == PJ_CLI_MAX_ARGS)
+		    PJ_THROW(PJ_CLI_ETOOMANYARGS);		
+		
+		status = get_available_cmds(sess, cmd, &str, val->argc-1, 
+					    pool, PJ_TRUE, parse_mode, 
+					    &next_cmd, info);
+
+		if (status != PJ_SUCCESS)
+		    PJ_THROW(status);
+		
+		if (cmd != next_cmd) {
+		    /* Found new command, set it as the active command */
+		    cmd = next_cmd;
+		    val->argc = 1;
+		    val->cmd = cmd;
+		}
+		if (parse_mode == PARSE_EXEC) 
+		    pj_strassign(&val->argv[val->argc-1], &info->hint->name);
+		else 
+		    pj_strassign(&val->argv[val->argc-1], &str);
+
+	    }
+            
+	    if (!pj_scan_is_eof(&scanner)) {
+		PJ_THROW(PJ_CLI_EINVARG);
+	    }
+	}
+	PJ_CATCH_ANY {
+	    pj_scan_fini(&scanner);
+	    return PJ_GET_EXCEPTION();
+	}
+	PJ_END;
+    } 
+    
+    if ((parse_mode == PARSE_NEXT_AVAIL) || (parse_mode == PARSE_EXEC)) {
+	/* If exec mode, just get the matching argument */
+	status = get_available_cmds(sess, cmd, NULL, val->argc, pool, 
+				    (parse_mode==PARSE_NEXT_AVAIL), 
+				    parse_mode,
+				    NULL, info);
+	if ((status != PJ_SUCCESS) && (status != PJ_CLI_EINVARG)) {
+	    pj_str_t data = pj_str(cmdline);
+	    pj_strrtrim(&data);
+	    data.ptr[data.slen] = ' ';
+	    data.ptr[data.slen+1] = 0;
+
+	    info->err_pos = pj_ansi_strlen(cmdline);
+	}
+
+    } else if (parse_mode == PARSE_COMPLETION) {
+	if (info->hint[0].name.slen > str.slen) {
+	    pj_str_t *hint_info = &info->hint[0].name;
+	    pj_memmove(&hint_info->ptr[0], &hint_info->ptr[str.slen], 
+		       info->hint[0].name.slen-str.slen);
+	    hint_info->slen = info->hint[0].name.slen-str.slen;		
+	} else {
+	    info->hint[0].name.slen = 0;
+	}
+    }    
     val->sess = sess;
-
-    return PJ_SUCCESS;
-}
-
-pj_status_t pj_cli_exec(pj_cli_sess *sess,
-                        char *cmdline,
-                        pj_cli_exec_info *info)
+    return status;
+}
+
+PJ_DECL(pj_status_t) pj_cli_sess_exec(pj_cli_sess *sess,
+				      char *cmdline,
+				      pj_pool_t *pool,
+				      pj_cli_exec_info *info)
 {
     pj_cli_cmd_val val;
     pj_status_t status;
     pj_cli_exec_info einfo;
+    pj_str_t cmd;
 
     PJ_ASSERT_RETURN(sess && cmdline, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(pool);
+
+    cmd.ptr = cmdline;
+    cmd.slen = pj_ansi_strlen(cmdline);
+
+    if (pj_strtrim(&cmd)->slen == 0)
+	return PJ_SUCCESS;
 
     if (!info)
         info = &einfo;
-    status = pj_cli_parse(sess, cmdline, &val, info);
+    status = pj_cli_sess_parse(sess, cmdline, &val, pool, info);
     if (status != PJ_SUCCESS)
         return status;
 
-    if (val.cmd->handler) {
+    if ((val.argc > 0) && (val.cmd->handler)) {
         info->cmd_ret = (*val.cmd->handler)(&val);
         if (info->cmd_ret == PJ_CLI_EINVARG ||
@@ -536,2 +962,197 @@
     return PJ_SUCCESS;
 }
+
+static pj_status_t pj_cli_insert_new_hint(pj_pool_t *pool, 
+					  const pj_str_t *name, 
+					  const pj_str_t *desc, 
+					  const pj_str_t *type, 
+					  pj_cli_exec_info *info)
+{    
+    pj_cli_hint_info *hint = &info->hint[info->hint_cnt];
+    PJ_ASSERT_RETURN(pool && info, PJ_EINVAL);    
+
+    pj_strdup(pool, &hint->name, name);
+
+    if (desc && (desc->slen > 0))  {
+	pj_strdup(pool, &hint->desc, desc);
+    } else {
+	hint->desc.slen = 0;
+    }
+    
+    if (type && (type->slen > 0)) {
+	pj_strdup(pool, &hint->type, type);
+    } else {
+	hint->type.slen = 0;
+    }
+
+    ++info->hint_cnt;    
+    return PJ_SUCCESS;
+}
+
+static pj_status_t get_match_cmds(pj_cli_cmd_spec *cmd, 
+				  const pj_str_t *cmd_val,				      
+				  pj_pool_t *pool, 
+				  pj_cli_cmd_spec **p_cmd, 			       
+				  pj_cli_exec_info *info)
+{
+    pj_status_t status = PJ_SUCCESS;
+    PJ_ASSERT_RETURN(cmd && pool && info && cmd_val, PJ_EINVAL);    
+
+    if (p_cmd)
+	*p_cmd = cmd;
+
+    /* Get matching command */
+    if (cmd->sub_cmd) {
+	pj_cli_cmd_spec *child_cmd = cmd->sub_cmd->next;
+	while (child_cmd != cmd->sub_cmd) {
+	    unsigned i;	   
+	    pj_bool_t found = PJ_FALSE;
+	    if (!pj_strnicmp(&child_cmd->name, cmd_val, cmd_val->slen)) {		
+		status = pj_cli_insert_new_hint(pool, &child_cmd->name, 
+						&child_cmd->desc, NULL, info);
+		if (status != PJ_SUCCESS)
+		    return status;
+
+		found = PJ_TRUE;
+	    }
+	    for (i=0; i < child_cmd->sc_cnt; ++i) {
+		static const pj_str_t SHORTCUT = {"SC", 2};
+		pj_str_t *sc = &child_cmd->sc[i];
+		PJ_ASSERT_RETURN(sc, PJ_EINVAL);
+
+		if (!pj_strnicmp(sc, cmd_val, cmd_val->slen)) {		
+		    status = pj_cli_insert_new_hint(pool, sc, &child_cmd->desc, 
+						    &SHORTCUT, info);
+		    if (status != PJ_SUCCESS)
+			return status;
+
+		    found = PJ_TRUE;
+		}		
+	    }
+	    if (found && p_cmd) {
+		*p_cmd = child_cmd;		
+	    }
+
+	    child_cmd = child_cmd->next;
+	}
+    }
+    return status;
+}
+
+static pj_status_t get_match_args(pj_cli_sess *sess,
+				  pj_cli_cmd_spec *cmd, 
+				  const pj_str_t *cmd_val,
+				  unsigned argc,
+				  pj_pool_t *pool, 
+				  pj_cli_parse_mode parse_mode,
+				  pj_cli_exec_info *info)
+{
+    pj_cli_arg_spec *arg;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(cmd && pool && cmd_val && info, PJ_EINVAL);
+
+    if ((argc > cmd->arg_cnt) && (!cmd->sub_cmd)) {
+	if (cmd_val->slen > 0)
+	    return PJ_CLI_ETOOMANYARGS;
+	else
+	    return PJ_SUCCESS;
+    }
+
+    if (cmd->arg_cnt > 0) {
+	arg = &cmd->arg[argc-1];
+	PJ_ASSERT_RETURN(arg, PJ_EINVAL);
+	if (arg->type == PJ_CLI_ARG_CHOICE) {	    
+	    unsigned j;	    
+	    pj_cli_dyn_choice_param dyn_choice_param;	    
+
+	    for (j=0; j < arg->stat_choice_cnt; ++j) {
+		pj_cli_arg_choice_val *choice_val = &arg->stat_choice_val[j];
+    	    
+		PJ_ASSERT_RETURN(choice_val, PJ_EINVAL);		
+    	    
+		if (!pj_strnicmp(&choice_val->value, cmd_val, cmd_val->slen)) {		    
+		    status = pj_cli_insert_new_hint(pool, &choice_val->value, 
+						    &choice_val->desc, 
+						    &arg_type[PJ_CLI_ARG_CHOICE].msg, 
+						    info);
+		    if (status != PJ_SUCCESS)
+			return status;		    
+		}
+	    }
+	    /* Get the dynamic choice values */	    
+	    dyn_choice_param.sess = sess;
+	    dyn_choice_param.cmd = cmd;
+	    dyn_choice_param.arg_id = arg->id;
+	    dyn_choice_param.max_cnt = PJ_CLI_MAX_CHOICE_VAL;
+	    dyn_choice_param.pool = pool;
+	    dyn_choice_param.cnt = 0;	    
+
+	    (*arg->get_dyn_choice)(&dyn_choice_param);
+	    for (j=0; j < dyn_choice_param.cnt; ++j) {
+		pj_cli_arg_choice_val *choice = &dyn_choice_param.choice[j];
+		pj_strassign(&info->hint[info->hint_cnt].name, &choice->value);
+		pj_strassign(&info->hint[info->hint_cnt].desc, &choice->desc);
+		++info->hint_cnt;
+	    }
+	} else {
+	    if (cmd_val->slen == 0) {
+		if (info->hint_cnt == 0) {
+		    if (!((parse_mode == PARSE_EXEC) && (arg->optional))) {
+			/* If exec mode, don't need to insert the hint if optional */
+			status = pj_cli_insert_new_hint(pool, &arg->name, &arg->desc, 
+							&arg_type[arg->type].msg, info);
+			if (status != PJ_SUCCESS)
+			    return status;
+		    }
+		    if (!arg->optional)
+			return PJ_CLI_EMISSINGARG;
+		} 
+	    } else {
+		return pj_cli_insert_new_hint(pool, cmd_val, 
+					      NULL, NULL, info);
+	    }
+	}
+    } 
+    return status;
+}
+
+static pj_status_t get_available_cmds(pj_cli_sess *sess,
+				      pj_cli_cmd_spec *cmd, 
+				      pj_str_t *cmd_val,
+				      unsigned argc,
+				      pj_pool_t *pool,
+				      pj_bool_t get_cmd,
+				      pj_cli_parse_mode parse_mode,
+				      pj_cli_cmd_spec **p_cmd,
+				      pj_cli_exec_info *info)
+{
+    pj_status_t status = PJ_SUCCESS;
+    pj_str_t *prefix;
+    pj_str_t EMPTY_STR = {NULL, 0};
+
+    prefix = cmd_val?(pj_strtrim(cmd_val)):(&EMPTY_STR);
+
+    info->hint_cnt = 0;    
+
+    if (get_cmd)
+	status = get_match_cmds(cmd, prefix, pool, p_cmd, info);
+    if (argc > 0)
+	status = get_match_args(sess, cmd, prefix, argc, pool, parse_mode, info);
+
+    if (status == PJ_SUCCESS) {	
+	if (prefix->slen > 0) {
+	    if (info->hint_cnt == 0) {
+		status = PJ_CLI_EINVARG;
+	    } else if (info->hint_cnt > 1) {
+		status = PJ_CLI_EAMBIGUOUS;
+	    }
+	} else {
+	    if (info->hint_cnt > 0)
+		status = PJ_CLI_EAMBIGUOUS;
+	}
+    } 
+
+    return status;
+}
+
Index: /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_console.c
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_console.c	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_console.c	(revision 4299)
@@ -28,4 +28,81 @@
 #include <pjlib-util/errno.h>
 
+#if defined(PJ_LINUX) && PJ_LINUX != 0 || \
+    defined(PJ_DARWINOS) && PJ_DARWINOS != 0
+#include <termios.h>
+
+static struct termios old, new;
+
+/* Initialize new terminal i/o settings */
+void initTermios(int echo) 
+{
+    tcgetattr(0, &old); 
+    new = old; 
+    new.c_lflag &= ~ICANON; 
+    new.c_lflag &= echo ? ECHO : ~ECHO; 
+    tcsetattr(0, TCSANOW, &new); 
+}
+
+/* Restore old terminal i/o settings */
+void resetTermios(void) 
+{
+    tcsetattr(0, TCSANOW, &old);
+}
+
+/* Read 1 character - echo defines echo mode */
+char getch_(int echo) 
+{
+    char ch;
+    initTermios(echo);
+    ch = getchar();
+    resetTermios();
+    return ch;
+}
+
+/* Read 1 character without echo */
+char getch(void) 
+{
+    return getch_(0);
+}
+
+#endif
+
+/**
+ * This specify the state of input character parsing.
+ */
+typedef enum cmd_parse_state
+{
+    ST_NORMAL,
+    ST_SCANMODE,
+    ST_ESC,
+    ST_ARROWMODE
+} cmd_parse_state;
+
+/**
+ * This structure contains the command line shown to the user.  
+ */
+typedef struct console_recv_buf {
+    /**
+     * Buffer containing the characters, NULL terminated.
+     */
+    unsigned char	    rbuf[PJ_CLI_MAX_CMDBUF];
+
+    /**
+     * Current length of the command line.
+     */
+    unsigned		    len;
+
+    /**
+     * Current cursor position.
+     */
+    unsigned		    cur_pos;
+} console_recv_buf;
+
+typedef struct cmd_history
+{
+    PJ_DECL_LIST_MEMBER(struct cmd_history);
+    pj_str_t command;
+} cmd_history;
+
 struct cli_console_fe
 {
@@ -35,13 +112,67 @@
     pj_thread_t        *input_thread;
     pj_bool_t           thread_quit;
-    pj_sem_t           *thread_sem;
+    pj_sem_t           *thread_sem;   
+    pj_cli_console_cfg  cfg;
 
     struct async_input_t
-    {
-        char       *buf;
-        unsigned    maxlen;
-        pj_sem_t   *sem;
+    {        
+	console_recv_buf    recv_buf;        
+        pj_sem_t	   *sem;
     } input;
+
+    cmd_history		    *history;
+    cmd_history		    *active_history;
 };
+
+static unsigned recv_buf_right_len(console_recv_buf *recv_buf)
+{
+    return (recv_buf->len - recv_buf->cur_pos);
+}
+
+static pj_bool_t recv_buf_insert(console_recv_buf *recv_buf, 
+				 unsigned char *data) 
+{    
+    if (recv_buf->len+1 >= PJ_CLI_MAX_CMDBUF) {
+	return PJ_FALSE;
+    } else {	
+	if (*data == '\t' || *data == '?' || *data == '\r') {
+	    /* Always insert to the end of line */
+	    recv_buf->rbuf[recv_buf->len] = *data;
+	} else {
+	    /* Insert based on the current cursor pos */
+	    unsigned cur_pos = recv_buf->cur_pos;
+	    unsigned rlen = recv_buf_right_len(recv_buf);	    
+	    if (rlen > 0) {		    
+		/* Shift right characters */
+		pj_memmove(&recv_buf->rbuf[cur_pos+1], 
+			   &recv_buf->rbuf[cur_pos], 
+			   rlen+1);
+	    } 
+	    recv_buf->rbuf[cur_pos] = *data;
+	}
+	++recv_buf->cur_pos;
+	++recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }
+    return PJ_TRUE;
+}
+
+static pj_bool_t recv_buf_backspace(console_recv_buf *recv_buf)
+{
+    if ((recv_buf->cur_pos == 0) || (recv_buf->len == 0)) {
+	return PJ_FALSE;
+    } else {
+	unsigned rlen = recv_buf_right_len(recv_buf);
+	if (rlen) {
+	    unsigned cur_pos = recv_buf->cur_pos;
+	    pj_memmove(&recv_buf->rbuf[cur_pos-1], &recv_buf->rbuf[cur_pos], 
+		       rlen);
+	}
+	--recv_buf->cur_pos;
+	--recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }    
+    return PJ_TRUE;
+}
 
 static void cli_console_write_log(pj_cli_front_end *fe, int level,
@@ -87,5 +218,5 @@
     pj_assert(param);
 
-    param->log_level = PJ_CLI_CONSOLE_LOG_LEVEL;
+    param->log_level = PJ_CLI_CONSOLE_LOG_LEVEL;    
 }
 
@@ -109,4 +240,7 @@
     sess = PJ_POOL_ZALLOC_T(pool, pj_cli_sess);
     fe = PJ_POOL_ZALLOC_T(pool, struct cli_console_fe);
+    fe->history = PJ_POOL_ZALLOC_T(pool, struct cmd_history);
+    pj_list_init(fe->history);
+    fe->active_history = fe->history;
 
     if (!param) {
@@ -129,4 +263,13 @@
     pj_cli_register_front_end(cli, &fe->base);
 
+    if (fe->cfg.prompt_str.slen == 0) {	
+	pj_str_t prompt_sign = pj_str(">>> ");
+	char *prompt_data = pj_pool_alloc(fe->pool, 5);	
+	fe->cfg.prompt_str.ptr = prompt_data;	
+
+	pj_strcpy(&fe->cfg.prompt_str, &prompt_sign);
+	prompt_data[4] = 0;
+    }
+
     *p_sess = sess;
     if (p_fe)
@@ -136,19 +279,553 @@
 }
 
-static int readline_thread(void * p)
-{
+static void send_prompt_str(pj_cli_sess *sess)
+{
+    pj_str_t send_data;
+    char data_str[128];
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
+
+    send_data.ptr = &data_str[0];
+    send_data.slen = 0;
+    
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+    send_data.ptr[send_data.slen] = 0;
+
+    printf("%s", send_data.ptr);
+}
+
+static void send_err_arg(pj_cli_sess *sess, 
+			 const pj_cli_exec_info *info, 
+			 const pj_str_t *msg,
+			 pj_bool_t with_return,
+			 pj_bool_t with_last_cmd)
+{
+    pj_str_t send_data;
+    char data_str[256];
+    unsigned len;
+    unsigned i;
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
+    console_recv_buf *recv_buf = &fe->input.recv_buf;
+
+    send_data.ptr = &data_str[0];
+    send_data.slen = 0;
+
+    if (with_return)
+	pj_strcat2(&send_data, "\r\n");
+
+    len = fe->cfg.prompt_str.slen + info->err_pos;
+
+    for (i=0;i<len;++i) {
+	pj_strcat2(&send_data, " ");
+    }
+    pj_strcat2(&send_data, "^");
+    pj_strcat2(&send_data, "\r\n");
+    pj_strcat(&send_data, msg);
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+    if (with_last_cmd)
+	pj_strcat2(&send_data, (char *)&recv_buf->rbuf[0]);
+
+    send_data.ptr[send_data.slen] = 0;
+    printf("%s", send_data.ptr);
+}
+
+static void send_return_key(pj_cli_sess *sess) 
+{    
+    PJ_UNUSED_ARG(sess);
+    printf("\r\n");    
+}
+
+static void send_inv_arg(pj_cli_sess *sess, 
+			 const pj_cli_exec_info *info,
+			 pj_bool_t with_return,
+			 pj_bool_t with_last_cmd)
+{
+    static const pj_str_t ERR_MSG = {"%Error : Invalid Arguments\r\n", 28};
+    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
+}
+
+static void send_too_many_arg(pj_cli_sess *sess, 
+			      const pj_cli_exec_info *info,
+			      pj_bool_t with_return,
+			      pj_bool_t with_last_cmd)
+{
+    static const pj_str_t ERR_MSG = {"%Error : Too Many Arguments\r\n", 29};
+    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
+}
+
+static void send_ambi_arg(pj_cli_sess *sess, 
+			  const pj_cli_exec_info *info,
+			  pj_bool_t with_return,
+			  pj_bool_t with_last_cmd)
+{
+    unsigned i;
+    pj_ssize_t j;
+    unsigned len;
+    pj_str_t send_data;
+    char data[1028];
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
+    console_recv_buf *recv_buf = &fe->input.recv_buf;
+    const pj_cli_hint_info *hint = info->hint;
+    pj_bool_t is_process_sc = PJ_FALSE;
+    pj_bool_t valid_type = PJ_FALSE;
+    pj_ssize_t max_length = 0;
+    const pj_str_t sc_type = pj_str("SC");
+    send_data.ptr = &data[0];
+    send_data.slen = 0;
+    
+    if (with_return)
+	pj_strcat2(&send_data, "\r\n");
+
+    len = fe->cfg.prompt_str.slen + info->err_pos;
+
+    for (i=0;i<len;++i) {
+	pj_strcat2(&send_data, " ");
+    }
+    pj_strcat2(&send_data, "^");        
+
+    /* Get the max length of the command name */
+    for (i=0;i<info->hint_cnt;++i) {
+	if (hint[i].name.slen > max_length) {
+	    max_length = hint[i].name.slen;
+	}
+    }
+
+    for (i=0;i<info->hint_cnt;++i) {	
+	if ((&hint[i].type) && (hint[i].type.slen > 0)) {
+	    valid_type = PJ_TRUE;
+	    if (pj_stricmp(&hint[i].type, &sc_type) == 0) {
+		if (is_process_sc) {
+		    pj_strcat2(&send_data, ", ");
+		} else {
+		    pj_strcat2(&send_data, "\r\n\t Shorcut: ");
+		    is_process_sc = PJ_TRUE;
+		}
+		pj_strcat(&send_data, &hint[i].name);
+	    } else {
+		is_process_sc = PJ_FALSE;
+	    }
+	} else {	    
+	    valid_type = PJ_FALSE;
+	    is_process_sc = PJ_FALSE;
+	}
+
+	if (!is_process_sc) {
+	    pj_strcat2(&send_data, "\r\n\t");
+
+	    if (valid_type) {
+		pj_strcat2(&send_data, "<");
+		pj_strcat(&send_data, &hint[i].type);
+		pj_strcat2(&send_data, ">");	
+	    } else {
+		pj_strcat(&send_data, &hint[i].name);	    
+	    }
+    	
+	    if ((&hint[i].desc) && (hint[i].desc.slen > 0)) {
+		if (!valid_type) {
+		    for (j=0;j<(max_length-hint[i].name.slen);++j) {
+			pj_strcat2(&send_data, " ");
+		    }
+		}
+		pj_strcat2(&send_data, "\t");
+		pj_strcat(&send_data, &hint[i].desc);	    
+	    }
+	}
+    }        
+    pj_strcat2(&send_data, "\r\n");
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+    if (with_last_cmd)
+	pj_strcat2(&send_data, (char *)&recv_buf->rbuf[0]);
+
+    send_data.ptr[send_data.slen] = 0;
+    printf("%s", send_data.ptr);
+}
+
+static void send_comp_arg(pj_cli_exec_info *info)
+{
+    pj_str_t send_data;
+    char data[128];
+
+    pj_strcat2(&info->hint[0].name, " ");
+
+    send_data.ptr = &data[0];
+    send_data.slen = 0;
+
+    pj_strcat(&send_data, &info->hint[0].name);        
+
+    send_data.ptr[send_data.slen] = 0;
+    printf("%s", send_data.ptr);
+}
+
+static int compare_str(void *value, const pj_list_type *nd)
+{
+    cmd_history *node = (cmd_history*)nd;
+    return (pj_strcmp((pj_str_t *)value, &node->command));
+}
+
+static pj_status_t insert_history(pj_cli_sess *sess, 				 
+				  char *cmd_val)
+{
+    cmd_history *in_history;
+    pj_str_t cmd;
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;    
+    cmd.ptr = cmd_val;
+    cmd.slen = pj_ansi_strlen(cmd_val)-1;
+
+    if (cmd.slen == 0)
+	return PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+    /* Find matching history */
+    in_history = pj_list_search(fe->history, (void*)&cmd, compare_str);
+    if (!in_history) {
+	if (pj_list_size(fe->history) < PJ_CLI_MAX_CMD_HISTORY) {	    
+	    char *data_history;
+	    in_history = PJ_POOL_ZALLOC_T(fe->pool, cmd_history);
+	    pj_list_init(in_history);
+	    data_history = (char *)pj_pool_calloc(fe->pool, 
+			   sizeof(char), PJ_CLI_MAX_CMDBUF);
+	    in_history->command.ptr = data_history;
+	    in_history->command.slen = 0;
+	} else {
+	    /* Get the oldest history */
+	    in_history = fe->history->prev;
+	}	    
+    } else {
+	pj_list_insert_nodes_after(in_history->prev, in_history->next);
+    }
+    pj_strcpy(&in_history->command, pj_strtrim(&cmd));
+    pj_list_push_front(fe->history, in_history);
+    fe->active_history = fe->history;
+
+    return PJ_SUCCESS;
+}
+
+static pj_str_t* get_prev_history(pj_cli_sess *sess, pj_bool_t is_forward)
+{
+    pj_str_t *retval;
+    pj_size_t history_size;
+    cmd_history *node;
+    cmd_history *root;
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
+
+    PJ_ASSERT_RETURN(sess && fe, NULL);
+
+    node = fe->active_history;
+    root = fe->history;
+    history_size = pj_list_size(fe->history);
+
+    if (history_size == 0) {
+	return NULL;
+    } else {
+	if (is_forward) {	    
+	    node = (node->next==root)?node->next->next:node->next;	    
+	} else {
+	    node = (node->prev==root)?node->prev->prev:node->prev;	    
+	}
+	retval = &node->command;
+	fe->active_history = node;
+    }
+    return retval;
+}
+
+static pj_bool_t handle_alfa_num(console_recv_buf *recv_buf, 
+				 unsigned char *cdata)
+{        
+    if (recv_buf_right_len(recv_buf) > 0) {
+	char out_str[255];	
+	pj_memset(&out_str[0], 0, 255);
+	out_str[0] = *cdata;
+	pj_memcpy(&out_str[1], &recv_buf->rbuf[recv_buf->cur_pos], 
+	          recv_buf_right_len(recv_buf));
+	pj_memset(&out_str[recv_buf_right_len(recv_buf)+1], '\b', 
+	          recv_buf_right_len(recv_buf));	
+	printf("%s", out_str); 
+    } else {	
+	printf("%c", *cdata);
+    }    
+    return PJ_TRUE;
+}
+
+static pj_bool_t handle_backspace(console_recv_buf *recv_buf)
+{    
+    if (recv_buf_backspace(recv_buf)) {
+	if(recv_buf_right_len(recv_buf) > 0) {
+	    char out_str[255];
+	    pj_memset(&out_str[0], 0, 255);
+	    out_str[0] = '\b';
+	    pj_memcpy(&out_str[1], &recv_buf->rbuf[recv_buf->cur_pos], 
+		recv_buf_right_len(recv_buf));
+	    out_str[recv_buf_right_len(recv_buf)+1] = ' '; 
+	    pj_memset(&out_str[recv_buf_right_len(recv_buf)+2], '\b', 
+		recv_buf_right_len(recv_buf)+1);
+	    printf("%s", out_str);
+	} else {
+	    char out_str[4];
+	    out_str[0] = '\b';
+	    out_str[1] = ' ';
+	    out_str[2] = '\b';
+	    out_str[3] = 0;
+	    printf("%s", out_str);
+	}
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_tab(pj_cli_sess *sess)
+{
+    pj_status_t status;
+    pj_bool_t retval = PJ_TRUE;
+    unsigned len;
+    
+    pj_pool_t *pool;
+    pj_cli_cmd_val *cmd_val;
+    pj_cli_exec_info info;
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
+    console_recv_buf *recv_buf = &fe->input.recv_buf;
+    pj_cli_t *cli = sess->fe->cli;
+
+    pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_tab",
+                          PJ_CLI_CONSOLE_POOL_SIZE, PJ_CLI_CONSOLE_POOL_INC,
+                          NULL);
+
+    cmd_val = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_val);
+    
+    status = pj_cli_sess_parse(sess, (char *)recv_buf->rbuf, cmd_val, 
+			       pool, &info);    
+
+    len = pj_ansi_strlen((char *)recv_buf->rbuf);
+
+    switch (status) {
+    case PJ_CLI_EINVARG:
+	send_inv_arg(sess, &info, PJ_TRUE, PJ_TRUE);		
+	break;
+    case PJ_CLI_ETOOMANYARGS:
+	send_too_many_arg(sess, &info, PJ_TRUE, PJ_TRUE);
+	break;
+    case PJ_CLI_EMISSINGARG:
+    case PJ_CLI_EAMBIGUOUS:
+	send_ambi_arg(sess, &info, PJ_TRUE, PJ_TRUE);
+	break;
+    case PJ_SUCCESS:
+	if (len > recv_buf->cur_pos)
+	{
+	    /* Send the cursor to EOL */
+	    unsigned char *data_sent = &recv_buf->rbuf[recv_buf->cur_pos-1];
+	    printf("%s", data_sent);	    
+	}
+	if (info.hint_cnt > 0) {	
+	    /* Compelete command */
+	    send_comp_arg(&info);
+
+	    pj_memcpy(&recv_buf->rbuf[len],  
+		      &info.hint[0].name.ptr[0], info.hint[0].name.slen);
+
+	    len += info.hint[0].name.slen;
+	    recv_buf->rbuf[len] = 0;		    
+	} else {
+	    retval = PJ_FALSE;
+	}
+	break;
+    }
+    recv_buf->len = len;
+    recv_buf->cur_pos = len;
+
+    pj_pool_release(pool);	
+    return retval; 
+}
+
+static pj_bool_t handle_return(pj_cli_sess *sess)
+{
+    pj_status_t status;
+    pj_bool_t retval = PJ_TRUE;
+    
+    pj_pool_t *pool;    
+    pj_cli_exec_info info;
+    pj_cli_t *cli = sess->fe->cli;
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
+    console_recv_buf *recv_buf = &fe->input.recv_buf;
+    
+    send_return_key(sess);    
+    insert_history(sess, (char *)&recv_buf->rbuf[0]);
+
+    pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_return",
+			  PJ_CLI_CONSOLE_POOL_SIZE, PJ_CLI_CONSOLE_POOL_INC,
+			  NULL);    
+    
+    status = pj_cli_sess_exec(sess, (char *)&recv_buf->rbuf[0], 
+			      pool, &info);
+
+    switch (status) {
+    case PJ_CLI_EINVARG:
+	send_inv_arg(sess, &info, PJ_FALSE, PJ_FALSE);	
+	break;
+    case PJ_CLI_ETOOMANYARGS:
+	send_too_many_arg(sess, &info, PJ_FALSE, PJ_FALSE);
+	break;
+    case PJ_CLI_EAMBIGUOUS:
+    case PJ_CLI_EMISSINGARG:
+	send_ambi_arg(sess, &info, PJ_FALSE, PJ_FALSE);
+	break;
+    case PJ_CLI_EEXIT:
+	retval = PJ_FALSE;
+	break;
+    case PJ_SUCCESS:
+	send_prompt_str(sess);
+	break;
+    }    
+    if (retval) {
+	recv_buf->rbuf[0] = 0;
+	recv_buf->len = 0;
+	recv_buf->cur_pos = 0;
+    }
+
+    pj_pool_release(pool);	
+    return retval; 
+}
+
+static pj_bool_t handle_up_down(pj_cli_sess *sess, pj_bool_t is_up)
+{
+    pj_str_t *history;
+    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
+    console_recv_buf *recv_buf = &fe->input.recv_buf;
+
+    PJ_ASSERT_RETURN(sess && fe, PJ_FALSE);
+
+    history = get_prev_history(sess, is_up);
+    if (history) {
+	pj_str_t send_data;
+	char str[PJ_CLI_MAX_CMDBUF];
+	send_data.ptr = &str[0];
+	send_data.slen = 0;
+
+	if (recv_buf->cur_pos > 0) {
+	    pj_memset(send_data.ptr, 0x08, recv_buf->cur_pos);
+	    send_data.slen = recv_buf->cur_pos;
+	}
+
+	if (recv_buf->len > (unsigned)history->slen) {
+	    unsigned buf_len = recv_buf->len;
+	    pj_memset(&send_data.ptr[send_data.slen], 0x20, buf_len);
+	    send_data.slen += buf_len;
+	    pj_memset(&send_data.ptr[send_data.slen], 0x08, buf_len);
+	    send_data.slen += buf_len;
+	} 
+	/* Send data */
+	pj_strcat(&send_data, history);	
+	send_data.ptr[send_data.slen] = 0;
+	printf("%s", send_data.ptr);
+	pj_ansi_strncpy((char*)&recv_buf->rbuf, history->ptr, history->slen);
+	recv_buf->rbuf[history->slen] = 0;
+	recv_buf->len = history->slen;
+	recv_buf->cur_pos = recv_buf->len;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_left_key(console_recv_buf *recv_buf)
+{
+    const static unsigned char BACK_SPACE = 0x08;
+    if (recv_buf->cur_pos) {
+	printf("%c", BACK_SPACE);
+	--recv_buf->cur_pos;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_right_key(console_recv_buf *recv_buf)
+{
+    if (recv_buf_right_len(recv_buf)) {
+	unsigned char *data = &recv_buf->rbuf[recv_buf->cur_pos++];
+	printf("%c", *data);
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static int readchar_thread(void * p)
+{    
     struct cli_console_fe * fe = (struct cli_console_fe *)p;
-    int i;
+    cmd_parse_state parse_state = ST_NORMAL;
+
+    printf("%s", fe->cfg.prompt_str.ptr);
 
     while (!fe->thread_quit) {
-        fgets(fe->input.buf, fe->input.maxlen, stdin);
-        for (i = pj_ansi_strlen(fe->input.buf) - 1; i >= 0; i--) {
-            if (fe->input.buf[i] == '\n' || fe->input.buf[i] == '\r')
-                fe->input.buf[i] = 0;
-            else
-                break;
-        }
-        pj_sem_post(fe->input.sem);
-        /* Sleep until the next call of pj_cli_console_readline() */
+	unsigned char cdata;
+	console_recv_buf *recv_buf = &fe->input.recv_buf;
+	pj_bool_t is_valid = PJ_TRUE;
+
+	cdata = (unsigned char)getch();
+
+	switch (parse_state) {
+	case ST_NORMAL:
+	    if (cdata == '\b') {
+		is_valid = handle_backspace(recv_buf);
+	    } else if (cdata == 224) {	    
+		parse_state = ST_SCANMODE;
+	    } else if (cdata == 27) {	    
+		parse_state = ST_ESC;
+	    } else {
+		if (cdata == '\n')
+		    cdata = '\r';
+		if (recv_buf_insert(recv_buf, &cdata)) {
+		    if (cdata == '\r') {
+			is_valid = handle_return(fe->sess);
+		    } else if ((cdata == '\t') || (cdata == '?')) {
+			is_valid = handle_tab(fe->sess);
+		    } else if (cdata > 31 && cdata < 127) {
+			is_valid = handle_alfa_num(recv_buf, &cdata);			
+		    }
+		} else {
+		    is_valid = PJ_FALSE;
+		}
+	    }	    
+	    break;
+	case ST_SCANMODE:
+	    switch (cdata) {
+	    case 72:
+		//UP
+	    case 80:
+		//DOwN
+		is_valid = handle_up_down(fe->sess, (cdata==72));
+		break;
+	    case 75:
+		is_valid = handle_left_key(recv_buf);
+		//LEFT
+		break;
+	    case 77:
+		is_valid = handle_right_key(recv_buf);
+		//RIGHT
+		break;
+	    };
+	    parse_state = ST_NORMAL;
+	    break;
+	case ST_ESC:
+	    parse_state = (cdata == 91)?ST_ARROWMODE:ST_NORMAL;
+	    break;
+	case ST_ARROWMODE:
+	    switch (cdata) {
+	    case 65:
+		//UP
+	    case 66:
+		//DOwN
+		is_valid = handle_up_down(fe->sess, (cdata==65));
+		break;
+	    case 68:
+		is_valid = handle_left_key(recv_buf);
+		//LEFT
+		break;
+	    case 67:
+		is_valid = handle_right_key(recv_buf);
+		//RIGHT
+		break;
+	    };
+	    parse_state = ST_NORMAL;	    
+	    break;
+	};
+
+        pj_sem_post(fe->input.sem);        
         pj_sem_wait(fe->thread_sem);
     }
@@ -158,24 +835,19 @@
 }
 
-PJ_DEF(pj_status_t) pj_cli_console_readline(pj_cli_sess *sess,
-					    char *buf,
-					    unsigned maxlen)
+PJ_DEF(pj_status_t) pj_cli_console_process(pj_cli_sess *sess)
 {
     struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;
 
-    PJ_ASSERT_RETURN(sess && buf, PJ_EINVAL);
-
-    fe->input.buf = buf;
-    fe->input.maxlen = maxlen;
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
 
     if (!fe->input_thread) {
         pj_status_t status;
 
-        status = pj_thread_create(fe->pool, NULL, &readline_thread, fe,
+        status = pj_thread_create(fe->pool, NULL, &readchar_thread, fe,
                                   0, 0, &fe->input_thread);
         if (status != PJ_SUCCESS)
             return status;
     } else {
-        /* Wake up readline thread */
+        /* Wake up readchar thread */
         pj_sem_post(fe->thread_sem);
     }
Index: /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c
===================================================================
--- /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c	(revision 4298)
+++ /pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c	(revision 4299)
@@ -42,18 +42,202 @@
 #endif
 
-struct cli_telnet_sess
-{
-    pj_cli_sess         base;
-    pj_pool_t          *pool;
-    pj_activesock_t    *asock;
-    pj_bool_t           authorized;
-    pj_ioqueue_op_key_t op_key;
-    pj_mutex_t         *smutex;
-
-    char                rcmd[PJ_CLI_MAX_CMDBUF];
-    int                 len;
-    char                buf[CLI_TELNET_BUF_SIZE + MAX_CUT_MSG_LEN];
-    unsigned            buf_len;
+#define MAX_CLI_TELNET_OPTIONS 256
+
+/**
+ * This specify the state for the telnet option negotiation.
+ */
+enum cli_telnet_option_states 
+{    
+    OPT_DISABLE,		/* Option disable */
+    OPT_ENABLE,			/* Option enable */
+    OPT_EXPECT_DISABLE,		/* Already send disable req, expecting resp */
+    OPT_EXPECT_ENABLE,		/* Already send enable req, expecting resp */
+    OPT_EXPECT_DISABLE_REV,	/* Already send disable req, expecting resp, 
+				 * need to send enable req */
+    OPT_EXPECT_ENABLE_REV	/* Already send enable req, expecting resp,
+				 * need to send disable req */
 };
+
+/**
+ * This structure contains information for telnet session option negotiation.
+ * It contains the local/peer option config and the option negotiation state.
+ */
+typedef struct cli_telnet_sess_option
+{
+    /**
+     * Local setting for the option.
+     * Default: FALSE;
+     */
+    pj_bool_t local_is_enable;
+
+    /**
+     * Remote setting for the option.
+     * Default: FALSE;
+     */
+    pj_bool_t peer_is_enable;
+
+    /**
+     * Local state of the option negotiation.
+     */
+    enum cli_telnet_option_states local_state;
+
+    /**
+     * Remote state of the option negotiation.
+     */
+    enum cli_telnet_option_states peer_state;
+} cli_telnet_sess_option;
+
+/**
+ * This specify the state of input character parsing.
+ */
+typedef enum cmd_parse_state
+{
+    ST_NORMAL,
+    ST_CR,
+    ST_ESC,
+    ST_VT100,
+    ST_IAC,
+    ST_DO,
+    ST_DONT,
+    ST_WILL,
+    ST_WONT
+} cmd_parse_state;
+
+typedef enum cli_telnet_command
+{
+    SUBNEGO_END	    = 240,	/* End of subnegotiation parameters. */
+    NOP		    = 241,	/* No operation. */
+    DATA_MARK	    = 242,	/* Marker for NVT cleaning. */
+    BREAK	    = 243,	/* Indicates that the "break" key was hit. */
+    INT_PROCESS	    = 244,	/* Suspend, interrupt or abort the process. */
+    ABORT_OUTPUT    = 245,	/* Abort output, abort output stream. */
+    ARE_YOU_THERE   = 246,	/* Are you there. */
+    ERASE_CHAR	    = 247,	/* Erase character, erase the current char. */
+    ERASE_LINE	    = 248,	/* Erase line, erase the current line. */
+    GO_AHEAD	    = 249,	/* Go ahead, other end can transmit. */
+    SUBNEGO_BEGIN   = 250,	/* Subnegotiation begin. */
+    WILL	    = 251,	/* Accept the use of option. */
+    WONT	    = 252,	/* Refuse the use of option. */
+    DO		    = 253,	/* Request to use option. */
+    DONT	    = 254,	/* Request to not use option. */
+    IAC		    = 255	/* Interpret as command */
+} cli_telnet_command;
+
+enum cli_telnet_options
+{
+    TRANSMIT_BINARY	= 0,	/* Transmit Binary. */
+    ECHO		= 1,	/* Echo. */
+    RECONNECT		= 2,	/* Reconnection. */
+    SUPPRESS_GA		= 3,	/* Suppress Go Aheah. */
+    MESSAGE_SIZE_NEGO	= 4,	/* Approx Message Size Negotiation. */
+    STATUS		= 5,	/* Status. */
+    TIMING_MARK		= 6,	/* Timing Mark. */
+    RTCE_OPTION		= 7,	/* Remote Controlled Trans and Echo. */
+    OUTPUT_LINE_WIDTH	= 8,	/* Output Line Width. */
+    OUTPUT_PAGE_SIZE	= 9,	/* Output Page Size. */
+    CR_DISPOSITION	= 10,	/* Carriage-Return Disposition. */
+    HORI_TABSTOPS	= 11,	/* Horizontal Tabstops. */
+    HORI_TAB_DISPO	= 12,	/* Horizontal Tab Disposition. */
+    FF_DISP0		= 13,	/* Formfeed Disposition. */
+    VERT_TABSTOPS	= 14,	/* Vertical Tabstops. */
+    VERT_TAB_DISPO	= 15,	/* Vertical Tab Disposition. */
+    LF_DISP0		= 16,	/* Linefeed Disposition. */
+    EXT_ASCII		= 17, 	/* Extended ASCII. */
+    LOGOUT		= 18,	/* Logout. */
+    BYTE_MACRO		= 19,	/* Byte Macro. */
+    DE_TERMINAL		= 20,	/* Data Entry Terminal. */
+    SUPDUP_PROTO	= 21,	/* SUPDUP Protocol. */
+    SUPDUP_OUTPUT	= 22,	/* SUPDUP Output. */
+    SEND_LOC		= 23,	/* Send Location. */
+    TERM_TYPE		= 24,	/* Terminal Type. */
+    EOR			= 25,	/* End of Record. */
+    TACACS_UID		= 26,	/* TACACS User Identification. */
+    OUTPUT_MARKING	= 27,	/* Output Marking. */
+    TTYLOC		= 28,	/* Terminal Location Number. */
+    USE_3270_REGIME	= 29,	/* Telnet 3270 Regime. */
+    USE_X3_PAD		= 30,	/* X.3 PAD. */
+    WINDOW_SIZE		= 31,	/* Window Size. */
+    TERM_SPEED		= 32,	/* Terminal Speed. */
+    REM_FLOW_CONTROL	= 33,	/* Remote Flow Control. */
+    LINE_MODE		= 34,	/* Linemode. */
+    X_DISP_LOC		= 35,	/* X Display Location. */
+    ENVIRONMENT		= 36,	/* Environment. */
+    AUTH		= 37,	/* Authentication. */
+    ENCRYPTION		= 38, 	/* Encryption Option. */
+    NEW_ENVIRONMENT	= 39,	/* New Environment. */
+    TN_3270E		= 40,	/* TN3270E. */
+    XAUTH		= 41,	/* XAUTH. */
+    CHARSET		= 42,	/* CHARSET. */
+    REM_SERIAL_PORT	= 43,	/* Telnet Remote Serial Port. */
+    COM_PORT_CONTROL	= 44,	/* Com Port Control. */
+    SUPP_LOCAL_ECHO	= 45,	/* Telnet Suppress Local Echo. */
+    START_TLS		= 46,	/* Telnet Start TLS. */
+    KERMIT		= 47,	/* KERMIT. */
+    SEND_URL		= 48,	/* SEND-URL. */
+    FWD_X		= 49,	/* FORWARD_X. */
+    EXT_OPTIONS		= 255	/* Extended-Options-List */
+};
+
+enum terminal_cmd 
+{
+    TC_ESC		= 27,
+    TC_UP		= 65,
+    TC_DOWN		= 66,
+    TC_RIGHT		= 67,
+    TC_LEFT		= 68,
+    TC_END		= 70,
+    TC_HOME		= 72,    
+    TC_CTRL_C		= 3,
+    TC_CR		= 13,
+    TC_BS		= 8,
+    TC_TAB		= 9,
+    TC_QM		= 63,
+    TC_BELL		= 7,
+    TC_DEL		= 127
+};
+
+/**
+ * This structure contains the command line shown to the user.  
+ */
+typedef struct telnet_recv_buf {
+    /**
+     * Buffer containing the characters, NULL terminated.
+     */
+    unsigned char	    rbuf[PJ_CLI_MAX_CMDBUF];
+
+    /**
+     * Current length of the command line.
+     */
+    unsigned		    len;
+
+    /**
+     * Current cursor position.
+     */
+    unsigned		    cur_pos;
+} telnet_recv_buf;
+
+typedef struct cmd_history
+{
+    PJ_DECL_LIST_MEMBER(struct cmd_history);
+    pj_str_t command;
+} cmd_history;
+
+typedef struct cli_telnet_sess
+{
+    pj_cli_sess		    base;
+    pj_pool_t		    *pool;
+    pj_activesock_t	    *asock;
+    pj_bool_t		    authorized;
+    pj_ioqueue_op_key_t	    op_key;
+    pj_mutex_t		    *smutex;
+    cmd_parse_state	    parse_state;
+    cli_telnet_sess_option  telnet_option[MAX_CLI_TELNET_OPTIONS];
+    cmd_history		    *history;
+    cmd_history		    *active_history;
+
+    telnet_recv_buf	    *rcmd;
+    unsigned char	    buf[CLI_TELNET_BUF_SIZE + MAX_CUT_MSG_LEN];
+    unsigned		    buf_len;
+} cli_telnet_sess;
 
 struct cli_telnet_fe
@@ -71,4 +255,832 @@
 };
 
+/* Forward Declaration */
+static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
+                                     const unsigned char *str, int len);
+
+static pj_status_t telnet_sess_send(cli_telnet_sess *sess,
+                                    const pj_str_t *str);
+
+/**
+ * Return the number of characters between the current cursor position 
+ * to the end of line.
+ */
+static unsigned recv_buf_right_len(telnet_recv_buf *recv_buf)
+{
+    return (recv_buf->len - recv_buf->cur_pos);
+}
+
+static pj_bool_t recv_buf_insert(telnet_recv_buf *recv_buf, 
+				 unsigned char *data) 
+{    
+    if (recv_buf->len+1 >= PJ_CLI_MAX_CMDBUF) {
+	return PJ_FALSE;
+    } else {	
+	if (*data == '\t' || *data == '?' || *data == '\r') {
+	    /* Always insert to the end of line */
+	    recv_buf->rbuf[recv_buf->len] = *data;
+	} else {
+	    /* Insert based on the current cursor pos */
+	    unsigned cur_pos = recv_buf->cur_pos;
+	    unsigned rlen = recv_buf_right_len(recv_buf);	    
+	    if (rlen > 0) {		    
+		/* Shift right characters */
+		pj_memmove(&recv_buf->rbuf[cur_pos+1], 
+			   &recv_buf->rbuf[cur_pos], 
+			   rlen+1);
+	    } 
+	    recv_buf->rbuf[cur_pos] = *data;
+	}
+	++recv_buf->cur_pos;
+	++recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }
+    return PJ_TRUE;
+}
+
+static pj_bool_t recv_buf_delete(telnet_recv_buf *recv_buf)
+{
+    if ((recv_buf->len == 0) || (recv_buf_right_len(recv_buf) == 0)) {
+	return PJ_FALSE;
+    } else {
+	unsigned rlen;
+	unsigned cur_pos = recv_buf->cur_pos;
+	rlen = recv_buf_right_len(recv_buf)-1;
+	pj_memmove(&recv_buf->rbuf[cur_pos], &recv_buf->rbuf[cur_pos+1], 
+		   rlen);
+	--recv_buf->cur_pos;
+	--recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }
+    return PJ_TRUE;
+}
+
+static pj_bool_t recv_buf_backspace(telnet_recv_buf *recv_buf)
+{
+    if ((recv_buf->cur_pos == 0) || (recv_buf->len == 0)) {
+	return PJ_FALSE;
+    } else {
+	unsigned rlen = recv_buf_right_len(recv_buf);
+	if (rlen) {
+	    unsigned cur_pos = recv_buf->cur_pos;
+	    pj_memmove(&recv_buf->rbuf[cur_pos-1], &recv_buf->rbuf[cur_pos], 
+		       rlen);
+	}
+	--recv_buf->cur_pos;
+	--recv_buf->len;
+	recv_buf->rbuf[recv_buf->len] = 0;
+    }    
+    return PJ_TRUE;
+}
+
+static int compare_str(void *value, const pj_list_type *nd)
+{
+    cmd_history *node = (cmd_history*)nd;
+    return (pj_strcmp((pj_str_t *)value, &node->command));
+}
+
+static pj_status_t insert_history(cli_telnet_sess *sess, 				 
+				  char *cmd_val)
+{
+    cmd_history *in_history;
+    pj_str_t cmd;
+    cmd.ptr = cmd_val;
+    cmd.slen = pj_ansi_strlen(cmd_val)-1;
+
+    if (cmd.slen == 0)
+	return PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
+
+    /* Find matching history */
+    in_history = pj_list_search(sess->history, (void*)&cmd, compare_str);
+    if (!in_history) {
+	if (pj_list_size(sess->history) < PJ_CLI_MAX_CMD_HISTORY) {	    
+	    char *data_history;
+	    in_history = PJ_POOL_ZALLOC_T(sess->pool, cmd_history);
+	    pj_list_init(in_history);
+	    data_history = (char *)pj_pool_calloc(sess->pool, 
+			   sizeof(char), PJ_CLI_MAX_CMDBUF);
+	    in_history->command.ptr = data_history;
+	    in_history->command.slen = 0;
+	} else {
+	    /* Get the oldest history */
+	    in_history = sess->history->prev;
+	}	    
+    } else {
+	pj_list_insert_nodes_after(in_history->prev, in_history->next);
+    }
+    pj_strcpy(&in_history->command, pj_strtrim(&cmd));
+    pj_list_push_front(sess->history, in_history);
+    sess->active_history = sess->history;
+
+    return PJ_SUCCESS;
+}
+
+static pj_str_t* get_prev_history(cli_telnet_sess *sess, pj_bool_t is_forward)
+{
+    pj_str_t *retval;
+    pj_size_t history_size;
+    cmd_history *node;
+    cmd_history *root;
+
+    PJ_ASSERT_RETURN(sess, NULL);
+
+    node = sess->active_history;
+    root = sess->history;
+    history_size = pj_list_size(sess->history);
+
+    if (history_size == 0) {
+	return NULL;
+    } else {
+	if (is_forward) {	    
+	    node = (node->next==root)?node->next->next:node->next;	    
+	} else {
+	    node = (node->prev==root)?node->prev->prev:node->prev;	    
+	}
+	retval = &node->command;
+	sess->active_history = node;
+    }
+    return retval;
+}
+
+static pj_bool_t send_telnet_cmd(cli_telnet_sess *sess, 
+				 cli_telnet_command cmd, 
+				 unsigned char option)
+{       
+    unsigned char buf[3];
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    buf[0] = IAC;
+    buf[1] = cmd;
+    buf[2] = option;
+    telnet_sess_send2(sess, buf, 3);
+
+    return PJ_TRUE;
+}
+
+/**
+ * This method will handle sending telnet's ENABLE option negotiation.
+ * For local option: send WILL.
+ * For remote option: send DO.
+ * This method also handle the state transition of the ENABLE 
+ * negotiation process.
+ */
+static pj_bool_t send_enable_option(cli_telnet_sess *sess, 
+				    pj_bool_t is_local, 
+				    unsigned char option)
+{
+    cli_telnet_sess_option *sess_option;
+    enum cli_telnet_option_states *state;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_option = &sess->telnet_option[option];
+    state = is_local?(&sess_option->local_state):(&sess_option->peer_state);
+    switch (*state) {
+	case OPT_ENABLE:
+	    /* Ignore if already enabled */
+	    break;
+	case OPT_DISABLE:
+	    *state = OPT_EXPECT_ENABLE;
+	    send_telnet_cmd(sess, (is_local?WILL:DO), option);
+	    break;
+	case OPT_EXPECT_ENABLE:
+	    *state = OPT_DISABLE;
+	    break;
+	case OPT_EXPECT_DISABLE:
+	    *state = OPT_EXPECT_DISABLE_REV;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:
+	    *state = OPT_EXPECT_ENABLE;
+	    break;
+	case OPT_EXPECT_DISABLE_REV:	    
+	    *state = OPT_DISABLE;
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;
+}
+
+/**
+ * This method will handle sending telnet's DISABLE option negotiation.
+ * For local option: send WON'T.
+ * For remote option: send DON'T.
+ * This method also handle the state transition of the DISABLE 
+ * negotiation process. 
+ */
+static pj_bool_t send_disable_option(cli_telnet_sess *sess, 
+				     pj_bool_t is_local, 
+				     unsigned char option)
+{
+    cli_telnet_sess_option *sess_option;
+    enum cli_telnet_option_states *state;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_option = &sess->telnet_option[option];
+    state = is_local?(&sess_option->local_state):(&sess_option->peer_state);
+    switch (*state) {
+	case OPT_ENABLE:
+	    *state = OPT_EXPECT_DISABLE;
+	    send_telnet_cmd(sess, (is_local?WONT:DONT), option);
+	    break;
+	case OPT_DISABLE:
+	    /* Ignore if already disabled */
+	    break;
+	case OPT_EXPECT_ENABLE:
+	    *state = OPT_EXPECT_ENABLE_REV;
+	    break;
+	case OPT_EXPECT_DISABLE:
+	    *state = OPT_ENABLE;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:	    
+	    *state = OPT_ENABLE;
+	    break;
+	case OPT_EXPECT_DISABLE_REV:
+	    *state = OPT_EXPECT_DISABLE;
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;
+}
+
+static pj_bool_t send_cmd_do(cli_telnet_sess *sess, 
+			     unsigned char option)
+{
+    return send_enable_option(sess, PJ_FALSE, option);
+}
+
+static pj_bool_t send_cmd_dont(cli_telnet_sess *sess, 
+			       unsigned char option)
+{
+    return send_disable_option(sess, PJ_FALSE, option);
+}
+
+static pj_bool_t send_cmd_will(cli_telnet_sess *sess, 
+			       unsigned char option)
+{
+    return send_enable_option(sess, PJ_TRUE, option);
+}
+
+static pj_bool_t send_cmd_wont(cli_telnet_sess *sess, 
+			       unsigned char option)
+{
+    return send_disable_option(sess, PJ_TRUE, option);
+}
+
+/**
+ * This method will handle receiving telnet's ENABLE option negotiation.
+ * This method also handle the state transition of the ENABLE 
+ * negotiation process. 
+ */
+static pj_bool_t receive_enable_option(cli_telnet_sess *sess, 
+				       pj_bool_t is_local, 
+				       unsigned char option)
+{
+    cli_telnet_sess_option *sess_opt;
+    enum cli_telnet_option_states *state;
+    pj_bool_t opt_ena;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_opt = &sess->telnet_option[option];
+    state = is_local?(&sess_opt->local_state):(&sess_opt->peer_state);
+    opt_ena = is_local?sess_opt->local_is_enable:sess_opt->peer_is_enable;
+    switch (*state) {
+	case OPT_ENABLE:
+	    /* Ignore if already enabled */
+    	    break;
+	case OPT_DISABLE:
+	    if (opt_ena) {
+		*state = OPT_ENABLE;
+		send_telnet_cmd(sess, is_local?WILL:DO, option);
+	    } else {
+		send_telnet_cmd(sess, is_local?WONT:DONT, option);
+	    }
+	    break;
+	case OPT_EXPECT_ENABLE:	    
+	    *state = OPT_ENABLE;
+	    break;
+	case OPT_EXPECT_DISABLE:	    
+	    *state = OPT_DISABLE;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:
+	    *state = OPT_EXPECT_DISABLE;
+	    send_telnet_cmd(sess, is_local?WONT:DONT, option);	    
+	    break;
+	case OPT_EXPECT_DISABLE_REV:
+	    *state = OPT_EXPECT_DISABLE;
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;        
+}
+
+/**
+ * This method will handle receiving telnet's DISABLE option negotiation.
+ * This method also handle the state transition of the DISABLE 
+ * negotiation process. 
+ */
+static pj_bool_t receive_disable_option(cli_telnet_sess *sess, 
+					pj_bool_t is_local, 
+					unsigned char option)
+{
+    cli_telnet_sess_option *sess_opt;
+    enum cli_telnet_option_states *state;
+    pj_bool_t opt_ena;
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    sess_opt = &sess->telnet_option[option];
+    state = is_local?(&sess_opt->local_state):(&sess_opt->peer_state);
+    opt_ena = is_local?sess_opt->local_is_enable:sess_opt->peer_is_enable;
+    switch (*state) {
+	case OPT_ENABLE:
+	    /* Disabling option always need to be accepted */
+	    *state = OPT_DISABLE;
+	    send_telnet_cmd(sess, is_local?WONT:DONT, option);
+    	    break;
+	case OPT_DISABLE:
+	    /* Ignore if already enabled */
+	    break;
+	case OPT_EXPECT_ENABLE:	    
+	case OPT_EXPECT_DISABLE:
+	    *state = OPT_DISABLE;
+	    break;
+	case OPT_EXPECT_ENABLE_REV:
+	    *state = OPT_DISABLE;
+	    send_telnet_cmd(sess, is_local?WONT:DONT, option);	    
+	    break;
+	case OPT_EXPECT_DISABLE_REV:
+	    *state = OPT_EXPECT_ENABLE;
+	    send_telnet_cmd(sess, is_local?WILL:DO, option);
+	    break;
+	default:
+	    return PJ_FALSE;
+    }
+    return PJ_TRUE;       
+}
+
+static pj_bool_t receive_do(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_enable_option(sess, PJ_TRUE, option);
+}
+
+static pj_bool_t receive_dont(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_disable_option(sess, PJ_TRUE, option);
+}
+
+static pj_bool_t receive_will(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_enable_option(sess, PJ_FALSE, option);
+}
+
+static pj_bool_t receive_wont(cli_telnet_sess *sess, unsigned char option)
+{
+    return receive_disable_option(sess, PJ_FALSE, option);
+}
+
+static void set_local_option(cli_telnet_sess *sess, 
+			     unsigned char option, 
+			     pj_bool_t enable) 
+{
+    sess->telnet_option[option].local_is_enable = enable;
+}
+
+static void set_peer_option(cli_telnet_sess *sess, 
+			    unsigned char option, 
+			    pj_bool_t enable) 
+{
+    sess->telnet_option[option].peer_is_enable = enable;
+}
+
+static pj_bool_t is_local_option_state_ena(cli_telnet_sess *sess, 
+					   unsigned char option)
+{    
+    return (sess->telnet_option[option].local_state == OPT_ENABLE);
+}
+
+static pj_bool_t is_peer_option_state_ena(cli_telnet_sess *sess, 
+					  unsigned char option)
+{    
+    return (sess->telnet_option[option].peer_state == OPT_ENABLE);
+}
+
+static void send_return_key(cli_telnet_sess *sess) 
+{    
+    telnet_sess_send2(sess, (unsigned char*)"\r\n", 2);
+}
+
+static void send_bell(cli_telnet_sess *sess) {
+    unsigned char bell[1] = {0x07};
+    telnet_sess_send2(sess, &bell[0], 1);
+}
+
+static void send_prompt_str(cli_telnet_sess *sess)
+{
+    pj_str_t send_data;
+    char data_str[128];
+    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+
+    send_data.ptr = &data_str[0];
+    send_data.slen = 0;
+    
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+
+    telnet_sess_send(sess, &send_data);
+}
+
+static void send_err_arg(cli_telnet_sess *sess, 
+			 const pj_cli_exec_info *info, 
+			 const pj_str_t *msg,
+			 pj_bool_t with_return,
+			 pj_bool_t with_last_cmd)
+{
+    pj_str_t send_data;
+    char data_str[256];
+    unsigned len;
+    unsigned i;
+    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+
+    send_data.ptr = &data_str[0];
+    send_data.slen = 0;
+
+    if (with_return)
+	pj_strcat2(&send_data, "\r\n");
+
+    len = fe->cfg.prompt_str.slen + info->err_pos;
+
+    for (i=0;i<len;++i) {
+	pj_strcat2(&send_data, " ");
+    }
+    pj_strcat2(&send_data, "^");
+    pj_strcat2(&send_data, "\r\n");
+    pj_strcat(&send_data, msg);
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+    if (with_last_cmd)
+	pj_strcat2(&send_data, (char *)&sess->rcmd->rbuf[0]);
+
+    telnet_sess_send(sess, &send_data);
+}
+
+static void send_inv_arg(cli_telnet_sess *sess, 
+			 const pj_cli_exec_info *info,
+			 pj_bool_t with_return,
+			 pj_bool_t with_last_cmd)
+{
+    static const pj_str_t ERR_MSG = {"%Error : Invalid Arguments\r\n", 28};
+    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
+}
+
+static void send_too_many_arg(cli_telnet_sess *sess, 
+			      const pj_cli_exec_info *info,
+			      pj_bool_t with_return,
+			      pj_bool_t with_last_cmd)
+{
+    static const pj_str_t ERR_MSG = {"%Error : Too Many Arguments\r\n", 29};
+    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd);
+}
+
+static void send_ambi_arg(cli_telnet_sess *sess, 
+			  const pj_cli_exec_info *info,
+			  pj_bool_t with_return,
+			  pj_bool_t with_last_cmd)
+{
+    unsigned i;
+    pj_ssize_t j;
+    unsigned len;
+    pj_str_t send_data;
+    char data[1028];
+    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe;
+    const pj_cli_hint_info *hint = info->hint;
+    pj_bool_t is_process_sc = PJ_FALSE;
+    pj_bool_t valid_type = PJ_FALSE;
+    pj_ssize_t max_length = 0;
+    const pj_str_t sc_type = pj_str("SC");    
+    send_data.ptr = &data[0];
+    send_data.slen = 0;
+    
+    if (with_return)
+	pj_strcat2(&send_data, "\r\n");
+
+    len = fe->cfg.prompt_str.slen + info->err_pos;
+
+    for (i=0;i<len;++i) {
+	pj_strcat2(&send_data, " ");
+    }
+    pj_strcat2(&send_data, "^");
+    /* Get the max length of the command name */
+    for (i=0;i<info->hint_cnt;++i) {
+	if (hint[i].name.slen > max_length) {
+	    max_length = hint[i].name.slen;
+	}
+    }
+
+    for (i=0;i<info->hint_cnt;++i) {	
+	if ((&hint[i].type) && (hint[i].type.slen > 0)) {
+	    valid_type = PJ_TRUE;
+	    if (pj_stricmp(&hint[i].type, &sc_type) == 0) {
+		if (is_process_sc) {
+		    pj_strcat2(&send_data, ", ");
+		} else {
+		    pj_strcat2(&send_data, "\r\n\t Shorcut: ");
+		    is_process_sc = PJ_TRUE;
+		}
+		pj_strcat(&send_data, &hint[i].name);
+	    } else {
+		is_process_sc = PJ_FALSE;
+	    }
+	} else {	    
+	    valid_type = PJ_FALSE;
+	    is_process_sc = PJ_FALSE;
+	}
+
+	if (!is_process_sc) {
+	    pj_strcat2(&send_data, "\r\n\t");
+
+	    if (valid_type) {
+		pj_strcat2(&send_data, "<");
+		pj_strcat(&send_data, &hint[i].type);
+		pj_strcat2(&send_data, ">");	
+	    } else {
+		pj_strcat(&send_data, &hint[i].name);	    
+	    }
+    	
+	    if ((&hint[i].desc) && (hint[i].desc.slen > 0)) {
+		if (!valid_type) {
+		    for (j=0;j<(max_length-hint[i].name.slen);++j) {
+			pj_strcat2(&send_data, " ");
+		    }
+		}
+		pj_strcat2(&send_data, "\t");
+		pj_strcat(&send_data, &hint[i].desc);	    
+	    }
+	}
+    }   
+    pj_strcat2(&send_data, "\r\n");       
+    pj_strcat(&send_data, &fe->cfg.prompt_str);
+    if (with_last_cmd)
+	pj_strcat2(&send_data, (char *)&sess->rcmd->rbuf[0]);
+
+    telnet_sess_send(sess, &send_data);    
+}
+
+static void send_comp_arg(cli_telnet_sess *sess, 
+			  pj_cli_exec_info *info)
+{
+    pj_str_t send_data;
+    char data[128];
+
+    pj_strcat2(&info->hint[0].name, " ");
+
+    send_data.ptr = &data[0];
+    send_data.slen = 0;
+
+    pj_strcat(&send_data, &info->hint[0].name);        
+
+    telnet_sess_send(sess, &send_data);
+}
+
+static pj_bool_t handle_alfa_num(cli_telnet_sess *sess, unsigned char *data)
+{        
+    if (is_local_option_state_ena(sess, ECHO)) {
+	if (recv_buf_right_len(sess->rcmd) > 0) {
+	    /* Insert character */	    
+	    unsigned char echo[5] = {0x1b, 0x5b, 0x31, 0x40, 0x00};
+	    echo[4] = *data;
+	    telnet_sess_send2(sess, &echo[0], 5);
+	} else {
+	    /* Append character */
+	    telnet_sess_send2(sess, data, 1);
+	}
+	return PJ_TRUE;
+    } 
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_backspace(cli_telnet_sess *sess, unsigned char *data)
+{
+    unsigned rlen = recv_buf_right_len(sess->rcmd);
+    if (recv_buf_backspace(sess->rcmd)) {
+	if (rlen) {
+	    /* Cursor is not at the end of line */
+	    unsigned char echo[5] = {0x00, 0x1b, 0x5b, 0x31, 0x50};
+	    echo[0] = *data;
+	    telnet_sess_send2(sess, &echo[0], 5);
+	} else {
+	    const static unsigned char echo[3] = {0x08, 0x20, 0x08};	    
+	    telnet_sess_send2(sess, &echo[0], 3);
+	}
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_tab(cli_telnet_sess *sess)
+{
+    pj_status_t status;
+    pj_bool_t retval = PJ_TRUE;
+    unsigned len;
+    
+    pj_pool_t *pool;
+    pj_cli_cmd_val *cmd_val;
+    pj_cli_exec_info info;
+    pool = pj_pool_create(sess->pool->factory, "handle_tab",
+			  PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
+			  NULL);
+
+    cmd_val = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_val);
+    
+    status = pj_cli_sess_parse(&sess->base, (char *)&sess->rcmd->rbuf, cmd_val, 
+			       pool, &info);    
+
+    len = pj_ansi_strlen((char *)&sess->rcmd->rbuf[0]);
+
+    switch (status) {
+    case PJ_CLI_EINVARG:
+	send_inv_arg(sess, &info, PJ_TRUE, PJ_TRUE);		
+	break;
+    case PJ_CLI_ETOOMANYARGS:
+	send_too_many_arg(sess, &info, PJ_TRUE, PJ_TRUE);
+	break;
+    case PJ_CLI_EMISSINGARG:
+    case PJ_CLI_EAMBIGUOUS:
+	send_ambi_arg(sess, &info, PJ_TRUE, PJ_TRUE);
+	break;
+    case PJ_SUCCESS:
+	if (len > sess->rcmd->cur_pos)
+	{
+	    /* Send the cursor to EOL */
+	    unsigned rlen = len - sess->rcmd->cur_pos+1;
+	    unsigned char *data_sent = &sess->rcmd->rbuf[sess->rcmd->cur_pos-1];
+	    telnet_sess_send2(sess, data_sent, rlen);
+	}
+	if (info.hint_cnt > 0) {	
+	    /* Complete command */
+	    send_comp_arg(sess, &info);
+
+	    pj_memcpy(&sess->rcmd->rbuf[len], 
+		      &info.hint[0].name.ptr[0], info.hint[0].name.slen);
+
+	    len += info.hint[0].name.slen;
+	    sess->rcmd->rbuf[len] = 0;		    
+	} else {
+	    retval = PJ_FALSE;
+	}
+	break;
+    }
+    sess->rcmd->len = len;
+    sess->rcmd->cur_pos = sess->rcmd->len;
+
+    pj_pool_release(pool);	
+    return retval; 
+}
+
+static pj_bool_t handle_return(cli_telnet_sess *sess)
+{
+    pj_status_t status;
+    pj_bool_t retval = PJ_TRUE;
+    
+    pj_pool_t *pool;    
+    pj_cli_exec_info info;    
+    
+    send_return_key(sess);
+    insert_history(sess, (char *)&sess->rcmd->rbuf);
+
+    pool = pj_pool_create(sess->pool->factory, "handle_return",
+			  PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC,
+			  NULL);    
+    
+    status = pj_cli_sess_exec(&sess->base, (char *)&sess->rcmd->rbuf, 
+			      pool, &info);    
+
+    switch (status) {
+    case PJ_CLI_EINVARG:
+	send_inv_arg(sess, &info, PJ_FALSE, PJ_FALSE);	
+	break;
+    case PJ_CLI_ETOOMANYARGS:
+	send_too_many_arg(sess, &info, PJ_FALSE, PJ_FALSE);
+	break;
+    case PJ_CLI_EAMBIGUOUS:
+    case PJ_CLI_EMISSINGARG:
+	send_ambi_arg(sess, &info, PJ_FALSE, PJ_FALSE);
+	break;
+    case PJ_CLI_EEXIT:
+	retval = PJ_FALSE;
+	break;
+    case PJ_SUCCESS:
+	send_prompt_str(sess);
+	break;
+    }    
+    if (retval) {
+	sess->rcmd->rbuf[0] = 0;
+	sess->rcmd->len = 0;
+	sess->rcmd->cur_pos = sess->rcmd->len;
+    }
+
+    pj_pool_release(pool);	
+    return retval; 
+}
+
+static pj_bool_t handle_right_key(cli_telnet_sess *sess)
+{
+    if (recv_buf_right_len(sess->rcmd)) {
+	unsigned char *data = &sess->rcmd->rbuf[sess->rcmd->cur_pos++];
+	telnet_sess_send2(sess, data, 1);		
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_left_key(cli_telnet_sess *sess)
+{
+    const static unsigned char BACK_SPACE = 0x08;
+    if (sess->rcmd->cur_pos) {
+	telnet_sess_send2(sess, &BACK_SPACE, 1);
+	--sess->rcmd->cur_pos;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_bool_t handle_up_down(cli_telnet_sess *sess, pj_bool_t is_up)
+{
+    pj_str_t *history;
+
+    PJ_ASSERT_RETURN(sess, PJ_FALSE);
+
+    history = get_prev_history(sess, is_up);
+    if (history) {
+	pj_str_t send_data;
+	char str[PJ_CLI_MAX_CMDBUF];
+	send_data.ptr = &str[0];
+	send_data.slen = 0;
+
+	if (sess->rcmd->cur_pos > 0) {
+	    pj_memset(send_data.ptr, 0x08, sess->rcmd->cur_pos);
+	    send_data.slen = sess->rcmd->cur_pos;
+	}
+
+	if (sess->rcmd->len > (unsigned)history->slen) {
+	    unsigned buf_len = sess->rcmd->len;
+	    pj_memset(&send_data.ptr[send_data.slen], 0x20, buf_len);
+	    send_data.slen += buf_len;
+	    pj_memset(&send_data.ptr[send_data.slen], 0x08, buf_len);
+	    send_data.slen += buf_len;
+	} 
+	/* Send data */
+	pj_strcat(&send_data, history);
+	telnet_sess_send(sess, &send_data);
+	pj_ansi_strncpy((char*)&sess->rcmd->rbuf, history->ptr, history->slen);
+	sess->rcmd->rbuf[history->slen] = 0;
+	sess->rcmd->len = history->slen;
+	sess->rcmd->cur_pos = sess->rcmd->len;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static pj_status_t process_vt100_cmd(cli_telnet_sess *sess, 
+				     unsigned char *cmd)
+{    
+    pj_status_t status = PJ_TRUE;
+    switch (*cmd) {
+	case TC_ESC:
+	    break;
+	case TC_UP:
+	    status = handle_up_down(sess, PJ_TRUE);
+	    break;
+	case TC_DOWN:
+	    status = handle_up_down(sess, PJ_FALSE);
+	    break;
+	case TC_RIGHT:
+	    status = handle_right_key(sess);
+	    break;
+	case TC_LEFT:
+	    status = handle_left_key(sess);
+	    break;
+	case TC_END:
+	    break;
+	case TC_HOME:
+	    break;
+	case TC_CTRL_C:
+	    break;
+	case TC_CR:
+	    break;
+	case TC_BS:
+	    break;
+	case TC_TAB:
+	    break;
+	case TC_QM:
+	    break;
+	case TC_BELL:
+	    break;
+	case TC_DEL:
+	    break;
+    };
+    return status;
+}
+
 PJ_DEF(void) pj_cli_telnet_cfg_default(pj_cli_telnet_cfg *param)
 {
@@ -80,7 +1092,6 @@
 }
 
-
 /* Send a message to a telnet session */
-static pj_status_t telnet_sess_send(struct cli_telnet_sess *sess,
+static pj_status_t telnet_sess_send(cli_telnet_sess *sess,
                                     const pj_str_t *str)
 {
@@ -110,8 +1121,8 @@
             pj_memmove(sess->buf + sess->buf_len, str->ptr, clen);
         if (clen < sz) {
-            pj_ansi_snprintf(sess->buf + CLI_TELNET_BUF_SIZE,
+            pj_ansi_snprintf((char *)sess->buf + CLI_TELNET_BUF_SIZE,
                              MAX_CUT_MSG_LEN, CUT_MSG);
             sess->buf_len = CLI_TELNET_BUF_SIZE +
-                            pj_ansi_strlen(sess->buf + CLI_TELNET_BUF_SIZE);
+                            pj_ansi_strlen((char *)sess->buf+CLI_TELNET_BUF_SIZE);
         } else
             sess->buf_len += clen;
@@ -126,6 +1137,6 @@
 }
 
-static pj_status_t telnet_sess_send2(struct cli_telnet_sess *sess,
-                                     const char *str, int len)
+static pj_status_t telnet_sess_send2(cli_telnet_sess *sess,
+                                     const unsigned char *str, int len)
 {
     pj_str_t s;
@@ -137,5 +1148,5 @@
 static void cli_telnet_sess_destroy(pj_cli_sess *sess)
 {
-    struct cli_telnet_sess *tsess = (struct cli_telnet_sess *)sess;
+    cli_telnet_sess *tsess = (cli_telnet_sess *)sess;
     pj_mutex_t *mutex = ((struct cli_telnet_fe *)sess->fe)->mutex;
 
@@ -161,9 +1172,9 @@
     sess = tfe->sess_head.next;
     while (sess != &tfe->sess_head) {
-        struct cli_telnet_sess *tsess = (struct cli_telnet_sess *)sess;
-
-        sess = sess->next;
-        if (tsess->base.log_level > level && tsess->authorized)
-            telnet_sess_send2(tsess, data, len);
+        cli_telnet_sess *tsess = (cli_telnet_sess *)sess;
+
+        sess = sess->next;        
+	if (tsess->base.log_level > level)
+            telnet_sess_send2(tsess, (unsigned char *)data, len);
     }
     
@@ -214,11 +1225,11 @@
 				          pj_ssize_t sent)
 {
-    struct cli_telnet_sess *sess = (struct cli_telnet_sess *)
-                                    pj_activesock_get_user_data(asock);
+    cli_telnet_sess *sess = (cli_telnet_sess *)
+			    pj_activesock_get_user_data(asock);
 
     PJ_UNUSED_ARG(op_key);
 
     if (sent <= 0) {
-        pj_cli_end_session(&sess->base);
+        pj_cli_sess_end_session(&sess->base);
         return PJ_FALSE;
     }
@@ -232,5 +1243,5 @@
         if (telnet_sess_send2(sess, sess->buf, len) != PJ_SUCCESS) {
             pj_mutex_unlock(sess->smutex);
-            pj_cli_end_session(&sess->base);
+            pj_cli_sess_end_session(&sess->base);
             return PJ_FALSE;
         }
@@ -248,8 +1259,9 @@
 			                  pj_size_t *remainder)
 {
-    struct cli_telnet_sess *sess = (struct cli_telnet_sess *)
-                                    pj_activesock_get_user_data(asock);
+    cli_telnet_sess *sess = (cli_telnet_sess *)
+                            pj_activesock_get_user_data(asock);
     struct cli_telnet_fe *tfe = (struct cli_telnet_fe *)sess->base.fe;
-    char *cdata = (char*)data;
+    unsigned char *cdata = (unsigned char*)data;    
+    pj_status_t is_valid = PJ_TRUE;
 
     PJ_UNUSED_ARG(size);
@@ -257,5 +1269,5 @@
 
     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-        pj_cli_end_session(&sess->base);
+        pj_cli_sess_end_session(&sess->base);
         return PJ_FALSE;
     }
@@ -266,62 +1278,87 @@
     pj_mutex_lock(sess->smutex);
 
-    if (*cdata == 8) {                          // Backspace
-        if (sess->len > 0)
-            sess->len--;
-        if (telnet_sess_send2(sess, " \b", 2) != PJ_SUCCESS)
-            goto on_exit;
-    } else {
-        if (sess->len < PJ_CLI_MAX_CMDBUF - 1)
-            sess->rcmd[sess->len++] = *cdata;
-        if (*cdata == '\n') {
-            /* Trim trailing newlines */
-            for (; sess->len > 0;) {
-                if (sess->rcmd[sess->len - 1] == '\n' ||
-                    sess->rcmd[sess->len - 1] == '\r')
-                    sess->rcmd[--sess->len] = 0;
-                else
-                    break;
-            }
-            if (sess->len > 0 && sess->rcmd[sess->len - 1] != 0)
-                sess->rcmd[sess->len++] = 0;
-
-            /* If a password is necessary, check whether the supplied password
-             * is correct.
-             */
-            if (!sess->authorized && 
-                ((struct cli_telnet_fe *)sess->base.fe)->cfg.passwd.slen > 0)
-            {
-                if (pj_strcmp2(&tfe->cfg.passwd, sess->rcmd)) {
-                    pj_str_t str = pj_str("Wrong password!\r\n");
-
-                    telnet_sess_send(sess, &str);
-                    goto on_exit;
-                } else {
-                    /* Password is correct, send welcome message */
-                    if (telnet_sess_send(sess, &tfe->cfg.welcome_msg)
-                        != PJ_SUCCESS)
-                    {
-                        goto on_exit;
-                    }
-                    sess->authorized = PJ_TRUE;
-                }
-            } else {
-                pj_status_t status;
-                
-                pj_mutex_unlock(sess->smutex);
-                status = pj_cli_exec(&sess->base, sess->rcmd, NULL);
-                if (status == PJ_CLI_EEXIT)
-                    return PJ_FALSE;
-                pj_mutex_lock(sess->smutex);
-            }
-            sess->len = 0;
-        } else if (!sess->authorized && 
-            ((struct cli_telnet_fe *)sess->base.fe)->cfg.passwd.slen > 0 &&
-            *cdata != '\r')
-        {
-            if (telnet_sess_send2(sess, "\b ", 2) != PJ_SUCCESS)
-                goto on_exit;
-        }
-
+    switch (sess->parse_state) {
+	case ST_CR:
+	    sess->parse_state = ST_NORMAL;
+	    if (*cdata == 0 || *cdata == '\n')				
+		pj_mutex_unlock(sess->smutex);
+		is_valid = handle_return(sess);
+		if (!is_valid)		    
+		    return PJ_FALSE;
+		pj_mutex_lock(sess->smutex);	
+		break;
+	case ST_NORMAL:
+	    if (*cdata == IAC) {
+		sess->parse_state = ST_IAC;
+	    } else if (*cdata == '\b') {
+		is_valid = handle_backspace(sess, cdata);
+	    } else if (*cdata == 27) {		    
+		sess->parse_state = ST_ESC;
+	    } else { 
+		if (recv_buf_insert(sess->rcmd, cdata)) {
+		    if (*cdata == '\r') {
+			sess->parse_state = ST_CR;
+		    } else if ((*cdata == '\t') || (*cdata == '?')) {
+			is_valid = handle_tab(sess);
+		    } else if (*cdata > 31 && *cdata < 127) {
+			is_valid = handle_alfa_num(sess, cdata);
+		    }
+		} else {
+		    is_valid = PJ_FALSE;
+		}
+	    }
+	    break;
+	case ST_ESC:
+	    if (*cdata == 91) {		
+		sess->parse_state = ST_VT100;
+	    } else {
+		sess->parse_state = ST_NORMAL;
+	    }
+	    break;
+	case ST_VT100:
+	    sess->parse_state = ST_NORMAL;
+	    is_valid = process_vt100_cmd(sess, cdata);
+	    break;
+	case ST_IAC:
+	    switch ((unsigned) *cdata) {
+		case DO:
+		    sess->parse_state = ST_DO;
+		    break;
+		case DONT:
+		    sess->parse_state = ST_DONT;
+		    break;
+		case WILL:
+		    sess->parse_state = ST_WILL;
+		    break;
+		case WONT:
+		    sess->parse_state = ST_WONT;
+		    break;
+		default:
+		    sess->parse_state = ST_NORMAL;
+		    break;
+	    }
+	    break;
+	case ST_DO:
+	    receive_do(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	case ST_DONT:
+	    receive_dont(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	case ST_WILL:
+	    receive_will(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	case ST_WONT:
+	    receive_wont(sess, *cdata);
+	    sess->parse_state = ST_NORMAL;
+	    break;
+	default:
+	    sess->parse_state = ST_NORMAL;
+	    break;
+    }
+    if (!is_valid) {
+	send_bell(sess);
     }
 
@@ -329,9 +1366,4 @@
 
     return PJ_TRUE;
-
-on_exit:
-    pj_mutex_unlock(sess->smutex);
-    pj_cli_end_session(&sess->base);
-    return PJ_FALSE;
 }
 
@@ -345,5 +1377,5 @@
     pj_status_t sstatus;
     pj_pool_t *pool;
-    struct cli_telnet_sess *sess;
+    cli_telnet_sess *sess;
     pj_activesock_cb asock_cb;
 
@@ -364,5 +1396,5 @@
     }
 
-    sess = PJ_POOL_ZALLOC_T(pool, struct cli_telnet_sess);
+    sess = PJ_POOL_ZALLOC_T(pool, cli_telnet_sess);
     sess->pool = pool;
     sess->base.fe = &fe->base;
@@ -373,4 +1405,8 @@
     asock_cb.on_data_read = &telnet_sess_on_data_read;
     asock_cb.on_data_sent = &telnet_sess_on_data_sent;
+    sess->rcmd = PJ_POOL_ZALLOC_T(pool, telnet_recv_buf);
+    sess->history = PJ_POOL_ZALLOC_T(pool, struct cmd_history);
+    pj_list_init(sess->history);
+    sess->active_history = sess->history;
 
     sstatus = pj_mutex_create_recursive(pool, "mutex_telnet_sess",
@@ -387,21 +1423,25 @@
     }
 
-    /* Prompt for password if required, otherwise directly send
-     * a welcome message.
-     */
-    if (fe->cfg.passwd.slen) {
-        pj_str_t pwd = pj_str("Password: ");
-        if (telnet_sess_send(sess, &pwd) != PJ_SUCCESS)
-            goto on_exit;
-    } else {
-        if (pj_strlen(&fe->cfg.welcome_msg)) {
-            if (telnet_sess_send(sess, &fe->cfg.welcome_msg) != PJ_SUCCESS)
-                goto on_exit;
-        } else {
-            if (telnet_sess_send2(sess, " \b", 2) != PJ_SUCCESS)
-                goto on_exit;
-        }
-        sess->authorized = PJ_TRUE;
-    }
+    pj_memset(sess->telnet_option, 0, sizeof(sess->telnet_option));
+    set_local_option(sess, TRANSMIT_BINARY, PJ_TRUE);
+    set_local_option(sess, STATUS, PJ_TRUE);
+    set_local_option(sess, SUPPRESS_GA, PJ_TRUE);
+    set_local_option(sess, TIMING_MARK, PJ_TRUE);
+    set_local_option(sess, TERM_SPEED, PJ_TRUE);
+    set_local_option(sess, TERM_TYPE, PJ_TRUE);
+
+    set_peer_option(sess, TRANSMIT_BINARY, PJ_TRUE);
+    set_peer_option(sess, SUPPRESS_GA, PJ_TRUE);
+    set_peer_option(sess, STATUS, PJ_TRUE);
+    set_peer_option(sess, TIMING_MARK, PJ_TRUE);
+    set_peer_option(sess, ECHO, PJ_TRUE);    
+
+    send_cmd_do(sess, SUPPRESS_GA);
+    send_cmd_will(sess, ECHO);
+    send_cmd_will(sess, STATUS);   
+    send_cmd_will(sess, SUPPRESS_GA);
+
+    /* Send prompt string */
+    telnet_sess_send(sess, &fe->cfg.prompt_str);
 
     /* Start reading for input from the new telnet session */
@@ -462,5 +1502,4 @@
     fe->base.type = PJ_CLI_TELNET_FRONT_END;
     fe->base.op->on_write_log = &cli_telnet_fe_write_log;
-//    fe->base.op->on_quit = &cli_telnet_fe_quit;
     fe->base.op->on_destroy = &cli_telnet_fe_destroy;
     fe->pool = pool;
@@ -497,4 +1536,14 @@
         PJ_LOG(3, (THIS_FILE, "CLI telnet daemon listening at port %d",
                fe->cfg.port));
+	
+	if (fe->cfg.prompt_str.slen == 0) {
+	    pj_str_t prompt_sign = {"> ", 2};
+	    char *prompt_data = pj_pool_alloc(fe->pool, 
+					      pj_gethostname()->slen+2);
+	    fe->cfg.prompt_str.ptr = prompt_data;
+
+	    pj_strcpy(&fe->cfg.prompt_str, pj_gethostname());
+	    pj_strcat(&fe->cfg.prompt_str, &prompt_sign);
+	}	
     } else {
         PJ_LOG(3, (THIS_FILE, "Failed binding the socket"));
Index: /pjproject/branches/projects/cli/pjsip-apps/src/samples/clidemo.c
===================================================================
--- /pjproject/branches/projects/cli/pjsip-apps/src/samples/clidemo.c	(revision 4298)
+++ /pjproject/branches/projects/cli/pjsip-apps/src/samples/clidemo.c	(revision 4299)
@@ -34,5 +34,5 @@
  * Default: 1
  */
-#define USE_RANDOM_PORT 1
+#define USE_RANDOM_PORT 0
 
 struct cmd_xml_t {
@@ -82,8 +82,8 @@
 }
 
-static pj_status_t quit(pj_cli_cmd_val *cval)
+static pj_status_t quit_app(pj_cli_cmd_val *cval)
 {
     PJ_UNUSED_ARG(cval);
-    pj_cli_end_session(cval->sess);
+    pj_cli_quit(cval->sess->fe->cli, cval->sess, PJ_FALSE);
 
     return PJ_CLI_EEXIT;
@@ -92,25 +92,32 @@
 static struct cmd_xml_t cmd_xmls[] = {
     {"<CMD name='sayhello' id='1' sc='  ,h , ,, sh  ,' desc='Will say hello'>"
-     "  <ARGS>"
      "    <ARG name='whom' type='text' desc='Whom to say hello to'/>"
-     "  </ARGS>"
      "</CMD>",
      &sayhello},
     {"<CMD name='saybye' id='2' sc='b,sb' desc='Will say bye'>"
-     "  <ARGS>"
      "    <ARG name='whom' type='text' desc='Whom to say bye to'/>"
-     "  </ARGS>"
      "</CMD>",
      &saybye},
-    {"<CMD name=' say ' id='3' sc='s' desc='Will say something'>"
-     "  <ARGS>"
+    {"<CMD name='saymsg' id='3' sc='s' desc='Will say something'>"
      "    <ARG name='msg' type='text' desc='Message to say'/>"
      "    <ARG name='whom' type='text' desc='Whom to say to'/>"
-     "  </ARGS>"
      "</CMD>",
      &say},
-    {"<CMD name='quit' id='999' sc='q' desc='Quit the application'>"
-     "</CMD>",
-     &quit},
+    {"<CMD name='vid' id='1' desc='Video Command'>"
+     "   <CMD name='help' id='2' desc='Show Help' />"
+     "   <CMD name='enable' id='3' desc='Enable Video' />"
+     "   <CMD name='disable' id='4' desc='Disable Video' />"
+     "   <CMD name='call' id='5' desc='Video call' >"
+     "            <CMD name='add' id='6' desc='Add Call' />"
+     "            <CMD name='cap' id='7' desc='Capture Call' >"
+     "               <ARG name='streamno' type='int' desc='Stream Number' id='1'/>"
+     "               <ARG name='devid' type='int' desc='Device Id' optional='1' id='2'/>"
+     "            </CMD>"
+     "   </CMD>"     
+     "</CMD>",
+     NULL},
+    {"<CMD name='quit_app' id='999' sc='qa' desc='Quit the application'>"
+     "</CMD>",
+     &quit_app},
 };
 
@@ -128,5 +135,5 @@
     pj_str_t xml;
     pj_status_t status;
-    int i;
+    int i;        
 
     pj_init();
@@ -150,7 +157,7 @@
      */
     for (i = 0; i < sizeof(cmd_xmls)/sizeof(cmd_xmls[0]); i++) {
-        xml = pj_str(cmd_xmls[i].xml);
+        xml = pj_str(cmd_xmls[i].xml);	
         status = pj_cli_add_cmd_from_xml(cli, NULL, &xml, 
-                                         cmd_xmls[i].handler, NULL);
+                                         cmd_xmls[i].handler, NULL, NULL);
         if (status != PJ_SUCCESS)
 	    goto on_return;
@@ -210,13 +217,11 @@
      */
     for (;;) {
-	char cmdline[PJ_CLI_MAX_CMDBUF];
         pj_status_t status;
 
-        status = pj_cli_console_readline(sess, cmdline, sizeof(cmdline));
+        status = pj_cli_console_process(sess);
 	if (status != PJ_SUCCESS)
 	    break;
 
-//        pj_ansi_strcpy(cmdline, "sayhello {Teluu Inc.}");
-	status = pj_cli_exec(sess, cmdline, NULL);
+	//pj_ansi_strcpy(cmdline, "sayhello {Teluu Inc.}");	
 	if (status == PJ_CLI_EEXIT) {
 	    /* exit is called */
Index: /pjproject/branches/projects/cli/pjsip-apps/src/samples/debug.c
===================================================================
--- /pjproject/branches/projects/cli/pjsip-apps/src/samples/debug.c	(revision 4298)
+++ /pjproject/branches/projects/cli/pjsip-apps/src/samples/debug.c	(revision 4299)
@@ -29,4 +29,4 @@
  *  #include "playfile.c"
  */
-#include "icedemo.c"
+#include "clidemo.c"
 
