Changeset 4299


Ignore:
Timestamp:
Nov 23, 2012 10:30:55 AM (8 years ago)
Author:
riza
Message:

Re #1098: Additional implementation to command parsing, telnet front end, console front end

Location:
pjproject/branches/projects/cli
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli.h

    r3231 r4299  
    3434 * @defgroup PJLIB_UTIL_CLI Command Line Interface Framework 
    3535 * @{ 
    36  * 
     36 * A CLI framework features an interface for defining command specification,  
     37 * parsing, and executing a command.  
     38 * It also features an interface to communicate with various front-ends,  
     39 * such as console, telnet. 
     40 * 
     41\verbatim 
     42| vid help                  Show this help screen                             | 
     43| vid enable|disable        Enable or disable video in next offer/answer      | 
     44| vid call add              Add video stream for current call                 | 
     45| vid call cap N ID         Set capture dev ID for stream #N in current call  | 
     46| disable_codec g711|g722   Show this help screen                             | 
     47<CMD name='vid' id='0' desc=""> 
     48       <CMD name='help' id='0' desc='' /> 
     49       <CMD name='enable' id='0' desc='' /> 
     50       <CMD name='disable' id='0' desc='' /> 
     51       <CMD name='call' id='0' desc='' > 
     52                <CMD name='add' id='101' desc='...' /> 
     53                <CMD name='cap' id='102' desc='...' > 
     54                   <ARG name='streamno' type='int' desc='...' id='1'/> 
     55                   <ARG name='devid' type='int' optional='1' id='2'/> 
     56                </CMD> 
     57       </CMD> 
     58</CMD> 
     59<CMD name='disable_codec' id=0 desc=""> 
     60        <ARG name='codec_list' type='choice' id='3'> 
     61            <CHOICE value='g711'/> 
     62            <CHOICE value='g722'/> 
     63        </ARG> 
     64</CMD> 
     65\endverbatim  
    3766 */ 
    3867 
     
    5079 
    5180/** 
    52  * Reserved command id constants. 
    53  */ 
    54 typedef enum pj_cli_std_cmd_id 
    55 { 
    56     /** 
    57      * Constant to indicate an invalid command id. 
    58      */ 
    59     PJ_CLI_INVALID_CMD_ID = -1, 
    60  
    61     /** 
    62      * A special command id to indicate that a command id denotes 
    63      * a command group. 
    64      */ 
    65     PJ_CLI_CMD_ID_GROUP = -2 
    66  
    67 } pj_cli_std_cmd_id; 
    68  
    69  
    70 /** 
    7181 * This describes the parameters to be specified when creating a CLI 
    7282 * application with pj_cli_create(). Application MUST initialize this 
     
    96106} pj_cli_cfg; 
    97107 
    98  
    99 /** 
    100  * This describes the type of an argument (pj_cli_arg_spec). 
    101  */ 
    102 typedef enum pj_cli_arg_type 
    103 { 
    104     /** 
    105      * Unformatted string. 
    106      */ 
    107     PJ_CLI_ARG_TEXT, 
    108  
    109     /** 
    110      * An integral number. 
    111      */ 
    112     PJ_CLI_ARG_INT 
    113  
    114 } pj_cli_arg_type; 
    115  
    116 /** 
    117  * This structure describe the specification of a command argument. 
    118  */ 
    119 typedef struct pj_cli_arg_spec 
    120 { 
    121     /** 
    122      * Argument name. 
    123      */ 
    124     pj_str_t name; 
    125  
    126     /** 
    127      * Helpful description of the argument. This text will be used when 
    128      * displaying help texts for the command/argument. 
    129      */ 
    130     pj_str_t desc; 
    131  
    132     /** 
    133      * Argument type, which will be used for rendering the argument and 
    134      * to perform basic validation against an input value. 
    135      */ 
    136     pj_cli_arg_type type; 
    137  
    138 } pj_cli_arg_spec; 
    139  
     108/** 
     109 * Type of argument id. 
     110 */ 
     111typedef int pj_cli_arg_id; 
    140112 
    141113/** 
     
    153125 */ 
    154126typedef struct pj_cli_front_end pj_cli_front_end; 
     127 
     128/** 
     129 * Forward declaration for CLI argument spec structure. 
     130 */ 
     131typedef struct pj_cli_arg_spec pj_cli_arg_spec; 
    155132 
    156133/** 
     
    173150} pj_cli_cmd_val; 
    174151 
    175  
    176  
    177 /** 
    178  * This specifies the callback type for command handlers, which will be 
    179  * executed when the specified command is invoked. 
    180  * 
    181  * @param sess      The CLI session where the command is invoked. 
    182  * @param cmd_val   The command that is specified by the user. 
    183  * 
    184  * @return          Return the status of the command execution. 
    185  */ 
    186 typedef pj_status_t (*pj_cli_cmd_handler)(pj_cli_cmd_val *cval); 
    187  
    188 /** 
    189  * This structure describes the full specification of a CLI command. A CLI 
    190  * command mainly consists of the name of the command, zero or more arguments, 
    191  * and a callback function to be called to execute the command. 
    192  * 
    193  * Application can create this specification by forming an XML document and 
    194  * calling pj_cli_create_cmd_from_xml() to instantiate the spec. A sample XML 
    195  * document containing a command spec is as follows: 
    196  * 
    197  \verbatim 
    198   <CMD name='makecall' id='101' sc='m,mc' desc='Make outgoing call'> 
    199       <ARGS> 
    200           <ARG name='target' type='text' desc='The destination'/> 
    201       </ARGS> 
    202   </CMD> 
    203  \endverbatim 
    204  
    205  */ 
    206 struct pj_cli_cmd_spec 
     152/** 
     153 * This structure contains the hints information for the end user.  
     154 * This structure could contain either command or argument information. 
     155 * The front-end will format the information and present it to the user. 
     156 */ 
     157typedef struct pj_cli_hint_info 
    207158{ 
    208159    /** 
    209      * To make list of child cmds. 
    210      */ 
    211     PJ_DECL_LIST_MEMBER(struct pj_cli_cmd_spec); 
    212  
    213     /** 
    214      * Command ID assigned to this command by the application during command 
    215      * creation. If this value is PJ_CLI_CMD_ID_GROUP (-2), then this is 
    216      * a command group and it can't be executed. 
    217      */ 
    218     pj_cli_cmd_id id; 
    219  
    220     /** 
    221      * The command name. 
     160     * The hint value. 
    222161     */ 
    223162    pj_str_t name; 
    224163 
    225164    /** 
    226      * The full description of the command. 
     165     * The hint type. 
     166     */ 
     167    pj_str_t type; 
     168 
     169    /** 
     170     * Helpful description of the hint value.  
    227171     */ 
    228172    pj_str_t desc; 
    229173 
    230     /** 
    231      * Number of optional shortcuts 
    232      */ 
    233     unsigned sc_cnt; 
    234  
    235     /** 
    236      * Optional array of shortcuts, if any. Shortcut is a short name version 
    237      * of the command. If the command doesn't have any shortcuts, this 
    238      * will be initialized to NULL. 
    239      */ 
    240     pj_str_t *sc; 
    241  
    242     /** 
    243      * The command handler, to be executed when a command matching this command 
    244      * specification is invoked by the end user. The value may be NULL if this 
    245      * is a command group. 
    246      */ 
    247     pj_cli_cmd_handler handler; 
    248  
    249     /** 
    250      * Number of arguments. 
    251      */ 
    252     unsigned arg_cnt; 
    253  
    254     /** 
    255      * Array of arguments. 
    256      */ 
    257     pj_cli_arg_spec *arg; 
    258  
    259     /** 
    260      * Child commands, if any. A command will only have subcommands if it is 
    261      * a group. If the command doesn't have subcommands, this field will be 
    262      * initialized with NULL. 
    263      */ 
    264     pj_cli_cmd_spec *sub_cmd; 
    265 }; 
    266  
    267  
    268 /** 
    269  * This contains extra parameters to be specified when calling pj_cli_exec(). 
     174} pj_cli_hint_info; 
     175 
     176/** 
     177 * This structure contains extra information returned by pj_cli_sess_exec()/ 
     178 * pj_cli_sess_parse(). 
    270179 * Upon return from the function, various other fields in this structure will 
    271180 * be set by the function. 
    272  * 
    273  * Application must call pj_cli_exec_info_default() to initialize this 
    274  * structure with its default values. 
    275181 */ 
    276182typedef struct pj_cli_exec_info 
     
    296202 
    297203    /** 
    298      * If pj_cli_exec() fails because an argument is missing (the function 
    299      * returned PJ_CLI_EMISSINGARG error), this field will be set to the 
    300      * index of the missing argument. This is useful to give more helpful 
    301      * error info to the end user, or to facilitate a more interactive 
    302      * input display. 
    303      */ 
    304     int arg_idx; 
     204     * The number of hint elements 
     205     **/ 
     206    unsigned hint_cnt; 
     207 
     208    /** 
     209     * If pj_cli_sess_parse() fails because of a missing argument or ambigous  
     210     * command/argument, the function returned PJ_CLI_EMISSINGARG or  
     211     * PJ_CLI_EAMBIGUOUS error.  
     212     * This field will contain the hint information. This is useful to give  
     213     * helpful information to the end_user. 
     214     */ 
     215    pj_cli_hint_info hint[PJ_CLI_MAX_HINTS]; 
    305216 
    306217} pj_cli_exec_info; 
    307218 
     219/** 
     220 * This structure contains the information returned from the dynamic  
     221 * argument callback. 
     222 */ 
     223typedef struct pj_cli_arg_choice_val 
     224{ 
     225    /** 
     226     * The argument choice value 
     227     */ 
     228    pj_str_t value; 
     229 
     230    /** 
     231     * Helpful description of the choice value. This text will be used when 
     232     * displaying the help texts for the choice value 
     233     */ 
     234    pj_str_t desc; 
     235 
     236} pj_cli_arg_choice_val; 
     237 
     238/** 
     239 * This structure contains the parameters for pj_cli_arg_get_dyn_choice_val 
     240 */ 
     241typedef struct pj_cli_dyn_choice_param 
     242{ 
     243    /** 
     244     * The session on which the command was executed on. 
     245     */ 
     246    pj_cli_sess *sess; 
     247 
     248    /** 
     249     * The command being processed. 
     250     */ 
     251    pj_cli_cmd_spec *cmd; 
     252 
     253    /** 
     254     * The argument id. 
     255     */ 
     256    pj_cli_arg_id arg_id; 
     257 
     258    /** 
     259     * The maximum number of values that the choice can hold. 
     260     */ 
     261    unsigned max_cnt; 
     262 
     263    /** 
     264     * The pool to allocate memory from. 
     265     */ 
     266    pj_pool_t *pool; 
     267 
     268    /** 
     269     * The choice values count. 
     270     */ 
     271    unsigned cnt; 
     272 
     273    /** 
     274     * Array containing the valid choice values. 
     275     */ 
     276    pj_cli_arg_choice_val choice[PJ_CLI_MAX_CHOICE_VAL]; 
     277} pj_cli_dyn_choice_param; 
     278 
     279/** 
     280 * This specifies the callback type for argument handlers, which will be 
     281 * called to get the valid values of the choice type arguments. 
     282 */ 
     283typedef void (*pj_cli_arg_get_dyn_choice_val) (pj_cli_dyn_choice_param *param); 
     284 
     285/** 
     286 * This specifies the function to get the id of the specified command 
     287 *  
     288 * @param cmd           The specified command. 
     289 * 
     290 * @return              The command id 
     291 */ 
     292PJ_DECL(pj_cli_cmd_id) pj_cli_get_cmd_id(const pj_cli_cmd_spec *cmd); 
     293 
     294/** 
     295 * This specifies the callback type for command handlers, which will be 
     296 * executed when the specified command is invoked. 
     297 * 
     298 * @param cmd_val   The command that is specified by the user. 
     299 * 
     300 * @return          Return the status of the command execution. 
     301 */ 
     302typedef pj_status_t (*pj_cli_cmd_handler)(pj_cli_cmd_val *cval); 
    308303 
    309304/** 
     
    313308 */ 
    314309PJ_DECL(void) pj_cli_cfg_default(pj_cli_cfg *param); 
    315  
    316 /** 
    317  * Initialize pj_cli_exec_info with its default values. 
    318  * 
    319  * @param param         The param to be initialized. 
    320  */ 
    321 PJ_DECL(void) pj_cli_exec_info_default(pj_cli_exec_info *param); 
    322310 
    323311/** 
     
    336324 
    337325/** 
     326 * Write a log message to the specific CLI session.  
     327 * 
     328 * @param sess          The CLI active session. 
     329 * @param buffer        The message itself. 
     330 * @param len           Length of this message. 
     331 */ 
     332PJ_DECL(void) pj_cli_sess_write_msg(pj_cli_sess *sess,                                
     333                                    const char *buffer, 
     334                                    int len); 
     335 
     336/** 
    338337 * Create a new CLI application instance. 
    339338 * 
     
    345344PJ_DECL(pj_status_t) pj_cli_create(pj_cli_cfg *cfg, 
    346345                                   pj_cli_t **p_cli); 
    347  
    348346/** 
    349347 * Get the internal parameter of the CLI instance. 
     
    354352 */ 
    355353PJ_DECL(pj_cli_cfg*) pj_cli_get_param(pj_cli_t *cli); 
    356  
    357354 
    358355/** 
     
    361358 * application. 
    362359 * 
    363  * See also pj_cli_end_session() to end a session instead of quitting the 
     360 * See also pj_cli_sess_end_session() to end a session instead of quitting the 
    364361 * whole application. 
    365362 * 
     
    371368PJ_DECL(void) pj_cli_quit(pj_cli_t *cli, pj_cli_sess *req, 
    372369                          pj_bool_t restart); 
    373  
    374370/** 
    375371 * Check if application shutdown or restart has been requested. 
     
    380376 */ 
    381377PJ_DECL(pj_bool_t) pj_cli_is_quitting(pj_cli_t *cli); 
    382  
    383378 
    384379/** 
     
    392387PJ_DECL(pj_bool_t) pj_cli_is_restarting(pj_cli_t *cli); 
    393388 
    394  
    395389/** 
    396390 * Destroy a CLI application instance. This would also close all sessions 
     
    401395PJ_DECL(void) pj_cli_destroy(pj_cli_t *cli); 
    402396 
    403  
    404397/** 
    405398 * End the specified session, and destroy it to release all resources used 
     
    410403 * @param sess          The CLI session to be destroyed. 
    411404 */ 
    412 PJ_DECL(void) pj_cli_end_session(pj_cli_sess *sess); 
     405PJ_DECL(void) pj_cli_sess_end_session(pj_cli_sess *sess); 
    413406 
    414407/** 
     
    435428 * @param p_cmd         Optional pointer to store the newly created 
    436429 *                      specification. 
     430 * @param get_choice    Function handler for the argument. Specify this for  
     431 *                      dynamic choice type arguments. 
    437432 * 
    438433 * @return              PJ_SUCCESS on success, or the appropriate error code. 
     
    442437                                             const pj_str_t *xml, 
    443438                                             pj_cli_cmd_handler handler, 
    444                                              pj_cli_cmd_spec *p_cmd); 
     439                                             pj_cli_cmd_spec **p_cmd,  
     440                                             pj_cli_arg_get_dyn_choice_val get_choice); 
     441/** 
     442 * Initialize pj_cli_exec_info with its default values. 
     443 * 
     444 * @param param         The param to be initialized. 
     445 */ 
     446PJ_DECL(void) pj_cli_exec_info_default(pj_cli_exec_info *param); 
    445447 
    446448/** 
     
    465467 * @param cmdline       The command line string to be parsed. 
    466468 * @param val           Structure to store the parsing result. 
     469 * @param pool          The pool to allocate memory from. 
    467470 * @param info          Additional info to be returned regarding the parsing. 
    468471 * 
     
    472475 *                        - PJ_EINVAL: invalid parameter to this function. 
    473476 *                        - PJ_ENOTFOUND: command is not found. 
     477 *                        - PJ_CLI_EAMBIGUOUS: command/argument is ambiguous. 
    474478 *                        - PJ_CLI_EMISSINGARG: missing argument. 
    475479 *                        - PJ_CLI_EINVARG: invalid command argument. 
     
    478482 *                            application to end it's main loop. 
    479483 */ 
    480 PJ_DECL(pj_status_t) pj_cli_parse(pj_cli_sess *sess, 
    481                                   char *cmdline, 
    482                                   pj_cli_cmd_val *val, 
    483                                   pj_cli_exec_info *info); 
     484PJ_DECL(pj_status_t) pj_cli_sess_parse(pj_cli_sess *sess, 
     485                                       char *cmdline, 
     486                                       pj_cli_cmd_val *val, 
     487                                       pj_pool_t *pool, 
     488                                       pj_cli_exec_info *info); 
    484489 
    485490/** 
     
    487492 * the appropriate command and verify whether the string matches the command 
    488493 * specifications. If matches, the command will be executed, and the return 
    489  * value of the command will be set in the \a cmd_ret field of the \a eparam 
     494 * value of the command will be set in the \a cmd_ret field of the \a info 
    490495 * argument, if specified. 
    491496 * 
    492  * Please also see pj_cli_parse() for more info regarding the cmdline format. 
     497 * Please also see pj_cli_sess_parse() for more info regarding the cmdline  
     498 * format. 
    493499 * 
    494500 * @param sess          The CLI session. 
    495  * @param cmdline       The command line string to be executed. See the 
    496  *                      description of pj_cli_parse() API for more info 
    497  *                      regarding the cmdline format. 
     501 * @param cmdline       The command line string to be executed.  
     502 * @param pool          The pool to allocate memory from. 
    498503 * @param info          Optional pointer to receive additional information 
    499504 *                      related to the execution of the command (such as 
     
    504509 *                      of the handler itself will be returned in \a info 
    505510 *                      argument, if specified). Please see the return value 
    506  *                      of pj_cli_parse() for possible return values. 
    507  */ 
    508 pj_status_t pj_cli_exec(pj_cli_sess *sess, 
    509                         char *cmdline, 
    510                         pj_cli_exec_info *info); 
    511  
     511 *                      of pj_cli_sess_parse() for possible return values. 
     512 */ 
     513PJ_DECL(pj_status_t) pj_cli_sess_exec(pj_cli_sess *sess, 
     514                                      char *cmdline, 
     515                                      pj_pool_t *pool, 
     516                                      pj_cli_exec_info *info); 
    512517 
    513518/** 
  • pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_console.h

    r3231 r4299  
    5151    int log_level; 
    5252 
     53    /** 
     54     * Specify text message as a prompt string to user. 
     55     * 
     56     * Default: empty 
     57     */ 
     58    pj_str_t prompt_str; 
     59 
    5360} pj_cli_console_cfg; 
    5461 
     
    8390 
    8491/** 
    85  * Retrieve a cmdline from console stdin. Application should use this 
    86  * instead of the standard gets() or fgets(), since unlike these functions, 
    87  * this is able to end the blocking when pj_cli_quit() is called by other 
    88  * session. This function requires a thread support. 
    89  * 
    90  * Note that this function would also remove the trailing newlines from the 
    91  * input, if any. 
     92 * Retrieve a cmdline from console stdin and process the input accordingly. 
    9293 * 
    9394 * @param sess          The CLI session. 
     
    9798 * @return              PJ_SUCCESS if an input was read 
    9899 */ 
    99 PJ_DECL(pj_status_t) pj_cli_console_readline(pj_cli_sess *sess, 
    100                                              char *buf, 
    101                                              unsigned maxlen); 
     100PJ_DECL(pj_status_t) pj_cli_console_process(pj_cli_sess *sess); 
    102101 
    103102/** 
  • pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/cli_telnet.h

    r3231 r4299  
    7979    pj_str_t welcome_msg; 
    8080 
     81    /** 
     82     * Specify text message as a prompt string to user. 
     83     * 
     84     * Default: empty 
     85     */ 
     86    pj_str_t prompt_str; 
     87 
    8188} pj_cli_telnet_cfg; 
    8289 
  • pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/config.h

    r3231 r4299  
    304304 
    305305/** 
     306 * Maximum number of hints. 
     307 * Default: 32 
     308 */ 
     309#ifndef PJ_CLI_MAX_HINTS 
     310#   define PJ_CLI_MAX_HINTS             32 
     311#endif 
     312 
     313/** 
    306314 * Maximum short name version (shortcuts) for a command. 
    307315 * Default: 4 
     
    343351#endif 
    344352 
     353/** 
     354 * Maximum number of argument values of choice type. 
     355 * Default: 16 
     356 */ 
     357#ifndef PJ_CLI_MAX_CHOICE_VAL 
     358#   define PJ_CLI_MAX_CHOICE_VAL  16 
     359#endif 
     360 
     361/** 
     362 * Maximum number of command history. 
     363 * Default: 16 
     364 */ 
     365#ifndef PJ_CLI_MAX_CMD_HISTORY 
     366#   define PJ_CLI_MAX_CMD_HISTORY  16 
     367#endif 
    345368 
    346369/** 
  • pjproject/branches/projects/cli/pjlib-util/include/pjlib-util/errno.h

    r3231 r4299  
    394394 * @hideinitializer 
    395395 * End the current session. This is a special error code returned by 
    396  * pj_cli_exec() to indicate that "exit" or equivalent command has been 
     396 * pj_cli_sess_exec() to indicate that "exit" or equivalent command has been 
    397397 * called to end the current session. 
    398398 */ 
     
    426426/** 
    427427 * @hideinitializer 
     428 * CLI command entered by user match with more than one command/argument  
     429 * specification. 
     430 */ 
     431#define PJ_CLI_EAMBIGUOUS           (PJLIB_UTIL_ERRNO_START+207)/* 320207 */ 
     432/** 
     433 * @hideinitializer 
    428434 * Telnet connection lost. 
    429435 */ 
  • pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c

    r3231 r4299  
    4343#endif 
    4444 
     45/** 
     46 * This structure describes the full specification of a CLI command. A CLI 
     47 * command mainly consists of the name of the command, zero or more arguments, 
     48 * and a callback function to be called to execute the command. 
     49 * 
     50 * Application can create this specification by forming an XML document and 
     51 * calling pj_cli_create_cmd_from_xml() to instantiate the spec. A sample XML 
     52 * document containing a command spec is as follows: 
     53 */ 
     54struct pj_cli_cmd_spec 
     55{ 
     56    /** 
     57     * To make list of child cmds. 
     58     */ 
     59    PJ_DECL_LIST_MEMBER(struct pj_cli_cmd_spec); 
     60 
     61    /** 
     62     * Command ID assigned to this command by the application during command 
     63     * creation. If this value is PJ_CLI_CMD_ID_GROUP (-2), then this is 
     64     * a command group and it can't be executed. 
     65     */ 
     66    pj_cli_cmd_id id; 
     67 
     68    /** 
     69     * The command name. 
     70     */ 
     71    pj_str_t name; 
     72 
     73    /** 
     74     * The full description of the command. 
     75     */ 
     76    pj_str_t desc; 
     77 
     78    /** 
     79     * Number of optional shortcuts 
     80     */ 
     81    unsigned sc_cnt; 
     82 
     83    /** 
     84     * Optional array of shortcuts, if any. Shortcut is a short name version 
     85     * of the command. If the command doesn't have any shortcuts, this 
     86     * will be initialized to NULL. 
     87     */ 
     88    pj_str_t *sc; 
     89 
     90    /** 
     91     * The command handler, to be executed when a command matching this command 
     92     * specification is invoked by the end user. The value may be NULL if this 
     93     * is a command group. 
     94     */ 
     95    pj_cli_cmd_handler handler; 
     96 
     97    /** 
     98     * Number of arguments. 
     99     */ 
     100    unsigned arg_cnt; 
     101 
     102    /** 
     103     * Array of arguments. 
     104     */ 
     105    pj_cli_arg_spec *arg; 
     106 
     107    /** 
     108     * Child commands, if any. A command will only have subcommands if it is 
     109     * a group. If the command doesn't have subcommands, this field will be 
     110     * initialized with NULL. 
     111     */ 
     112    pj_cli_cmd_spec *sub_cmd; 
     113}; 
     114 
    45115struct pj_cli_t 
    46116{ 
     
    55125}; 
    56126 
     127/** 
     128 * Reserved command id constants. 
     129 */ 
     130typedef enum pj_cli_std_cmd_id 
     131{ 
     132    /** 
     133     * Constant to indicate an invalid command id. 
     134     */ 
     135    PJ_CLI_INVALID_CMD_ID = -1, 
     136 
     137    /** 
     138     * A special command id to indicate that a command id denotes 
     139     * a command group. 
     140     */ 
     141    PJ_CLI_CMD_ID_GROUP = -2 
     142 
     143} pj_cli_std_cmd_id; 
     144 
     145/** 
     146 * This describes the type of an argument (pj_cli_arg_spec). 
     147 */ 
     148typedef enum pj_cli_arg_type 
     149{ 
     150    /** 
     151     * Unformatted string. 
     152     */ 
     153    PJ_CLI_ARG_TEXT, 
     154 
     155    /** 
     156     * An integral number. 
     157     */ 
     158    PJ_CLI_ARG_INT, 
     159 
     160    /** 
     161     * Choice type 
     162    */ 
     163    PJ_CLI_ARG_CHOICE 
     164 
     165} pj_cli_arg_type; 
     166 
     167static const struct 
     168{ 
     169    const pj_str_t msg; 
     170} arg_type[] =  
     171{ 
     172    {"Text", 4}, 
     173    {"Int", 3}, 
     174    {"Choice", 6} 
     175}; 
     176 
     177/** 
     178 * This structure describe the specification of a command argument. 
     179 */ 
     180struct pj_cli_arg_spec 
     181{ 
     182    /** 
     183     * Argument id 
     184     */ 
     185    pj_cli_arg_id id; 
     186 
     187    /** 
     188     * Argument name. 
     189     */ 
     190    pj_str_t name; 
     191 
     192    /** 
     193     * Helpful description of the argument. This text will be used when 
     194     * displaying help texts for the command/argument. 
     195     */ 
     196    pj_str_t desc; 
     197 
     198    /** 
     199     * Argument type, which will be used for rendering the argument and 
     200     * to perform basic validation against an input value. 
     201     */ 
     202    pj_cli_arg_type type; 
     203 
     204    /** 
     205     * Argument status 
     206     */ 
     207    pj_bool_t optional; 
     208 
     209    /** 
     210     * Static Choice Values count 
     211     */ 
     212    unsigned stat_choice_cnt;  
     213 
     214    /** 
     215     * Static Choice Values 
     216     */ 
     217    pj_cli_arg_choice_val *stat_choice_val;  
     218 
     219    /** 
     220     * Argument callback to get the valid values 
     221     */ 
     222    pj_cli_arg_get_dyn_choice_val get_dyn_choice; 
     223 
     224}; 
     225 
     226/** 
     227 * This describe the parse mode of the command line 
     228 */ 
     229typedef enum pj_cli_parse_mode { 
     230    PARSE_NONE, 
     231    PARSE_COMPLETION,   /* Complete the command line */ 
     232    PARSE_NEXT_AVAIL,   /* Find the next available command line */ 
     233    PARSE_EXEC          /* Exec the command line */ 
     234} pj_cli_parse_mode; 
     235 
     236/**  
     237 * This is used to get the matched command/argument from the  
     238 * command/argument structure. 
     239 *  
     240 * @param sess          The session on which the command is execute on. 
     241 * @param cmd           The active command. 
     242 * @param cmd_val       The command value to match. 
     243 * @param argc          The number of argument that the  
     244 *                      current active command have. 
     245 * @param pool          The memory pool to allocate memory. 
     246 * @param get_cmd       Set true to search matching command from sub command. 
     247 * @param parse_mode    The parse mode. 
     248 * @param info          The output information containing any hints for  
     249 *                      matching command/arg. 
     250 * @return              This function return the status of the  
     251 *                      matching process.Please see the return value 
     252 *                      of pj_cli_sess_parse() for possible return values. 
     253 */ 
     254static pj_status_t get_available_cmds(pj_cli_sess *sess, 
     255                                      pj_cli_cmd_spec *cmd,  
     256                                      pj_str_t *cmd_val, 
     257                                      unsigned argc, 
     258                                      pj_pool_t *pool, 
     259                                      pj_bool_t get_cmd, 
     260                                      pj_cli_parse_mode parse_mode, 
     261                                      pj_cli_cmd_spec **p_cmd, 
     262                                      pj_cli_exec_info *info); 
     263 
     264PJ_DEF(pj_cli_cmd_id) pj_cli_get_cmd_id(const pj_cli_cmd_spec *cmd) 
     265{ 
     266    return cmd->id; 
     267} 
     268 
    57269PJ_DEF(void) pj_cli_cfg_default(pj_cli_cfg *param) 
    58270{ 
     
    86298        fe = fe->next; 
    87299    } 
     300} 
     301 
     302PJ_DECL(void) pj_cli_sess_write_msg(pj_cli_sess *sess,                                
     303                                    const char *buffer, 
     304                                    int len) 
     305{ 
     306    struct pj_cli_front_end *fe; 
     307 
     308    pj_assert(sess); 
     309 
     310    fe = sess->fe; 
     311    if (fe->op && fe->op->on_write_log) 
     312        (*fe->op->on_write_log)(fe, 0, buffer, len); 
    88313} 
    89314 
     
    102327        return PJ_SUCCESS; 
    103328    case CLI_CMD_EXIT: 
    104         pj_cli_end_session(cval->sess); 
     329        pj_cli_sess_end_session(cval->sess); 
    105330        return PJ_CLI_EEXIT; 
    106331    default: 
     
    109334} 
    110335 
     336#define print_(arg) \ 
     337    do { \ 
     338        unsigned d = pj_log_get_decor(); \ 
     339        pj_log_set_decor(0); \ 
     340        PJ_LOG(1, arg); \ 
     341        pj_log_set_decor(d); \ 
     342    } while (0) 
     343 
     344/**  
     345 *  Example to send the command structure to all cli session  
     346 **/   
     347PJ_DEF(void) pj_cli_log_command_struct(pj_cli_t *cli, pj_str_t *indent, pj_cli_cmd_spec *in_cmd) 
     348{ 
     349    static const pj_str_t CMD_SIGN = {"+", 1};         
     350    static const pj_str_t ARG_SIGN = {"-", 1};     
     351 
     352    if (cli) { 
     353        pj_cli_cmd_spec *cmd; 
     354        pj_pool_t *pool = pj_pool_create(cli->cfg.pf, "log_cmd", 64, 64, NULL); 
     355        cmd = (in_cmd)?in_cmd:&cli->root; 
     356        if (pool) { 
     357            pj_str_t modif_indent;           
     358            pj_cli_cmd_spec *child_cmd; 
     359            unsigned i; 
     360            char *indent_data; 
     361            pj_str_t *print_indent; 
     362 
     363            if (!indent) { 
     364                print_indent = PJ_POOL_ALLOC_T(pool, pj_str_t); 
     365                pj_strdup2(pool, print_indent, ""); 
     366            } else { 
     367                print_indent = indent;   
     368            } 
     369            indent_data = (char *)pj_pool_alloc(pool, print_indent->slen + 2); 
     370            modif_indent.ptr = indent_data; 
     371            modif_indent.slen = 0; 
     372 
     373            if (&cli->root != cmd) { 
     374                print_(("", "%.*s%.*s%.*s\r\n",  
     375                       (int)print_indent->slen, print_indent->ptr, 
     376                       (int)CMD_SIGN.slen, CMD_SIGN.ptr, 
     377                       (int)cmd->name.slen, cmd->name.ptr)); 
     378            } 
     379            pj_strcpy(&modif_indent, print_indent); 
     380            pj_strcat2(&modif_indent, "  ");         
     381 
     382            //print child commands 
     383            if (cmd->sub_cmd) { 
     384                child_cmd = cmd->sub_cmd->next; 
     385                while(child_cmd != cmd->sub_cmd) { 
     386                    pj_cli_log_command_struct(cli, &modif_indent, child_cmd); 
     387                    child_cmd = child_cmd->next; 
     388                } 
     389            } 
     390 
     391            //print argumen 
     392            for (i=0; i<cmd->arg_cnt;++i) { 
     393                pj_cli_arg_spec *arg = &cmd->arg[i]; 
     394                print_(("", "%.*s%.*s%.*s\r\n",  
     395                       (int)modif_indent.slen, modif_indent.ptr, 
     396                       (int)ARG_SIGN.slen, ARG_SIGN.ptr, 
     397                       (int)arg->name.slen, arg->name.ptr)); 
     398            } 
     399            pj_pool_release(pool); 
     400        } 
     401    }      
     402} 
     403 
    111404PJ_DEF(pj_status_t) pj_cli_create(pj_cli_cfg *cfg, 
    112405                                  pj_cli_t **p_cli) 
    113406{ 
    114407    pj_pool_t *pool; 
    115     pj_cli_t *cli; 
     408    pj_cli_t *cli;     
    116409    unsigned i; 
     410    /* This is an example of the command structure */ 
    117411    char* cmd_xmls[] = { 
    118412     "<CMD name='log' id='30000' sc='' desc='Change log level'>" 
    119      "  <ARGS>" 
    120      "    <ARG name='level' type='int' desc='Log level'/>" 
    121      "  </ARGS>" 
    122      "</CMD>", 
    123      "<CMD name='exit' id='30001' sc='' desc='Exit session'>" 
     413     "    <ARG name='level' type='int' optional='0' desc='Log level'/>" 
     414     "</CMD>",      
     415     "<CMD name='exit' id='30001' sc='' desc='Exit session'>"      
    124416     "</CMD>", 
    125417    }; 
     
    146438        pj_str_t xml = pj_str(cmd_xmls[i]); 
    147439 
    148         if (pj_cli_add_cmd_from_xml(cli, NULL, &xml, &cmd_handler, NULL) != 
     440        if (pj_cli_add_cmd_from_xml(cli, NULL, &xml, &cmd_handler, NULL, NULL) != 
    149441            PJ_SUCCESS) 
    150442            TRACE_((THIS_FILE, "Failed to add command #%d", i)); 
    151443    } 
    152444 
    153     *p_cli = cli; 
     445    *p_cli = cli;     
    154446 
    155447    return PJ_SUCCESS; 
     
    224516} 
    225517 
    226 PJ_DEF(void) pj_cli_end_session(pj_cli_sess *sess) 
     518PJ_DEF(void) pj_cli_sess_end_session(pj_cli_sess *sess) 
    227519{ 
    228520    pj_assert(sess); 
     
    239531} 
    240532 
    241 PJ_DEF(pj_status_t) pj_cli_add_cmd_from_xml(pj_cli_t *cli, 
    242                                             pj_cli_cmd_spec *group, 
    243                                             const pj_str_t *xml, 
    244                                             pj_cli_cmd_handler handler, 
    245                                             pj_cli_cmd_spec *p_cmd) 
    246 { 
    247     #define ERROR_(STATUS) \ 
    248         do {status = STATUS; goto on_exit;} while(0) 
    249     pj_pool_t *pool; 
    250     pj_xml_node *root; 
     533/** 
     534 * This method is to parse and add the choice type  
     535 * argument values to command structure. 
     536 **/ 
     537static pj_status_t pj_cli_add_choice_node(pj_cli_t *cli, 
     538                                          pj_xml_node *xml_node, 
     539                                          pj_cli_arg_spec *arg, 
     540                                          pj_cli_arg_get_dyn_choice_val get_choice) 
     541{ 
     542    pj_xml_node *choice_node; 
     543    pj_xml_node *sub_node; 
     544    pj_cli_arg_choice_val choice_values[PJ_CLI_MAX_CHOICE_VAL]; 
     545    pj_status_t status = PJ_SUCCESS; 
     546 
     547    sub_node = xml_node; 
     548    arg->type = PJ_CLI_ARG_CHOICE; 
     549    arg->get_dyn_choice = get_choice;                                            
     550 
     551    choice_node = sub_node->node_head.next; 
     552    while (choice_node != (pj_xml_node*)&sub_node->node_head) { 
     553        pj_xml_attr *choice_attr; 
     554        pj_cli_arg_choice_val *choice_val = &choice_values[arg->stat_choice_cnt];                     
     555        pj_bzero(choice_val, sizeof(*choice_val)); 
     556 
     557        choice_attr = choice_node->attr_head.next; 
     558        while (choice_attr != &choice_node->attr_head) { 
     559            if (!pj_stricmp2(&choice_attr->name, "value")) { 
     560                pj_strassign(&choice_val->value, &choice_attr->value); 
     561            } else if (!pj_stricmp2(&choice_attr->name, "desc")) { 
     562                pj_strassign(&choice_val->desc, &choice_attr->value); 
     563            }                            
     564        }                            
     565        ++(arg->stat_choice_cnt); 
     566        choice_node = choice_node->next; 
     567    }     
     568    if (arg->stat_choice_cnt > 0) { 
     569        unsigned i; 
     570 
     571        arg->stat_choice_val = (pj_cli_arg_choice_val *)pj_pool_zalloc(cli->pool,  
     572                                                                       arg->stat_choice_cnt * 
     573                                                                       sizeof(pj_cli_arg_choice_val)); 
     574        for (i = 0; i < arg->stat_choice_cnt; i++) { 
     575            pj_strdup(cli->pool, &arg->stat_choice_val[i].value, &choice_values[i].value); 
     576            pj_strdup(cli->pool, &arg->stat_choice_val[i].desc, &choice_values[i].desc);             
     577        } 
     578    } 
     579    return status; 
     580} 
     581 
     582/** 
     583 * This method is to parse and add the argument attribute to command structure. 
     584 **/ 
     585static pj_status_t pj_cli_add_arg_node(pj_cli_t *cli, 
     586                                       pj_xml_node *xml_node, 
     587                                       pj_cli_cmd_spec *cmd, 
     588                                       pj_cli_arg_spec *arg, 
     589                                       pj_cli_arg_get_dyn_choice_val get_choice) 
     590{     
     591    pj_xml_attr *attr; 
     592    pj_status_t status = PJ_SUCCESS; 
     593    pj_xml_node *sub_node = xml_node; 
     594 
     595    if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS) 
     596        return PJ_CLI_ETOOMANYARGS; 
     597     
     598    pj_bzero(arg, sizeof(*arg)); 
     599    attr = sub_node->attr_head.next; 
     600    arg->optional = PJ_FALSE; 
     601    while (attr != &sub_node->attr_head) {       
     602        if (!pj_stricmp2(&attr->name, "name")) { 
     603            pj_strassign(&arg->name, &attr->value); 
     604        } else if (!pj_stricmp2(&attr->name, "id")) { 
     605            arg->id = pj_strtol(&attr->value); 
     606        } else if (!pj_stricmp2(&attr->name, "type")) { 
     607            if (!pj_stricmp2(&attr->value, "text")) { 
     608                arg->type = PJ_CLI_ARG_TEXT; 
     609            } else if (!pj_stricmp2(&attr->value, "int")) { 
     610                arg->type = PJ_CLI_ARG_INT; 
     611            } else if (!pj_stricmp2(&attr->value, "CHOICE")) { 
     612                /* Get choice value */ 
     613                pj_cli_add_choice_node(cli, xml_node, arg, get_choice); 
     614            }  
     615        } else if (!pj_stricmp2(&attr->name, "desc")) { 
     616            pj_strassign(&arg->desc, &attr->value); 
     617        } else if (!pj_stricmp2(&attr->name, "optional")) { 
     618            if (!pj_strcmp2(&attr->value, "1")) { 
     619                arg->optional = PJ_TRUE; 
     620            } 
     621        } 
     622        attr = attr->next; 
     623    } 
     624    cmd->arg_cnt++; 
     625    return status; 
     626} 
     627 
     628/** 
     629 * This method is to parse and add the command attribute to command structure. 
     630 **/ 
     631static pj_status_t pj_cli_add_cmd_node(pj_cli_t *cli,                              
     632                                       pj_cli_cmd_spec *group,                                    
     633                                       pj_xml_node *xml_node, 
     634                                       pj_cli_cmd_handler handler, 
     635                                       pj_cli_cmd_spec **p_cmd, 
     636                                       pj_cli_arg_get_dyn_choice_val get_choice) 
     637{ 
     638    pj_xml_node *root = xml_node; 
    251639    pj_xml_attr *attr; 
    252640    pj_xml_node *sub_node; 
     
    256644    pj_status_t status = PJ_SUCCESS; 
    257645 
    258     PJ_ASSERT_RETURN(cli && xml, PJ_EINVAL); 
    259  
    260     /* Parse the xml */ 
    261     pool = pj_pool_create(cli->cfg.pf, "xml", 1024, 1024, NULL); 
    262     if (!pool) 
    263         return PJ_ENOMEM; 
    264     root = pj_xml_parse(pool, xml->ptr, xml->slen); 
    265     if (!root) { 
    266         TRACE_((THIS_FILE, "Error: unable to parse XML")); 
    267         ERROR_(PJ_CLI_EBADXML); 
    268     } 
    269  
    270646    if (pj_stricmp2(&root->name, "CMD")) 
    271         ERROR_(PJ_EINVAL); 
     647        return PJ_EINVAL; 
    272648 
    273649    /* Initialize the command spec */ 
     
    279655        if (!pj_stricmp2(&attr->name, "name")) { 
    280656            pj_strltrim(&attr->value); 
    281             pj_strrtrim(&attr->value); 
    282             /* Check whether command with the specified name already exists */ 
    283657            if (!attr->value.slen || 
    284658                pj_hash_get(cli->hash, attr->value.ptr,  
    285659                            attr->value.slen, NULL)) 
    286660            { 
    287                 ERROR_(PJ_CLI_EBADNAME); 
     661                return PJ_CLI_EBADNAME; 
    288662            } 
    289  
    290663            pj_strdup(cli->pool, &cmd->name, &attr->value); 
    291664        } else if (!pj_stricmp2(&attr->name, "id")) { 
     
    324697            PJ_CATCH_ANY { 
    325698                pj_scan_fini(&scanner); 
    326                 ERROR_(PJ_GET_EXCEPTION()); 
     699                return (PJ_GET_EXCEPTION()); 
    327700            } 
    328701            PJ_END; 
     
    331704            pj_strdup(cli->pool, &cmd->desc, &attr->value); 
    332705        } 
    333  
    334706        attr = attr->next; 
    335707    } 
    336708 
    337     /* Get the command arguments */ 
     709    /* Get the command childs/arguments */ 
    338710    sub_node = root->node_head.next; 
    339711    while (sub_node != (pj_xml_node*)&root->node_head) { 
    340         if (!pj_stricmp2(&sub_node->name, "ARGS")) { 
    341             pj_xml_node *arg_node; 
    342  
    343             arg_node = sub_node->node_head.next; 
    344             while (arg_node != (pj_xml_node*)&sub_node->node_head) { 
    345                 if (!pj_stricmp2(&arg_node->name, "ARG")) { 
    346                     pj_cli_arg_spec *arg; 
    347  
    348                     if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS) 
    349                         ERROR_(PJ_CLI_ETOOMANYARGS); 
    350                     arg = &args[cmd->arg_cnt]; 
    351                     pj_bzero(arg, sizeof(*arg)); 
    352                     attr = arg_node->attr_head.next; 
    353                     while (attr != &arg_node->attr_head) { 
    354                         if (!pj_stricmp2(&attr->name, "name")) { 
    355                             pj_strassign(&arg->name, &attr->value); 
    356                         } else if (!pj_stricmp2(&attr->name, "type")) { 
    357                             if (!pj_stricmp2(&attr->value, "text")) { 
    358                                 arg->type = PJ_CLI_ARG_TEXT; 
    359                             } else if (!pj_stricmp2(&attr->value, "int")) { 
    360                                 arg->type = PJ_CLI_ARG_INT; 
    361                             } 
    362                         } else if (!pj_stricmp2(&attr->name, "desc")) { 
    363                             pj_strassign(&arg->desc, &attr->value); 
    364                         } 
    365  
    366                         attr = attr->next; 
    367                     } 
    368                     cmd->arg_cnt++; 
    369                 } 
    370  
    371                 arg_node = arg_node->next; 
    372             } 
     712        if (!pj_stricmp2(&sub_node->name, "CMD")) { 
     713            status = pj_cli_add_cmd_node(cli, cmd, sub_node, handler, NULL,  
     714                                         get_choice); 
     715            if (status != PJ_SUCCESS) 
     716                return status; 
     717        } else if (!pj_stricmp2(&sub_node->name, "ARG")) { 
     718            /* Get argument attribute */ 
     719            status = pj_cli_add_arg_node(cli, sub_node, cmd, &args[cmd->arg_cnt],  
     720                                         get_choice); 
     721            if (status != PJ_SUCCESS) 
     722                return status; 
    373723        } 
    374724        sub_node = sub_node->next; 
    375725    } 
    376726 
    377     if (cmd->id == PJ_CLI_CMD_ID_GROUP) { 
    378         /* Command group shouldn't have any shortcuts nor arguments */ 
    379         if (!cmd->sc_cnt || !cmd->arg_cnt) 
    380             ERROR_(PJ_EINVAL); 
    381         cmd->sub_cmd = PJ_POOL_ALLOC_T(cli->pool, struct pj_cli_cmd_spec); 
    382         pj_list_init(cmd->sub_cmd); 
    383     } 
    384  
    385727    if (!cmd->name.slen) 
    386         ERROR_(PJ_CLI_EBADNAME); 
     728        return PJ_CLI_EBADNAME; 
    387729 
    388730    if (cmd->arg_cnt) { 
     
    395737            pj_strdup(cli->pool, &cmd->arg[i].desc, &args[i].desc); 
    396738            cmd->arg[i].type = args[i].type; 
     739            cmd->arg[i].optional = args[i].optional; 
    397740        } 
    398741    } 
     
    414757    cmd->handler = handler; 
    415758 
    416     if (group) 
     759    if (group) { 
     760        if (!group->sub_cmd) { 
     761            group->sub_cmd = PJ_POOL_ALLOC_T(cli->pool, struct pj_cli_cmd_spec); 
     762            pj_list_init(group->sub_cmd); 
     763        } 
    417764        pj_list_push_back(group->sub_cmd, cmd); 
    418     else 
     765    } else { 
    419766        pj_list_push_back(cli->root.sub_cmd, cmd); 
     767    } 
    420768 
    421769    if (p_cmd) 
    422         p_cmd = cmd; 
    423  
    424 on_exit: 
     770        *p_cmd = cmd; 
     771 
     772    return status; 
     773} 
     774 
     775PJ_DEF(pj_status_t) pj_cli_add_cmd_from_xml(pj_cli_t *cli, 
     776                                            pj_cli_cmd_spec *group, 
     777                                            const pj_str_t *xml, 
     778                                            pj_cli_cmd_handler handler, 
     779                                            pj_cli_cmd_spec **p_cmd,  
     780                                            pj_cli_arg_get_dyn_choice_val get_choice) 
     781{  
     782    pj_pool_t *pool; 
     783    pj_xml_node *root; 
     784    pj_status_t status = PJ_SUCCESS; 
     785     
     786    PJ_ASSERT_RETURN(cli && xml, PJ_EINVAL); 
     787 
     788    /* Parse the xml */ 
     789    pool = pj_pool_create(cli->cfg.pf, "xml", 1024, 1024, NULL); 
     790    if (!pool) 
     791        return PJ_ENOMEM; 
     792 
     793    root = pj_xml_parse(pool, xml->ptr, xml->slen); 
     794    if (!root) { 
     795        TRACE_((THIS_FILE, "Error: unable to parse XML")); 
     796        pj_pool_release(pool); 
     797        return PJ_CLI_EBADXML; 
     798    }     
     799    status = pj_cli_add_cmd_node(cli, group, root, handler, p_cmd, get_choice); 
    425800    pj_pool_release(pool); 
    426  
    427801    return status; 
    428 #undef ERROR_ 
    429 } 
    430  
    431 PJ_DEF(pj_status_t) pj_cli_parse(pj_cli_sess *sess, 
    432                                  char *cmdline, 
    433                                  pj_cli_cmd_val *val, 
    434                                  pj_cli_exec_info *info) 
    435 { 
     802} 
     803 
     804PJ_DEF(pj_status_t) pj_cli_sess_parse(pj_cli_sess *sess, 
     805                                      char *cmdline, 
     806                                      pj_cli_cmd_val *val, 
     807                                      pj_pool_t *pool, 
     808                                      pj_cli_exec_info *info) 
     809{     
    436810    pj_scanner scanner; 
    437811    pj_str_t str; 
    438     int len; 
    439     pj_cli_exec_info einfo; 
     812    int len;     
     813    pj_cli_cmd_spec *cmd; 
     814    pj_cli_cmd_spec *next_cmd; 
     815    pj_status_t status = PJ_SUCCESS; 
     816    pj_cli_parse_mode parse_mode = PARSE_NONE;     
    440817 
    441818    PJ_USE_EXCEPTION; 
     
    443820    PJ_ASSERT_RETURN(sess && cmdline && val, PJ_EINVAL); 
    444821 
    445     if (!info) 
    446         info = &einfo; 
     822    PJ_UNUSED_ARG(pool); 
     823 
     824    str.slen = 0; 
    447825    pj_cli_exec_info_default(info); 
    448826 
    449     /* Parse the command line. */ 
     827    /* Set the parse mode based on the latest char. */ 
    450828    len = pj_ansi_strlen(cmdline); 
    451     if (len > 0 && cmdline[len - 1] == '\n') { 
     829    if (len > 0 && cmdline[len - 1] == '\r') { 
    452830        cmdline[--len] = 0; 
    453         if (len > 0 && cmdline[len - 1] == '\r') 
    454             cmdline[--len] = 0; 
    455     } 
    456     pj_scan_init(&scanner, cmdline, len, PJ_SCAN_AUTOSKIP_WS,  
    457                  &on_syntax_error); 
    458     PJ_TRY { 
    459         pj_scan_get_until_chr(&scanner, " \t\r\n", &str); 
    460  
    461         /* Find the command from the hash table */ 
    462         val->cmd = pj_hash_get(sess->fe->cli->hash, str.ptr,  
    463                                str.slen, NULL); 
    464  
    465         /* Error, command not found */ 
    466         if (!val->cmd) 
    467             PJ_THROW(PJ_ENOTFOUND); 
    468  
    469         info->cmd_id = val->cmd->id; 
    470  
    471         /* Parse the command arguments */ 
    472         val->argc = 1; 
    473         pj_strassign(&val->argv[0], &str); 
    474         while (!pj_scan_is_eof(&scanner)) { 
    475             if (*scanner.curptr == '\'' || *scanner.curptr == '"' || 
    476                 *scanner.curptr == '[' || *scanner.curptr == '{') 
    477             { 
    478                 pj_scan_get_quotes(&scanner, "'\"[{", "'\"]}", 4, &str); 
    479                 /* Remove the quotes */ 
    480                 str.ptr++; 
    481                 str.slen -= 2; 
    482             } else { 
    483                 pj_scan_get_until_chr(&scanner, " \t\r\n", &str); 
    484             } 
    485             if (val->argc == PJ_CLI_MAX_ARGS) 
    486                 PJ_THROW(PJ_CLI_ETOOMANYARGS); 
    487             pj_strassign(&val->argv[val->argc++], &str); 
    488         } 
    489          
    490         if (!pj_scan_is_eof(&scanner)) { 
    491             PJ_THROW(PJ_CLI_EINVARG); 
    492         } 
    493     } 
    494     PJ_CATCH_ANY { 
    495         pj_scan_fini(&scanner); 
    496         return PJ_GET_EXCEPTION(); 
    497     } 
    498     PJ_END; 
    499  
    500     if ((val->argc - 1) < (int)val->cmd->arg_cnt) { 
    501         info->arg_idx = val->argc; 
    502         return PJ_CLI_EMISSINGARG; 
    503     } else if ((val->argc - 1) > (int)val->cmd->arg_cnt) { 
    504         info->arg_idx = val->cmd->arg_cnt + 1; 
    505         return PJ_CLI_ETOOMANYARGS; 
    506     } 
    507  
     831        parse_mode = PARSE_EXEC; 
     832    } else if (len > 0 &&  
     833               (cmdline[len - 1] == '\t' || cmdline[len - 1] == '?')) { 
     834 
     835        cmdline[--len] = 0; 
     836        if (len == 0) { 
     837            parse_mode = PARSE_NEXT_AVAIL; 
     838        } else { 
     839            if (cmdline[len - 1] == ' ')  
     840                parse_mode = PARSE_NEXT_AVAIL; 
     841            else  
     842                parse_mode = PARSE_COMPLETION; 
     843        } 
     844    } 
     845    val->argc = 0; 
     846    info->err_pos = 0; 
     847    cmd = &sess->fe->cli->root; 
     848    if (len > 0) { 
     849        pj_scan_init(&scanner, cmdline, len, PJ_SCAN_AUTOSKIP_WS,  
     850                     &on_syntax_error); 
     851        PJ_TRY { 
     852            val->argc = 0;           
     853            while (!pj_scan_is_eof(&scanner)) { 
     854                info->err_pos = scanner.curptr - scanner.begin; 
     855                if (*scanner.curptr == '\'' || *scanner.curptr == '"' || 
     856                    *scanner.curptr == '[' || *scanner.curptr == '{') 
     857                { 
     858                    pj_scan_get_quotes(&scanner, "'\"[{", "'\"]}", 4, &str); 
     859                    /* Remove the quotes */ 
     860                    str.ptr++; 
     861                    str.slen -= 2; 
     862                } else { 
     863                    pj_scan_get_until_chr(&scanner, " \t\r\n", &str); 
     864                } 
     865                ++val->argc; 
     866                if (val->argc == PJ_CLI_MAX_ARGS) 
     867                    PJ_THROW(PJ_CLI_ETOOMANYARGS);               
     868                 
     869                status = get_available_cmds(sess, cmd, &str, val->argc-1,  
     870                                            pool, PJ_TRUE, parse_mode,  
     871                                            &next_cmd, info); 
     872 
     873                if (status != PJ_SUCCESS) 
     874                    PJ_THROW(status); 
     875                 
     876                if (cmd != next_cmd) { 
     877                    /* Found new command, set it as the active command */ 
     878                    cmd = next_cmd; 
     879                    val->argc = 1; 
     880                    val->cmd = cmd; 
     881                } 
     882                if (parse_mode == PARSE_EXEC)  
     883                    pj_strassign(&val->argv[val->argc-1], &info->hint->name); 
     884                else  
     885                    pj_strassign(&val->argv[val->argc-1], &str); 
     886 
     887            } 
     888             
     889            if (!pj_scan_is_eof(&scanner)) { 
     890                PJ_THROW(PJ_CLI_EINVARG); 
     891            } 
     892        } 
     893        PJ_CATCH_ANY { 
     894            pj_scan_fini(&scanner); 
     895            return PJ_GET_EXCEPTION(); 
     896        } 
     897        PJ_END; 
     898    }  
     899     
     900    if ((parse_mode == PARSE_NEXT_AVAIL) || (parse_mode == PARSE_EXEC)) { 
     901        /* If exec mode, just get the matching argument */ 
     902        status = get_available_cmds(sess, cmd, NULL, val->argc, pool,  
     903                                    (parse_mode==PARSE_NEXT_AVAIL),  
     904                                    parse_mode, 
     905                                    NULL, info); 
     906        if ((status != PJ_SUCCESS) && (status != PJ_CLI_EINVARG)) { 
     907            pj_str_t data = pj_str(cmdline); 
     908            pj_strrtrim(&data); 
     909            data.ptr[data.slen] = ' '; 
     910            data.ptr[data.slen+1] = 0; 
     911 
     912            info->err_pos = pj_ansi_strlen(cmdline); 
     913        } 
     914 
     915    } else if (parse_mode == PARSE_COMPLETION) { 
     916        if (info->hint[0].name.slen > str.slen) { 
     917            pj_str_t *hint_info = &info->hint[0].name; 
     918            pj_memmove(&hint_info->ptr[0], &hint_info->ptr[str.slen],  
     919                       info->hint[0].name.slen-str.slen); 
     920            hint_info->slen = info->hint[0].name.slen-str.slen;          
     921        } else { 
     922            info->hint[0].name.slen = 0; 
     923        } 
     924    }     
    508925    val->sess = sess; 
    509  
    510     return PJ_SUCCESS; 
    511 } 
    512  
    513 pj_status_t pj_cli_exec(pj_cli_sess *sess, 
    514                         char *cmdline, 
    515                         pj_cli_exec_info *info) 
     926    return status; 
     927} 
     928 
     929PJ_DECL(pj_status_t) pj_cli_sess_exec(pj_cli_sess *sess, 
     930                                      char *cmdline, 
     931                                      pj_pool_t *pool, 
     932                                      pj_cli_exec_info *info) 
    516933{ 
    517934    pj_cli_cmd_val val; 
    518935    pj_status_t status; 
    519936    pj_cli_exec_info einfo; 
     937    pj_str_t cmd; 
    520938 
    521939    PJ_ASSERT_RETURN(sess && cmdline, PJ_EINVAL); 
     940 
     941    PJ_UNUSED_ARG(pool); 
     942 
     943    cmd.ptr = cmdline; 
     944    cmd.slen = pj_ansi_strlen(cmdline); 
     945 
     946    if (pj_strtrim(&cmd)->slen == 0) 
     947        return PJ_SUCCESS; 
    522948 
    523949    if (!info) 
    524950        info = &einfo; 
    525     status = pj_cli_parse(sess, cmdline, &val, info); 
     951    status = pj_cli_sess_parse(sess, cmdline, &val, pool, info); 
    526952    if (status != PJ_SUCCESS) 
    527953        return status; 
    528954 
    529     if (val.cmd->handler) { 
     955    if ((val.argc > 0) && (val.cmd->handler)) { 
    530956        info->cmd_ret = (*val.cmd->handler)(&val); 
    531957        if (info->cmd_ret == PJ_CLI_EINVARG || 
     
    536962    return PJ_SUCCESS; 
    537963} 
     964 
     965static pj_status_t pj_cli_insert_new_hint(pj_pool_t *pool,  
     966                                          const pj_str_t *name,  
     967                                          const pj_str_t *desc,  
     968                                          const pj_str_t *type,  
     969                                          pj_cli_exec_info *info) 
     970{     
     971    pj_cli_hint_info *hint = &info->hint[info->hint_cnt]; 
     972    PJ_ASSERT_RETURN(pool && info, PJ_EINVAL);     
     973 
     974    pj_strdup(pool, &hint->name, name); 
     975 
     976    if (desc && (desc->slen > 0))  { 
     977        pj_strdup(pool, &hint->desc, desc); 
     978    } else { 
     979        hint->desc.slen = 0; 
     980    } 
     981     
     982    if (type && (type->slen > 0)) { 
     983        pj_strdup(pool, &hint->type, type); 
     984    } else { 
     985        hint->type.slen = 0; 
     986    } 
     987 
     988    ++info->hint_cnt;     
     989    return PJ_SUCCESS; 
     990} 
     991 
     992static pj_status_t get_match_cmds(pj_cli_cmd_spec *cmd,  
     993                                  const pj_str_t *cmd_val,                                     
     994                                  pj_pool_t *pool,  
     995                                  pj_cli_cmd_spec **p_cmd,                              
     996                                  pj_cli_exec_info *info) 
     997{ 
     998    pj_status_t status = PJ_SUCCESS; 
     999    PJ_ASSERT_RETURN(cmd && pool && info && cmd_val, PJ_EINVAL);     
     1000 
     1001    if (p_cmd) 
     1002        *p_cmd = cmd; 
     1003 
     1004    /* Get matching command */ 
     1005    if (cmd->sub_cmd) { 
     1006        pj_cli_cmd_spec *child_cmd = cmd->sub_cmd->next; 
     1007        while (child_cmd != cmd->sub_cmd) { 
     1008            unsigned i;     
     1009            pj_bool_t found = PJ_FALSE; 
     1010            if (!pj_strnicmp(&child_cmd->name, cmd_val, cmd_val->slen)) {                
     1011                status = pj_cli_insert_new_hint(pool, &child_cmd->name,  
     1012                                                &child_cmd->desc, NULL, info); 
     1013                if (status != PJ_SUCCESS) 
     1014                    return status; 
     1015 
     1016                found = PJ_TRUE; 
     1017            } 
     1018            for (i=0; i < child_cmd->sc_cnt; ++i) { 
     1019                static const pj_str_t SHORTCUT = {"SC", 2}; 
     1020                pj_str_t *sc = &child_cmd->sc[i]; 
     1021                PJ_ASSERT_RETURN(sc, PJ_EINVAL); 
     1022 
     1023                if (!pj_strnicmp(sc, cmd_val, cmd_val->slen)) {          
     1024                    status = pj_cli_insert_new_hint(pool, sc, &child_cmd->desc,  
     1025                                                    &SHORTCUT, info); 
     1026                    if (status != PJ_SUCCESS) 
     1027                        return status; 
     1028 
     1029                    found = PJ_TRUE; 
     1030                }                
     1031            } 
     1032            if (found && p_cmd) { 
     1033                *p_cmd = child_cmd;              
     1034            } 
     1035 
     1036            child_cmd = child_cmd->next; 
     1037        } 
     1038    } 
     1039    return status; 
     1040} 
     1041 
     1042static pj_status_t get_match_args(pj_cli_sess *sess, 
     1043                                  pj_cli_cmd_spec *cmd,  
     1044                                  const pj_str_t *cmd_val, 
     1045                                  unsigned argc, 
     1046                                  pj_pool_t *pool,  
     1047                                  pj_cli_parse_mode parse_mode, 
     1048                                  pj_cli_exec_info *info) 
     1049{ 
     1050    pj_cli_arg_spec *arg; 
     1051    pj_status_t status = PJ_SUCCESS; 
     1052 
     1053    PJ_ASSERT_RETURN(cmd && pool && cmd_val && info, PJ_EINVAL); 
     1054 
     1055    if ((argc > cmd->arg_cnt) && (!cmd->sub_cmd)) { 
     1056        if (cmd_val->slen > 0) 
     1057            return PJ_CLI_ETOOMANYARGS; 
     1058        else 
     1059            return PJ_SUCCESS; 
     1060    } 
     1061 
     1062    if (cmd->arg_cnt > 0) { 
     1063        arg = &cmd->arg[argc-1]; 
     1064        PJ_ASSERT_RETURN(arg, PJ_EINVAL); 
     1065        if (arg->type == PJ_CLI_ARG_CHOICE) {        
     1066            unsigned j;      
     1067            pj_cli_dyn_choice_param dyn_choice_param;        
     1068 
     1069            for (j=0; j < arg->stat_choice_cnt; ++j) { 
     1070                pj_cli_arg_choice_val *choice_val = &arg->stat_choice_val[j]; 
     1071             
     1072                PJ_ASSERT_RETURN(choice_val, PJ_EINVAL);                 
     1073             
     1074                if (!pj_strnicmp(&choice_val->value, cmd_val, cmd_val->slen)) {              
     1075                    status = pj_cli_insert_new_hint(pool, &choice_val->value,  
     1076                                                    &choice_val->desc,  
     1077                                                    &arg_type[PJ_CLI_ARG_CHOICE].msg,  
     1078                                                    info); 
     1079                    if (status != PJ_SUCCESS) 
     1080                        return status;               
     1081                } 
     1082            } 
     1083            /* Get the dynamic choice values */      
     1084            dyn_choice_param.sess = sess; 
     1085            dyn_choice_param.cmd = cmd; 
     1086            dyn_choice_param.arg_id = arg->id; 
     1087            dyn_choice_param.max_cnt = PJ_CLI_MAX_CHOICE_VAL; 
     1088            dyn_choice_param.pool = pool; 
     1089            dyn_choice_param.cnt = 0;        
     1090 
     1091            (*arg->get_dyn_choice)(&dyn_choice_param); 
     1092            for (j=0; j < dyn_choice_param.cnt; ++j) { 
     1093                pj_cli_arg_choice_val *choice = &dyn_choice_param.choice[j]; 
     1094                pj_strassign(&info->hint[info->hint_cnt].name, &choice->value); 
     1095                pj_strassign(&info->hint[info->hint_cnt].desc, &choice->desc); 
     1096                ++info->hint_cnt; 
     1097            } 
     1098        } else { 
     1099            if (cmd_val->slen == 0) { 
     1100                if (info->hint_cnt == 0) { 
     1101                    if (!((parse_mode == PARSE_EXEC) && (arg->optional))) { 
     1102                        /* If exec mode, don't need to insert the hint if optional */ 
     1103                        status = pj_cli_insert_new_hint(pool, &arg->name, &arg->desc,  
     1104                                                        &arg_type[arg->type].msg, info); 
     1105                        if (status != PJ_SUCCESS) 
     1106                            return status; 
     1107                    } 
     1108                    if (!arg->optional) 
     1109                        return PJ_CLI_EMISSINGARG; 
     1110                }  
     1111            } else { 
     1112                return pj_cli_insert_new_hint(pool, cmd_val,  
     1113                                              NULL, NULL, info); 
     1114            } 
     1115        } 
     1116    }  
     1117    return status; 
     1118} 
     1119 
     1120static pj_status_t get_available_cmds(pj_cli_sess *sess, 
     1121                                      pj_cli_cmd_spec *cmd,  
     1122                                      pj_str_t *cmd_val, 
     1123                                      unsigned argc, 
     1124                                      pj_pool_t *pool, 
     1125                                      pj_bool_t get_cmd, 
     1126                                      pj_cli_parse_mode parse_mode, 
     1127                                      pj_cli_cmd_spec **p_cmd, 
     1128                                      pj_cli_exec_info *info) 
     1129{ 
     1130    pj_status_t status = PJ_SUCCESS; 
     1131    pj_str_t *prefix; 
     1132    pj_str_t EMPTY_STR = {NULL, 0}; 
     1133 
     1134    prefix = cmd_val?(pj_strtrim(cmd_val)):(&EMPTY_STR); 
     1135 
     1136    info->hint_cnt = 0;     
     1137 
     1138    if (get_cmd) 
     1139        status = get_match_cmds(cmd, prefix, pool, p_cmd, info); 
     1140    if (argc > 0) 
     1141        status = get_match_args(sess, cmd, prefix, argc, pool, parse_mode, info); 
     1142 
     1143    if (status == PJ_SUCCESS) {  
     1144        if (prefix->slen > 0) { 
     1145            if (info->hint_cnt == 0) { 
     1146                status = PJ_CLI_EINVARG; 
     1147            } else if (info->hint_cnt > 1) { 
     1148                status = PJ_CLI_EAMBIGUOUS; 
     1149            } 
     1150        } else { 
     1151            if (info->hint_cnt > 0) 
     1152                status = PJ_CLI_EAMBIGUOUS; 
     1153        } 
     1154    }  
     1155 
     1156    return status; 
     1157} 
     1158 
  • pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_console.c

    r3231 r4299  
    2828#include <pjlib-util/errno.h> 
    2929 
     30#if defined(PJ_LINUX) && PJ_LINUX != 0 || \ 
     31    defined(PJ_DARWINOS) && PJ_DARWINOS != 0 
     32#include <termios.h> 
     33 
     34static struct termios old, new; 
     35 
     36/* Initialize new terminal i/o settings */ 
     37void initTermios(int echo)  
     38{ 
     39    tcgetattr(0, &old);  
     40    new = old;  
     41    new.c_lflag &= ~ICANON;  
     42    new.c_lflag &= echo ? ECHO : ~ECHO;  
     43    tcsetattr(0, TCSANOW, &new);  
     44} 
     45 
     46/* Restore old terminal i/o settings */ 
     47void resetTermios(void)  
     48{ 
     49    tcsetattr(0, TCSANOW, &old); 
     50} 
     51 
     52/* Read 1 character - echo defines echo mode */ 
     53char getch_(int echo)  
     54{ 
     55    char ch; 
     56    initTermios(echo); 
     57    ch = getchar(); 
     58    resetTermios(); 
     59    return ch; 
     60} 
     61 
     62/* Read 1 character without echo */ 
     63char getch(void)  
     64{ 
     65    return getch_(0); 
     66} 
     67 
     68#endif 
     69 
     70/** 
     71 * This specify the state of input character parsing. 
     72 */ 
     73typedef enum cmd_parse_state 
     74{ 
     75    ST_NORMAL, 
     76    ST_SCANMODE, 
     77    ST_ESC, 
     78    ST_ARROWMODE 
     79} cmd_parse_state; 
     80 
     81/** 
     82 * This structure contains the command line shown to the user.   
     83 */ 
     84typedef struct console_recv_buf { 
     85    /** 
     86     * Buffer containing the characters, NULL terminated. 
     87     */ 
     88    unsigned char           rbuf[PJ_CLI_MAX_CMDBUF]; 
     89 
     90    /** 
     91     * Current length of the command line. 
     92     */ 
     93    unsigned                len; 
     94 
     95    /** 
     96     * Current cursor position. 
     97     */ 
     98    unsigned                cur_pos; 
     99} console_recv_buf; 
     100 
     101typedef struct cmd_history 
     102{ 
     103    PJ_DECL_LIST_MEMBER(struct cmd_history); 
     104    pj_str_t command; 
     105} cmd_history; 
     106 
    30107struct cli_console_fe 
    31108{ 
     
    35112    pj_thread_t        *input_thread; 
    36113    pj_bool_t           thread_quit; 
    37     pj_sem_t           *thread_sem; 
     114    pj_sem_t           *thread_sem;    
     115    pj_cli_console_cfg  cfg; 
    38116 
    39117    struct async_input_t 
    40     { 
    41         char       *buf; 
    42         unsigned    maxlen; 
    43         pj_sem_t   *sem; 
     118    {         
     119        console_recv_buf    recv_buf;         
     120        pj_sem_t           *sem; 
    44121    } input; 
     122 
     123    cmd_history             *history; 
     124    cmd_history             *active_history; 
    45125}; 
     126 
     127static unsigned recv_buf_right_len(console_recv_buf *recv_buf) 
     128{ 
     129    return (recv_buf->len - recv_buf->cur_pos); 
     130} 
     131 
     132static pj_bool_t recv_buf_insert(console_recv_buf *recv_buf,  
     133                                 unsigned char *data)  
     134{     
     135    if (recv_buf->len+1 >= PJ_CLI_MAX_CMDBUF) { 
     136        return PJ_FALSE; 
     137    } else {     
     138        if (*data == '\t' || *data == '?' || *data == '\r') { 
     139            /* Always insert to the end of line */ 
     140            recv_buf->rbuf[recv_buf->len] = *data; 
     141        } else { 
     142            /* Insert based on the current cursor pos */ 
     143            unsigned cur_pos = recv_buf->cur_pos; 
     144            unsigned rlen = recv_buf_right_len(recv_buf);            
     145            if (rlen > 0) {                  
     146                /* Shift right characters */ 
     147                pj_memmove(&recv_buf->rbuf[cur_pos+1],  
     148                           &recv_buf->rbuf[cur_pos],  
     149                           rlen+1); 
     150            }  
     151            recv_buf->rbuf[cur_pos] = *data; 
     152        } 
     153        ++recv_buf->cur_pos; 
     154        ++recv_buf->len; 
     155        recv_buf->rbuf[recv_buf->len] = 0; 
     156    } 
     157    return PJ_TRUE; 
     158} 
     159 
     160static pj_bool_t recv_buf_backspace(console_recv_buf *recv_buf) 
     161{ 
     162    if ((recv_buf->cur_pos == 0) || (recv_buf->len == 0)) { 
     163        return PJ_FALSE; 
     164    } else { 
     165        unsigned rlen = recv_buf_right_len(recv_buf); 
     166        if (rlen) { 
     167            unsigned cur_pos = recv_buf->cur_pos; 
     168            pj_memmove(&recv_buf->rbuf[cur_pos-1], &recv_buf->rbuf[cur_pos],  
     169                       rlen); 
     170        } 
     171        --recv_buf->cur_pos; 
     172        --recv_buf->len; 
     173        recv_buf->rbuf[recv_buf->len] = 0; 
     174    }     
     175    return PJ_TRUE; 
     176} 
    46177 
    47178static void cli_console_write_log(pj_cli_front_end *fe, int level, 
     
    87218    pj_assert(param); 
    88219 
    89     param->log_level = PJ_CLI_CONSOLE_LOG_LEVEL; 
     220    param->log_level = PJ_CLI_CONSOLE_LOG_LEVEL;     
    90221} 
    91222 
     
    109240    sess = PJ_POOL_ZALLOC_T(pool, pj_cli_sess); 
    110241    fe = PJ_POOL_ZALLOC_T(pool, struct cli_console_fe); 
     242    fe->history = PJ_POOL_ZALLOC_T(pool, struct cmd_history); 
     243    pj_list_init(fe->history); 
     244    fe->active_history = fe->history; 
    111245 
    112246    if (!param) { 
     
    129263    pj_cli_register_front_end(cli, &fe->base); 
    130264 
     265    if (fe->cfg.prompt_str.slen == 0) {  
     266        pj_str_t prompt_sign = pj_str(">>> "); 
     267        char *prompt_data = pj_pool_alloc(fe->pool, 5);  
     268        fe->cfg.prompt_str.ptr = prompt_data;    
     269 
     270        pj_strcpy(&fe->cfg.prompt_str, &prompt_sign); 
     271        prompt_data[4] = 0; 
     272    } 
     273 
    131274    *p_sess = sess; 
    132275    if (p_fe) 
     
    136279} 
    137280 
    138 static int readline_thread(void * p) 
    139 { 
     281static void send_prompt_str(pj_cli_sess *sess) 
     282{ 
     283    pj_str_t send_data; 
     284    char data_str[128]; 
     285    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
     286 
     287    send_data.ptr = &data_str[0]; 
     288    send_data.slen = 0; 
     289     
     290    pj_strcat(&send_data, &fe->cfg.prompt_str); 
     291    send_data.ptr[send_data.slen] = 0; 
     292 
     293    printf("%s", send_data.ptr); 
     294} 
     295 
     296static void send_err_arg(pj_cli_sess *sess,  
     297                         const pj_cli_exec_info *info,  
     298                         const pj_str_t *msg, 
     299                         pj_bool_t with_return, 
     300                         pj_bool_t with_last_cmd) 
     301{ 
     302    pj_str_t send_data; 
     303    char data_str[256]; 
     304    unsigned len; 
     305    unsigned i; 
     306    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
     307    console_recv_buf *recv_buf = &fe->input.recv_buf; 
     308 
     309    send_data.ptr = &data_str[0]; 
     310    send_data.slen = 0; 
     311 
     312    if (with_return) 
     313        pj_strcat2(&send_data, "\r\n"); 
     314 
     315    len = fe->cfg.prompt_str.slen + info->err_pos; 
     316 
     317    for (i=0;i<len;++i) { 
     318        pj_strcat2(&send_data, " "); 
     319    } 
     320    pj_strcat2(&send_data, "^"); 
     321    pj_strcat2(&send_data, "\r\n"); 
     322    pj_strcat(&send_data, msg); 
     323    pj_strcat(&send_data, &fe->cfg.prompt_str); 
     324    if (with_last_cmd) 
     325        pj_strcat2(&send_data, (char *)&recv_buf->rbuf[0]); 
     326 
     327    send_data.ptr[send_data.slen] = 0; 
     328    printf("%s", send_data.ptr); 
     329} 
     330 
     331static void send_return_key(pj_cli_sess *sess)  
     332{     
     333    PJ_UNUSED_ARG(sess); 
     334    printf("\r\n");     
     335} 
     336 
     337static void send_inv_arg(pj_cli_sess *sess,  
     338                         const pj_cli_exec_info *info, 
     339                         pj_bool_t with_return, 
     340                         pj_bool_t with_last_cmd) 
     341{ 
     342    static const pj_str_t ERR_MSG = {"%Error : Invalid Arguments\r\n", 28}; 
     343    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd); 
     344} 
     345 
     346static void send_too_many_arg(pj_cli_sess *sess,  
     347                              const pj_cli_exec_info *info, 
     348                              pj_bool_t with_return, 
     349                              pj_bool_t with_last_cmd) 
     350{ 
     351    static const pj_str_t ERR_MSG = {"%Error : Too Many Arguments\r\n", 29}; 
     352    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd); 
     353} 
     354 
     355static void send_ambi_arg(pj_cli_sess *sess,  
     356                          const pj_cli_exec_info *info, 
     357                          pj_bool_t with_return, 
     358                          pj_bool_t with_last_cmd) 
     359{ 
     360    unsigned i; 
     361    pj_ssize_t j; 
     362    unsigned len; 
     363    pj_str_t send_data; 
     364    char data[1028]; 
     365    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
     366    console_recv_buf *recv_buf = &fe->input.recv_buf; 
     367    const pj_cli_hint_info *hint = info->hint; 
     368    pj_bool_t is_process_sc = PJ_FALSE; 
     369    pj_bool_t valid_type = PJ_FALSE; 
     370    pj_ssize_t max_length = 0; 
     371    const pj_str_t sc_type = pj_str("SC"); 
     372    send_data.ptr = &data[0]; 
     373    send_data.slen = 0; 
     374     
     375    if (with_return) 
     376        pj_strcat2(&send_data, "\r\n"); 
     377 
     378    len = fe->cfg.prompt_str.slen + info->err_pos; 
     379 
     380    for (i=0;i<len;++i) { 
     381        pj_strcat2(&send_data, " "); 
     382    } 
     383    pj_strcat2(&send_data, "^");         
     384 
     385    /* Get the max length of the command name */ 
     386    for (i=0;i<info->hint_cnt;++i) { 
     387        if (hint[i].name.slen > max_length) { 
     388            max_length = hint[i].name.slen; 
     389        } 
     390    } 
     391 
     392    for (i=0;i<info->hint_cnt;++i) {     
     393        if ((&hint[i].type) && (hint[i].type.slen > 0)) { 
     394            valid_type = PJ_TRUE; 
     395            if (pj_stricmp(&hint[i].type, &sc_type) == 0) { 
     396                if (is_process_sc) { 
     397                    pj_strcat2(&send_data, ", "); 
     398                } else { 
     399                    pj_strcat2(&send_data, "\r\n\t Shorcut: "); 
     400                    is_process_sc = PJ_TRUE; 
     401                } 
     402                pj_strcat(&send_data, &hint[i].name); 
     403            } else { 
     404                is_process_sc = PJ_FALSE; 
     405            } 
     406        } else {             
     407            valid_type = PJ_FALSE; 
     408            is_process_sc = PJ_FALSE; 
     409        } 
     410 
     411        if (!is_process_sc) { 
     412            pj_strcat2(&send_data, "\r\n\t"); 
     413 
     414            if (valid_type) { 
     415                pj_strcat2(&send_data, "<"); 
     416                pj_strcat(&send_data, &hint[i].type); 
     417                pj_strcat2(&send_data, ">");     
     418            } else { 
     419                pj_strcat(&send_data, &hint[i].name);        
     420            } 
     421         
     422            if ((&hint[i].desc) && (hint[i].desc.slen > 0)) { 
     423                if (!valid_type) { 
     424                    for (j=0;j<(max_length-hint[i].name.slen);++j) { 
     425                        pj_strcat2(&send_data, " "); 
     426                    } 
     427                } 
     428                pj_strcat2(&send_data, "\t"); 
     429                pj_strcat(&send_data, &hint[i].desc);        
     430            } 
     431        } 
     432    }         
     433    pj_strcat2(&send_data, "\r\n"); 
     434    pj_strcat(&send_data, &fe->cfg.prompt_str); 
     435    if (with_last_cmd) 
     436        pj_strcat2(&send_data, (char *)&recv_buf->rbuf[0]); 
     437 
     438    send_data.ptr[send_data.slen] = 0; 
     439    printf("%s", send_data.ptr); 
     440} 
     441 
     442static void send_comp_arg(pj_cli_exec_info *info) 
     443{ 
     444    pj_str_t send_data; 
     445    char data[128]; 
     446 
     447    pj_strcat2(&info->hint[0].name, " "); 
     448 
     449    send_data.ptr = &data[0]; 
     450    send_data.slen = 0; 
     451 
     452    pj_strcat(&send_data, &info->hint[0].name);         
     453 
     454    send_data.ptr[send_data.slen] = 0; 
     455    printf("%s", send_data.ptr); 
     456} 
     457 
     458static int compare_str(void *value, const pj_list_type *nd) 
     459{ 
     460    cmd_history *node = (cmd_history*)nd; 
     461    return (pj_strcmp((pj_str_t *)value, &node->command)); 
     462} 
     463 
     464static pj_status_t insert_history(pj_cli_sess *sess,                              
     465                                  char *cmd_val) 
     466{ 
     467    cmd_history *in_history; 
     468    pj_str_t cmd; 
     469    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe;     
     470    cmd.ptr = cmd_val; 
     471    cmd.slen = pj_ansi_strlen(cmd_val)-1; 
     472 
     473    if (cmd.slen == 0) 
     474        return PJ_SUCCESS; 
     475 
     476    PJ_ASSERT_RETURN(sess, PJ_EINVAL); 
     477 
     478    /* Find matching history */ 
     479    in_history = pj_list_search(fe->history, (void*)&cmd, compare_str); 
     480    if (!in_history) { 
     481        if (pj_list_size(fe->history) < PJ_CLI_MAX_CMD_HISTORY) {            
     482            char *data_history; 
     483            in_history = PJ_POOL_ZALLOC_T(fe->pool, cmd_history); 
     484            pj_list_init(in_history); 
     485            data_history = (char *)pj_pool_calloc(fe->pool,  
     486                           sizeof(char), PJ_CLI_MAX_CMDBUF); 
     487            in_history->command.ptr = data_history; 
     488            in_history->command.slen = 0; 
     489        } else { 
     490            /* Get the oldest history */ 
     491            in_history = fe->history->prev; 
     492        }            
     493    } else { 
     494        pj_list_insert_nodes_after(in_history->prev, in_history->next); 
     495    } 
     496    pj_strcpy(&in_history->command, pj_strtrim(&cmd)); 
     497    pj_list_push_front(fe->history, in_history); 
     498    fe->active_history = fe->history; 
     499 
     500    return PJ_SUCCESS; 
     501} 
     502 
     503static pj_str_t* get_prev_history(pj_cli_sess *sess, pj_bool_t is_forward) 
     504{ 
     505    pj_str_t *retval; 
     506    pj_size_t history_size; 
     507    cmd_history *node; 
     508    cmd_history *root; 
     509    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
     510 
     511    PJ_ASSERT_RETURN(sess && fe, NULL); 
     512 
     513    node = fe->active_history; 
     514    root = fe->history; 
     515    history_size = pj_list_size(fe->history); 
     516 
     517    if (history_size == 0) { 
     518        return NULL; 
     519    } else { 
     520        if (is_forward) {            
     521            node = (node->next==root)?node->next->next:node->next;           
     522        } else { 
     523            node = (node->prev==root)?node->prev->prev:node->prev;           
     524        } 
     525        retval = &node->command; 
     526        fe->active_history = node; 
     527    } 
     528    return retval; 
     529} 
     530 
     531static pj_bool_t handle_alfa_num(console_recv_buf *recv_buf,  
     532                                 unsigned char *cdata) 
     533{         
     534    if (recv_buf_right_len(recv_buf) > 0) { 
     535        char out_str[255];       
     536        pj_memset(&out_str[0], 0, 255); 
     537        out_str[0] = *cdata; 
     538        pj_memcpy(&out_str[1], &recv_buf->rbuf[recv_buf->cur_pos],  
     539                  recv_buf_right_len(recv_buf)); 
     540        pj_memset(&out_str[recv_buf_right_len(recv_buf)+1], '\b',  
     541                  recv_buf_right_len(recv_buf));         
     542        printf("%s", out_str);  
     543    } else {     
     544        printf("%c", *cdata); 
     545    }     
     546    return PJ_TRUE; 
     547} 
     548 
     549static pj_bool_t handle_backspace(console_recv_buf *recv_buf) 
     550{     
     551    if (recv_buf_backspace(recv_buf)) { 
     552        if(recv_buf_right_len(recv_buf) > 0) { 
     553            char out_str[255]; 
     554            pj_memset(&out_str[0], 0, 255); 
     555            out_str[0] = '\b'; 
     556            pj_memcpy(&out_str[1], &recv_buf->rbuf[recv_buf->cur_pos],  
     557                recv_buf_right_len(recv_buf)); 
     558            out_str[recv_buf_right_len(recv_buf)+1] = ' ';  
     559            pj_memset(&out_str[recv_buf_right_len(recv_buf)+2], '\b',  
     560                recv_buf_right_len(recv_buf)+1); 
     561            printf("%s", out_str); 
     562        } else { 
     563            char out_str[4]; 
     564            out_str[0] = '\b'; 
     565            out_str[1] = ' '; 
     566            out_str[2] = '\b'; 
     567            out_str[3] = 0; 
     568            printf("%s", out_str); 
     569        } 
     570        return PJ_TRUE; 
     571    } 
     572    return PJ_FALSE; 
     573} 
     574 
     575static pj_bool_t handle_tab(pj_cli_sess *sess) 
     576{ 
     577    pj_status_t status; 
     578    pj_bool_t retval = PJ_TRUE; 
     579    unsigned len; 
     580     
     581    pj_pool_t *pool; 
     582    pj_cli_cmd_val *cmd_val; 
     583    pj_cli_exec_info info; 
     584    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
     585    console_recv_buf *recv_buf = &fe->input.recv_buf; 
     586    pj_cli_t *cli = sess->fe->cli; 
     587 
     588    pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_tab", 
     589                          PJ_CLI_CONSOLE_POOL_SIZE, PJ_CLI_CONSOLE_POOL_INC, 
     590                          NULL); 
     591 
     592    cmd_val = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_val); 
     593     
     594    status = pj_cli_sess_parse(sess, (char *)recv_buf->rbuf, cmd_val,  
     595                               pool, &info);     
     596 
     597    len = pj_ansi_strlen((char *)recv_buf->rbuf); 
     598 
     599    switch (status) { 
     600    case PJ_CLI_EINVARG: 
     601        send_inv_arg(sess, &info, PJ_TRUE, PJ_TRUE);             
     602        break; 
     603    case PJ_CLI_ETOOMANYARGS: 
     604        send_too_many_arg(sess, &info, PJ_TRUE, PJ_TRUE); 
     605        break; 
     606    case PJ_CLI_EMISSINGARG: 
     607    case PJ_CLI_EAMBIGUOUS: 
     608        send_ambi_arg(sess, &info, PJ_TRUE, PJ_TRUE); 
     609        break; 
     610    case PJ_SUCCESS: 
     611        if (len > recv_buf->cur_pos) 
     612        { 
     613            /* Send the cursor to EOL */ 
     614            unsigned char *data_sent = &recv_buf->rbuf[recv_buf->cur_pos-1]; 
     615            printf("%s", data_sent);         
     616        } 
     617        if (info.hint_cnt > 0) {         
     618            /* Compelete command */ 
     619            send_comp_arg(&info); 
     620 
     621            pj_memcpy(&recv_buf->rbuf[len],   
     622                      &info.hint[0].name.ptr[0], info.hint[0].name.slen); 
     623 
     624            len += info.hint[0].name.slen; 
     625            recv_buf->rbuf[len] = 0;                 
     626        } else { 
     627            retval = PJ_FALSE; 
     628        } 
     629        break; 
     630    } 
     631    recv_buf->len = len; 
     632    recv_buf->cur_pos = len; 
     633 
     634    pj_pool_release(pool);       
     635    return retval;  
     636} 
     637 
     638static pj_bool_t handle_return(pj_cli_sess *sess) 
     639{ 
     640    pj_status_t status; 
     641    pj_bool_t retval = PJ_TRUE; 
     642     
     643    pj_pool_t *pool;     
     644    pj_cli_exec_info info; 
     645    pj_cli_t *cli = sess->fe->cli; 
     646    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
     647    console_recv_buf *recv_buf = &fe->input.recv_buf; 
     648     
     649    send_return_key(sess);     
     650    insert_history(sess, (char *)&recv_buf->rbuf[0]); 
     651 
     652    pool = pj_pool_create(pj_cli_get_param(cli)->pf, "handle_return", 
     653                          PJ_CLI_CONSOLE_POOL_SIZE, PJ_CLI_CONSOLE_POOL_INC, 
     654                          NULL);     
     655     
     656    status = pj_cli_sess_exec(sess, (char *)&recv_buf->rbuf[0],  
     657                              pool, &info); 
     658 
     659    switch (status) { 
     660    case PJ_CLI_EINVARG: 
     661        send_inv_arg(sess, &info, PJ_FALSE, PJ_FALSE);   
     662        break; 
     663    case PJ_CLI_ETOOMANYARGS: 
     664        send_too_many_arg(sess, &info, PJ_FALSE, PJ_FALSE); 
     665        break; 
     666    case PJ_CLI_EAMBIGUOUS: 
     667    case PJ_CLI_EMISSINGARG: 
     668        send_ambi_arg(sess, &info, PJ_FALSE, PJ_FALSE); 
     669        break; 
     670    case PJ_CLI_EEXIT: 
     671        retval = PJ_FALSE; 
     672        break; 
     673    case PJ_SUCCESS: 
     674        send_prompt_str(sess); 
     675        break; 
     676    }     
     677    if (retval) { 
     678        recv_buf->rbuf[0] = 0; 
     679        recv_buf->len = 0; 
     680        recv_buf->cur_pos = 0; 
     681    } 
     682 
     683    pj_pool_release(pool);       
     684    return retval;  
     685} 
     686 
     687static pj_bool_t handle_up_down(pj_cli_sess *sess, pj_bool_t is_up) 
     688{ 
     689    pj_str_t *history; 
     690    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
     691    console_recv_buf *recv_buf = &fe->input.recv_buf; 
     692 
     693    PJ_ASSERT_RETURN(sess && fe, PJ_FALSE); 
     694 
     695    history = get_prev_history(sess, is_up); 
     696    if (history) { 
     697        pj_str_t send_data; 
     698        char str[PJ_CLI_MAX_CMDBUF]; 
     699        send_data.ptr = &str[0]; 
     700        send_data.slen = 0; 
     701 
     702        if (recv_buf->cur_pos > 0) { 
     703            pj_memset(send_data.ptr, 0x08, recv_buf->cur_pos); 
     704            send_data.slen = recv_buf->cur_pos; 
     705        } 
     706 
     707        if (recv_buf->len > (unsigned)history->slen) { 
     708            unsigned buf_len = recv_buf->len; 
     709            pj_memset(&send_data.ptr[send_data.slen], 0x20, buf_len); 
     710            send_data.slen += buf_len; 
     711            pj_memset(&send_data.ptr[send_data.slen], 0x08, buf_len); 
     712            send_data.slen += buf_len; 
     713        }  
     714        /* Send data */ 
     715        pj_strcat(&send_data, history);  
     716        send_data.ptr[send_data.slen] = 0; 
     717        printf("%s", send_data.ptr); 
     718        pj_ansi_strncpy((char*)&recv_buf->rbuf, history->ptr, history->slen); 
     719        recv_buf->rbuf[history->slen] = 0; 
     720        recv_buf->len = history->slen; 
     721        recv_buf->cur_pos = recv_buf->len; 
     722        return PJ_TRUE; 
     723    } 
     724    return PJ_FALSE; 
     725} 
     726 
     727static pj_bool_t handle_left_key(console_recv_buf *recv_buf) 
     728{ 
     729    const static unsigned char BACK_SPACE = 0x08; 
     730    if (recv_buf->cur_pos) { 
     731        printf("%c", BACK_SPACE); 
     732        --recv_buf->cur_pos; 
     733        return PJ_TRUE; 
     734    } 
     735    return PJ_FALSE; 
     736} 
     737 
     738static pj_bool_t handle_right_key(console_recv_buf *recv_buf) 
     739{ 
     740    if (recv_buf_right_len(recv_buf)) { 
     741        unsigned char *data = &recv_buf->rbuf[recv_buf->cur_pos++]; 
     742        printf("%c", *data); 
     743        return PJ_TRUE; 
     744    } 
     745    return PJ_FALSE; 
     746} 
     747 
     748static int readchar_thread(void * p) 
     749{     
    140750    struct cli_console_fe * fe = (struct cli_console_fe *)p; 
    141     int i; 
     751    cmd_parse_state parse_state = ST_NORMAL; 
     752 
     753    printf("%s", fe->cfg.prompt_str.ptr); 
    142754 
    143755    while (!fe->thread_quit) { 
    144         fgets(fe->input.buf, fe->input.maxlen, stdin); 
    145         for (i = pj_ansi_strlen(fe->input.buf) - 1; i >= 0; i--) { 
    146             if (fe->input.buf[i] == '\n' || fe->input.buf[i] == '\r') 
    147                 fe->input.buf[i] = 0; 
    148             else 
    149                 break; 
    150         } 
    151         pj_sem_post(fe->input.sem); 
    152         /* Sleep until the next call of pj_cli_console_readline() */ 
     756        unsigned char cdata; 
     757        console_recv_buf *recv_buf = &fe->input.recv_buf; 
     758        pj_bool_t is_valid = PJ_TRUE; 
     759 
     760        cdata = (unsigned char)getch(); 
     761 
     762        switch (parse_state) { 
     763        case ST_NORMAL: 
     764            if (cdata == '\b') { 
     765                is_valid = handle_backspace(recv_buf); 
     766            } else if (cdata == 224) {       
     767                parse_state = ST_SCANMODE; 
     768            } else if (cdata == 27) {        
     769                parse_state = ST_ESC; 
     770            } else { 
     771                if (cdata == '\n') 
     772                    cdata = '\r'; 
     773                if (recv_buf_insert(recv_buf, &cdata)) { 
     774                    if (cdata == '\r') { 
     775                        is_valid = handle_return(fe->sess); 
     776                    } else if ((cdata == '\t') || (cdata == '?')) { 
     777                        is_valid = handle_tab(fe->sess); 
     778                    } else if (cdata > 31 && cdata < 127) { 
     779                        is_valid = handle_alfa_num(recv_buf, &cdata);                    
     780                    } 
     781                } else { 
     782                    is_valid = PJ_FALSE; 
     783                } 
     784            }        
     785            break; 
     786        case ST_SCANMODE: 
     787            switch (cdata) { 
     788            case 72: 
     789                //UP 
     790            case 80: 
     791                //DOwN 
     792                is_valid = handle_up_down(fe->sess, (cdata==72)); 
     793                break; 
     794            case 75: 
     795                is_valid = handle_left_key(recv_buf); 
     796                //LEFT 
     797                break; 
     798            case 77: 
     799                is_valid = handle_right_key(recv_buf); 
     800                //RIGHT 
     801                break; 
     802            }; 
     803            parse_state = ST_NORMAL; 
     804            break; 
     805        case ST_ESC: 
     806            parse_state = (cdata == 91)?ST_ARROWMODE:ST_NORMAL; 
     807            break; 
     808        case ST_ARROWMODE: 
     809            switch (cdata) { 
     810            case 65: 
     811                //UP 
     812            case 66: 
     813                //DOwN 
     814                is_valid = handle_up_down(fe->sess, (cdata==65)); 
     815                break; 
     816            case 68: 
     817                is_valid = handle_left_key(recv_buf); 
     818                //LEFT 
     819                break; 
     820            case 67: 
     821                is_valid = handle_right_key(recv_buf); 
     822                //RIGHT 
     823                break; 
     824            }; 
     825            parse_state = ST_NORMAL;         
     826            break; 
     827        }; 
     828 
     829        pj_sem_post(fe->input.sem);         
    153830        pj_sem_wait(fe->thread_sem); 
    154831    } 
     
    158835} 
    159836 
    160 PJ_DEF(pj_status_t) pj_cli_console_readline(pj_cli_sess *sess, 
    161                                             char *buf, 
    162                                             unsigned maxlen) 
     837PJ_DEF(pj_status_t) pj_cli_console_process(pj_cli_sess *sess) 
    163838{ 
    164839    struct cli_console_fe *fe = (struct cli_console_fe *)sess->fe; 
    165840 
    166     PJ_ASSERT_RETURN(sess && buf, PJ_EINVAL); 
    167  
    168     fe->input.buf = buf; 
    169     fe->input.maxlen = maxlen; 
     841    PJ_ASSERT_RETURN(sess, PJ_EINVAL); 
    170842 
    171843    if (!fe->input_thread) { 
    172844        pj_status_t status; 
    173845 
    174         status = pj_thread_create(fe->pool, NULL, &readline_thread, fe, 
     846        status = pj_thread_create(fe->pool, NULL, &readchar_thread, fe, 
    175847                                  0, 0, &fe->input_thread); 
    176848        if (status != PJ_SUCCESS) 
    177849            return status; 
    178850    } else { 
    179         /* Wake up readline thread */ 
     851        /* Wake up readchar thread */ 
    180852        pj_sem_post(fe->thread_sem); 
    181853    } 
  • pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli_telnet.c

    r3232 r4299  
    4242#endif 
    4343 
    44 struct cli_telnet_sess 
    45 { 
    46     pj_cli_sess         base; 
    47     pj_pool_t          *pool; 
    48     pj_activesock_t    *asock; 
    49     pj_bool_t           authorized; 
    50     pj_ioqueue_op_key_t op_key; 
    51     pj_mutex_t         *smutex; 
    52  
    53     char                rcmd[PJ_CLI_MAX_CMDBUF]; 
    54     int                 len; 
    55     char                buf[CLI_TELNET_BUF_SIZE + MAX_CUT_MSG_LEN]; 
    56     unsigned            buf_len; 
     44#define MAX_CLI_TELNET_OPTIONS 256 
     45 
     46/** 
     47 * This specify the state for the telnet option negotiation. 
     48 */ 
     49enum cli_telnet_option_states  
     50{     
     51    OPT_DISABLE,                /* Option disable */ 
     52    OPT_ENABLE,                 /* Option enable */ 
     53    OPT_EXPECT_DISABLE,         /* Already send disable req, expecting resp */ 
     54    OPT_EXPECT_ENABLE,          /* Already send enable req, expecting resp */ 
     55    OPT_EXPECT_DISABLE_REV,     /* Already send disable req, expecting resp,  
     56                                 * need to send enable req */ 
     57    OPT_EXPECT_ENABLE_REV       /* Already send enable req, expecting resp, 
     58                                 * need to send disable req */ 
    5759}; 
     60 
     61/** 
     62 * This structure contains information for telnet session option negotiation. 
     63 * It contains the local/peer option config and the option negotiation state. 
     64 */ 
     65typedef struct cli_telnet_sess_option 
     66{ 
     67    /** 
     68     * Local setting for the option. 
     69     * Default: FALSE; 
     70     */ 
     71    pj_bool_t local_is_enable; 
     72 
     73    /** 
     74     * Remote setting for the option. 
     75     * Default: FALSE; 
     76     */ 
     77    pj_bool_t peer_is_enable; 
     78 
     79    /** 
     80     * Local state of the option negotiation. 
     81     */ 
     82    enum cli_telnet_option_states local_state; 
     83 
     84    /** 
     85     * Remote state of the option negotiation. 
     86     */ 
     87    enum cli_telnet_option_states peer_state; 
     88} cli_telnet_sess_option; 
     89 
     90/** 
     91 * This specify the state of input character parsing. 
     92 */ 
     93typedef enum cmd_parse_state 
     94{ 
     95    ST_NORMAL, 
     96    ST_CR, 
     97    ST_ESC, 
     98    ST_VT100, 
     99    ST_IAC, 
     100    ST_DO, 
     101    ST_DONT, 
     102    ST_WILL, 
     103    ST_WONT 
     104} cmd_parse_state; 
     105 
     106typedef enum cli_telnet_command 
     107{ 
     108    SUBNEGO_END     = 240,      /* End of subnegotiation parameters. */ 
     109    NOP             = 241,      /* No operation. */ 
     110    DATA_MARK       = 242,      /* Marker for NVT cleaning. */ 
     111    BREAK           = 243,      /* Indicates that the "break" key was hit. */ 
     112    INT_PROCESS     = 244,      /* Suspend, interrupt or abort the process. */ 
     113    ABORT_OUTPUT    = 245,      /* Abort output, abort output stream. */ 
     114    ARE_YOU_THERE   = 246,      /* Are you there. */ 
     115    ERASE_CHAR      = 247,      /* Erase character, erase the current char. */ 
     116    ERASE_LINE      = 248,      /* Erase line, erase the current line. */ 
     117    GO_AHEAD        = 249,      /* Go ahead, other end can transmit. */ 
     118    SUBNEGO_BEGIN   = 250,      /* Subnegotiation begin. */ 
     119    WILL            = 251,      /* Accept the use of option. */ 
     120    WONT            = 252,      /* Refuse the use of option. */ 
     121    DO              = 253,      /* Request to use option. */ 
     122    DONT            = 254,      /* Request to not use option. */ 
     123    IAC             = 255       /* Interpret as command */ 
     124} cli_telnet_command; 
     125 
     126enum cli_telnet_options 
     127{ 
     128    TRANSMIT_BINARY     = 0,    /* Transmit Binary. */ 
     129    ECHO                = 1,    /* Echo. */ 
     130    RECONNECT           = 2,    /* Reconnection. */ 
     131    SUPPRESS_GA         = 3,    /* Suppress Go Aheah. */ 
     132    MESSAGE_SIZE_NEGO   = 4,    /* Approx Message Size Negotiation. */ 
     133    STATUS              = 5,    /* Status. */ 
     134    TIMING_MARK         = 6,    /* Timing Mark. */ 
     135    RTCE_OPTION         = 7,    /* Remote Controlled Trans and Echo. */ 
     136    OUTPUT_LINE_WIDTH   = 8,    /* Output Line Width. */ 
     137    OUTPUT_PAGE_SIZE    = 9,    /* Output Page Size. */ 
     138    CR_DISPOSITION      = 10,   /* Carriage-Return Disposition. */ 
     139    HORI_TABSTOPS       = 11,   /* Horizontal Tabstops. */ 
     140    HORI_TAB_DISPO      = 12,   /* Horizontal Tab Disposition. */ 
     141    FF_DISP0            = 13,   /* Formfeed Disposition. */ 
     142    VERT_TABSTOPS       = 14,   /* Vertical Tabstops. */ 
     143    VERT_TAB_DISPO      = 15,   /* Vertical Tab Disposition. */ 
     144    LF_DISP0            = 16,   /* Linefeed Disposition. */ 
     145    EXT_ASCII           = 17,   /* Extended ASCII. */ 
     146    LOGOUT              = 18,   /* Logout. */ 
     147    BYTE_MACRO          = 19,   /* Byte Macro. */ 
     148    DE_TERMINAL         = 20,   /* Data Entry Terminal. */ 
     149    SUPDUP_PROTO        = 21,   /* SUPDUP Protocol. */ 
     150    SUPDUP_OUTPUT       = 22,   /* SUPDUP Output. */ 
     151    SEND_LOC            = 23,   /* Send Location. */ 
     152    TERM_TYPE           = 24,   /* Terminal Type. */ 
     153    EOR                 = 25,   /* End of Record. */ 
     154    TACACS_UID          = 26,   /* TACACS User Identification. */ 
     155    OUTPUT_MARKING      = 27,   /* Output Marking. */ 
     156    TTYLOC              = 28,   /* Terminal Location Number. */ 
     157    USE_3270_REGIME     = 29,   /* Telnet 3270 Regime. */ 
     158    USE_X3_PAD          = 30,   /* X.3 PAD. */ 
     159    WINDOW_SIZE         = 31,   /* Window Size. */ 
     160    TERM_SPEED          = 32,   /* Terminal Speed. */ 
     161    REM_FLOW_CONTROL    = 33,   /* Remote Flow Control. */ 
     162    LINE_MODE           = 34,   /* Linemode. */ 
     163    X_DISP_LOC          = 35,   /* X Display Location. */ 
     164    ENVIRONMENT         = 36,   /* Environment. */ 
     165    AUTH                = 37,   /* Authentication. */ 
     166    ENCRYPTION          = 38,   /* Encryption Option. */ 
     167    NEW_ENVIRONMENT     = 39,   /* New Environment. */ 
     168    TN_3270E            = 40,   /* TN3270E. */ 
     169    XAUTH               = 41,   /* XAUTH. */ 
     170    CHARSET             = 42,   /* CHARSET. */ 
     171    REM_SERIAL_PORT     = 43,   /* Telnet Remote Serial Port. */ 
     172    COM_PORT_CONTROL    = 44,   /* Com Port Control. */ 
     173    SUPP_LOCAL_ECHO     = 45,   /* Telnet Suppress Local Echo. */ 
     174    START_TLS           = 46,   /* Telnet Start TLS. */ 
     175    KERMIT              = 47,   /* KERMIT. */ 
     176    SEND_URL            = 48,   /* SEND-URL. */ 
     177    FWD_X               = 49,   /* FORWARD_X. */ 
     178    EXT_OPTIONS         = 255   /* Extended-Options-List */ 
     179}; 
     180 
     181enum terminal_cmd  
     182{ 
     183    TC_ESC              = 27, 
     184    TC_UP               = 65, 
     185    TC_DOWN             = 66, 
     186    TC_RIGHT            = 67, 
     187    TC_LEFT             = 68, 
     188    TC_END              = 70, 
     189    TC_HOME             = 72,     
     190    TC_CTRL_C           = 3, 
     191    TC_CR               = 13, 
     192    TC_BS               = 8, 
     193    TC_TAB              = 9, 
     194    TC_QM               = 63, 
     195    TC_BELL             = 7, 
     196    TC_DEL              = 127 
     197}; 
     198 
     199/** 
     200 * This structure contains the command line shown to the user.   
     201 */ 
     202typedef struct telnet_recv_buf { 
     203    /** 
     204     * Buffer containing the characters, NULL terminated. 
     205     */ 
     206    unsigned char           rbuf[PJ_CLI_MAX_CMDBUF]; 
     207 
     208    /** 
     209     * Current length of the command line. 
     210     */ 
     211    unsigned                len; 
     212 
     213    /** 
     214     * Current cursor position. 
     215     */ 
     216    unsigned                cur_pos; 
     217} telnet_recv_buf; 
     218 
     219typedef struct cmd_history 
     220{ 
     221    PJ_DECL_LIST_MEMBER(struct cmd_history); 
     222    pj_str_t command; 
     223} cmd_history; 
     224 
     225typedef struct cli_telnet_sess 
     226{ 
     227    pj_cli_sess             base; 
     228    pj_pool_t               *pool; 
     229    pj_activesock_t         *asock; 
     230    pj_bool_t               authorized; 
     231    pj_ioqueue_op_key_t     op_key; 
     232    pj_mutex_t              *smutex; 
     233    cmd_parse_state         parse_state; 
     234    cli_telnet_sess_option  telnet_option[MAX_CLI_TELNET_OPTIONS]; 
     235    cmd_history             *history; 
     236    cmd_history             *active_history; 
     237 
     238    telnet_recv_buf         *rcmd; 
     239    unsigned char           buf[CLI_TELNET_BUF_SIZE + MAX_CUT_MSG_LEN]; 
     240    unsigned                buf_len; 
     241} cli_telnet_sess; 
    58242 
    59243struct cli_telnet_fe 
     
    71255}; 
    72256 
     257/* Forward Declaration */ 
     258static pj_status_t telnet_sess_send2(cli_telnet_sess *sess, 
     259                                     const unsigned char *str, int len); 
     260 
     261static pj_status_t telnet_sess_send(cli_telnet_sess *sess, 
     262                                    const pj_str_t *str); 
     263 
     264/** 
     265 * Return the number of characters between the current cursor position  
     266 * to the end of line. 
     267 */ 
     268static unsigned recv_buf_right_len(telnet_recv_buf *recv_buf) 
     269{ 
     270    return (recv_buf->len - recv_buf->cur_pos); 
     271} 
     272 
     273static pj_bool_t recv_buf_insert(telnet_recv_buf *recv_buf,  
     274                                 unsigned char *data)  
     275{     
     276    if (recv_buf->len+1 >= PJ_CLI_MAX_CMDBUF) { 
     277        return PJ_FALSE; 
     278    } else {     
     279        if (*data == '\t' || *data == '?' || *data == '\r') { 
     280            /* Always insert to the end of line */ 
     281            recv_buf->rbuf[recv_buf->len] = *data; 
     282        } else { 
     283            /* Insert based on the current cursor pos */ 
     284            unsigned cur_pos = recv_buf->cur_pos; 
     285            unsigned rlen = recv_buf_right_len(recv_buf);            
     286            if (rlen > 0) {                  
     287                /* Shift right characters */ 
     288                pj_memmove(&recv_buf->rbuf[cur_pos+1],  
     289                           &recv_buf->rbuf[cur_pos],  
     290                           rlen+1); 
     291            }  
     292            recv_buf->rbuf[cur_pos] = *data; 
     293        } 
     294        ++recv_buf->cur_pos; 
     295        ++recv_buf->len; 
     296        recv_buf->rbuf[recv_buf->len] = 0; 
     297    } 
     298    return PJ_TRUE; 
     299} 
     300 
     301static pj_bool_t recv_buf_delete(telnet_recv_buf *recv_buf) 
     302{ 
     303    if ((recv_buf->len == 0) || (recv_buf_right_len(recv_buf) == 0)) { 
     304        return PJ_FALSE; 
     305    } else { 
     306        unsigned rlen; 
     307        unsigned cur_pos = recv_buf->cur_pos; 
     308        rlen = recv_buf_right_len(recv_buf)-1; 
     309        pj_memmove(&recv_buf->rbuf[cur_pos], &recv_buf->rbuf[cur_pos+1],  
     310                   rlen); 
     311        --recv_buf->cur_pos; 
     312        --recv_buf->len; 
     313        recv_buf->rbuf[recv_buf->len] = 0; 
     314    } 
     315    return PJ_TRUE; 
     316} 
     317 
     318static pj_bool_t recv_buf_backspace(telnet_recv_buf *recv_buf) 
     319{ 
     320    if ((recv_buf->cur_pos == 0) || (recv_buf->len == 0)) { 
     321        return PJ_FALSE; 
     322    } else { 
     323        unsigned rlen = recv_buf_right_len(recv_buf); 
     324        if (rlen) { 
     325            unsigned cur_pos = recv_buf->cur_pos; 
     326            pj_memmove(&recv_buf->rbuf[cur_pos-1], &recv_buf->rbuf[cur_pos],  
     327                       rlen); 
     328        } 
     329        --recv_buf->cur_pos; 
     330        --recv_buf->len; 
     331        recv_buf->rbuf[recv_buf->len] = 0; 
     332    }     
     333    return PJ_TRUE; 
     334} 
     335 
     336static int compare_str(void *value, const pj_list_type *nd) 
     337{ 
     338    cmd_history *node = (cmd_history*)nd; 
     339    return (pj_strcmp((pj_str_t *)value, &node->command)); 
     340} 
     341 
     342static pj_status_t insert_history(cli_telnet_sess *sess,                                  
     343                                  char *cmd_val) 
     344{ 
     345    cmd_history *in_history; 
     346    pj_str_t cmd; 
     347    cmd.ptr = cmd_val; 
     348    cmd.slen = pj_ansi_strlen(cmd_val)-1; 
     349 
     350    if (cmd.slen == 0) 
     351        return PJ_SUCCESS; 
     352 
     353    PJ_ASSERT_RETURN(sess, PJ_EINVAL); 
     354 
     355    /* Find matching history */ 
     356    in_history = pj_list_search(sess->history, (void*)&cmd, compare_str); 
     357    if (!in_history) { 
     358        if (pj_list_size(sess->history) < PJ_CLI_MAX_CMD_HISTORY) {          
     359            char *data_history; 
     360            in_history = PJ_POOL_ZALLOC_T(sess->pool, cmd_history); 
     361            pj_list_init(in_history); 
     362            data_history = (char *)pj_pool_calloc(sess->pool,  
     363                           sizeof(char), PJ_CLI_MAX_CMDBUF); 
     364            in_history->command.ptr = data_history; 
     365            in_history->command.slen = 0; 
     366        } else { 
     367            /* Get the oldest history */ 
     368            in_history = sess->history->prev; 
     369        }            
     370    } else { 
     371        pj_list_insert_nodes_after(in_history->prev, in_history->next); 
     372    } 
     373    pj_strcpy(&in_history->command, pj_strtrim(&cmd)); 
     374    pj_list_push_front(sess->history, in_history); 
     375    sess->active_history = sess->history; 
     376 
     377    return PJ_SUCCESS; 
     378} 
     379 
     380static pj_str_t* get_prev_history(cli_telnet_sess *sess, pj_bool_t is_forward) 
     381{ 
     382    pj_str_t *retval; 
     383    pj_size_t history_size; 
     384    cmd_history *node; 
     385    cmd_history *root; 
     386 
     387    PJ_ASSERT_RETURN(sess, NULL); 
     388 
     389    node = sess->active_history; 
     390    root = sess->history; 
     391    history_size = pj_list_size(sess->history); 
     392 
     393    if (history_size == 0) { 
     394        return NULL; 
     395    } else { 
     396        if (is_forward) {            
     397            node = (node->next==root)?node->next->next:node->next;           
     398        } else { 
     399            node = (node->prev==root)?node->prev->prev:node->prev;           
     400        } 
     401        retval = &node->command; 
     402        sess->active_history = node; 
     403    } 
     404    return retval; 
     405} 
     406 
     407static pj_bool_t send_telnet_cmd(cli_telnet_sess *sess,  
     408                                 cli_telnet_command cmd,  
     409                                 unsigned char option) 
     410{        
     411    unsigned char buf[3]; 
     412    PJ_ASSERT_RETURN(sess, PJ_FALSE); 
     413 
     414    buf[0] = IAC; 
     415    buf[1] = cmd; 
     416    buf[2] = option; 
     417    telnet_sess_send2(sess, buf, 3); 
     418 
     419    return PJ_TRUE; 
     420} 
     421 
     422/** 
     423 * This method will handle sending telnet's ENABLE option negotiation. 
     424 * For local option: send WILL. 
     425 * For remote option: send DO. 
     426 * This method also handle the state transition of the ENABLE  
     427 * negotiation process. 
     428 */ 
     429static pj_bool_t send_enable_option(cli_telnet_sess *sess,  
     430                                    pj_bool_t is_local,  
     431                                    unsigned char option) 
     432{ 
     433    cli_telnet_sess_option *sess_option; 
     434    enum cli_telnet_option_states *state; 
     435    PJ_ASSERT_RETURN(sess, PJ_FALSE); 
     436 
     437    sess_option = &sess->telnet_option[option]; 
     438    state = is_local?(&sess_option->local_state):(&sess_option->peer_state); 
     439    switch (*state) { 
     440        case OPT_ENABLE: 
     441            /* Ignore if already enabled */ 
     442            break; 
     443        case OPT_DISABLE: 
     444            *state = OPT_EXPECT_ENABLE; 
     445            send_telnet_cmd(sess, (is_local?WILL:DO), option); 
     446            break; 
     447        case OPT_EXPECT_ENABLE: 
     448            *state = OPT_DISABLE; 
     449            break; 
     450        case OPT_EXPECT_DISABLE: 
     451            *state = OPT_EXPECT_DISABLE_REV; 
     452            break; 
     453        case OPT_EXPECT_ENABLE_REV: 
     454            *state = OPT_EXPECT_ENABLE; 
     455            break; 
     456        case OPT_EXPECT_DISABLE_REV:         
     457            *state = OPT_DISABLE; 
     458            break; 
     459        default: 
     460            return PJ_FALSE; 
     461    } 
     462    return PJ_TRUE; 
     463} 
     464 
     465/** 
     466 * This method will handle sending telnet's DISABLE option negotiation. 
     467 * For local option: send WON'T. 
     468 * For remote option: send DON'T. 
     469 * This method also handle the state transition of the DISABLE  
     470 * negotiation process.  
     471 */ 
     472static pj_bool_t send_disable_option(cli_telnet_sess *sess,  
     473                                     pj_bool_t is_local,  
     474                                     unsigned char option) 
     475{ 
     476    cli_telnet_sess_option *sess_option; 
     477    enum cli_telnet_option_states *state; 
     478    PJ_ASSERT_RETURN(sess, PJ_FALSE); 
     479 
     480    sess_option = &sess->telnet_option[option]; 
     481    state = is_local?(&sess_option->local_state):(&sess_option->peer_state); 
     482    switch (*state) { 
     483        case OPT_ENABLE: 
     484            *state = OPT_EXPECT_DISABLE; 
     485            send_telnet_cmd(sess, (is_local?WONT:DONT), option); 
     486            break; 
     487        case OPT_DISABLE: 
     488            /* Ignore if already disabled */ 
     489            break; 
     490        case OPT_EXPECT_ENABLE: 
     491            *state = OPT_EXPECT_ENABLE_REV; 
     492            break; 
     493        case OPT_EXPECT_DISABLE: 
     494            *state = OPT_ENABLE; 
     495            break; 
     496        case OPT_EXPECT_ENABLE_REV:          
     497            *state = OPT_ENABLE; 
     498            break; 
     499        case OPT_EXPECT_DISABLE_REV: 
     500            *state = OPT_EXPECT_DISABLE; 
     501            break; 
     502        default: 
     503            return PJ_FALSE; 
     504    } 
     505    return PJ_TRUE; 
     506} 
     507 
     508static pj_bool_t send_cmd_do(cli_telnet_sess *sess,  
     509                             unsigned char option) 
     510{ 
     511    return send_enable_option(sess, PJ_FALSE, option); 
     512} 
     513 
     514static pj_bool_t send_cmd_dont(cli_telnet_sess *sess,  
     515                               unsigned char option) 
     516{ 
     517    return send_disable_option(sess, PJ_FALSE, option); 
     518} 
     519 
     520static pj_bool_t send_cmd_will(cli_telnet_sess *sess,  
     521                               unsigned char option) 
     522{ 
     523    return send_enable_option(sess, PJ_TRUE, option); 
     524} 
     525 
     526static pj_bool_t send_cmd_wont(cli_telnet_sess *sess,  
     527                               unsigned char option) 
     528{ 
     529    return send_disable_option(sess, PJ_TRUE, option); 
     530} 
     531 
     532/** 
     533 * This method will handle receiving telnet's ENABLE option negotiation. 
     534 * This method also handle the state transition of the ENABLE  
     535 * negotiation process.  
     536 */ 
     537static pj_bool_t receive_enable_option(cli_telnet_sess *sess,  
     538                                       pj_bool_t is_local,  
     539                                       unsigned char option) 
     540{ 
     541    cli_telnet_sess_option *sess_opt; 
     542    enum cli_telnet_option_states *state; 
     543    pj_bool_t opt_ena; 
     544    PJ_ASSERT_RETURN(sess, PJ_FALSE); 
     545 
     546    sess_opt = &sess->telnet_option[option]; 
     547    state = is_local?(&sess_opt->local_state):(&sess_opt->peer_state); 
     548    opt_ena = is_local?sess_opt->local_is_enable:sess_opt->peer_is_enable; 
     549    switch (*state) { 
     550        case OPT_ENABLE: 
     551            /* Ignore if already enabled */ 
     552            break; 
     553        case OPT_DISABLE: 
     554            if (opt_ena) { 
     555                *state = OPT_ENABLE; 
     556                send_telnet_cmd(sess, is_local?WILL:DO, option); 
     557            } else { 
     558                send_telnet_cmd(sess, is_local?WONT:DONT, option); 
     559            } 
     560            break; 
     561        case OPT_EXPECT_ENABLE:      
     562            *state = OPT_ENABLE; 
     563            break; 
     564        case OPT_EXPECT_DISABLE:             
     565            *state = OPT_DISABLE; 
     566            break; 
     567        case OPT_EXPECT_ENABLE_REV: 
     568            *state = OPT_EXPECT_DISABLE; 
     569            send_telnet_cmd(sess, is_local?WONT:DONT, option);       
     570            break; 
     571        case OPT_EXPECT_DISABLE_REV: 
     572            *state = OPT_EXPECT_DISABLE; 
     573            break; 
     574        default: 
     575            return PJ_FALSE; 
     576    } 
     577    return PJ_TRUE;         
     578} 
     579 
     580/** 
     581 * This method will handle receiving telnet's DISABLE option negotiation. 
     582 * This method also handle the state transition of the DISABLE  
     583 * negotiation process.  
     584 */ 
     585static pj_bool_t receive_disable_option(cli_telnet_sess *sess,  
     586                                        pj_bool_t is_local,  
     587                                        unsigned char option) 
     588{ 
     589    cli_telnet_sess_option *sess_opt; 
     590    enum cli_telnet_option_states *state; 
     591    pj_bool_t opt_ena; 
     592    PJ_ASSERT_RETURN(sess, PJ_FALSE); 
     593 
     594    sess_opt = &sess->telnet_option[option]; 
     595    state = is_local?(&sess_opt->local_state):(&sess_opt->peer_state); 
     596    opt_ena = is_local?sess_opt->local_is_enable:sess_opt->peer_is_enable; 
     597    switch (*state) { 
     598        case OPT_ENABLE: 
     599            /* Disabling option always need to be accepted */ 
     600            *state = OPT_DISABLE; 
     601            send_telnet_cmd(sess, is_local?WONT:DONT, option); 
     602            break; 
     603        case OPT_DISABLE: 
     604            /* Ignore if already enabled */ 
     605            break; 
     606        case OPT_EXPECT_ENABLE:      
     607        case OPT_EXPECT_DISABLE: 
     608            *state = OPT_DISABLE; 
     609            break; 
     610        case OPT_EXPECT_ENABLE_REV: 
     611            *state = OPT_DISABLE; 
     612            send_telnet_cmd(sess, is_local?WONT:DONT, option);       
     613            break; 
     614        case OPT_EXPECT_DISABLE_REV: 
     615            *state = OPT_EXPECT_ENABLE; 
     616            send_telnet_cmd(sess, is_local?WILL:DO, option); 
     617            break; 
     618        default: 
     619            return PJ_FALSE; 
     620    } 
     621    return PJ_TRUE;        
     622} 
     623 
     624static pj_bool_t receive_do(cli_telnet_sess *sess, unsigned char option) 
     625{ 
     626    return receive_enable_option(sess, PJ_TRUE, option); 
     627} 
     628 
     629static pj_bool_t receive_dont(cli_telnet_sess *sess, unsigned char option) 
     630{ 
     631    return receive_disable_option(sess, PJ_TRUE, option); 
     632} 
     633 
     634static pj_bool_t receive_will(cli_telnet_sess *sess, unsigned char option) 
     635{ 
     636    return receive_enable_option(sess, PJ_FALSE, option); 
     637} 
     638 
     639static pj_bool_t receive_wont(cli_telnet_sess *sess, unsigned char option) 
     640{ 
     641    return receive_disable_option(sess, PJ_FALSE, option); 
     642} 
     643 
     644static void set_local_option(cli_telnet_sess *sess,  
     645                             unsigned char option,  
     646                             pj_bool_t enable)  
     647{ 
     648    sess->telnet_option[option].local_is_enable = enable; 
     649} 
     650 
     651static void set_peer_option(cli_telnet_sess *sess,  
     652                            unsigned char option,  
     653                            pj_bool_t enable)  
     654{ 
     655    sess->telnet_option[option].peer_is_enable = enable; 
     656} 
     657 
     658static pj_bool_t is_local_option_state_ena(cli_telnet_sess *sess,  
     659                                           unsigned char option) 
     660{     
     661    return (sess->telnet_option[option].local_state == OPT_ENABLE); 
     662} 
     663 
     664static pj_bool_t is_peer_option_state_ena(cli_telnet_sess *sess,  
     665                                          unsigned char option) 
     666{     
     667    return (sess->telnet_option[option].peer_state == OPT_ENABLE); 
     668} 
     669 
     670static void send_return_key(cli_telnet_sess *sess)  
     671{     
     672    telnet_sess_send2(sess, (unsigned char*)"\r\n", 2); 
     673} 
     674 
     675static void send_bell(cli_telnet_sess *sess) { 
     676    unsigned char bell[1] = {0x07}; 
     677    telnet_sess_send2(sess, &bell[0], 1); 
     678} 
     679 
     680static void send_prompt_str(cli_telnet_sess *sess) 
     681{ 
     682    pj_str_t send_data; 
     683    char data_str[128]; 
     684    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe; 
     685 
     686    send_data.ptr = &data_str[0]; 
     687    send_data.slen = 0; 
     688     
     689    pj_strcat(&send_data, &fe->cfg.prompt_str); 
     690 
     691    telnet_sess_send(sess, &send_data); 
     692} 
     693 
     694static void send_err_arg(cli_telnet_sess *sess,  
     695                         const pj_cli_exec_info *info,  
     696                         const pj_str_t *msg, 
     697                         pj_bool_t with_return, 
     698                         pj_bool_t with_last_cmd) 
     699{ 
     700    pj_str_t send_data; 
     701    char data_str[256]; 
     702    unsigned len; 
     703    unsigned i; 
     704    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe; 
     705 
     706    send_data.ptr = &data_str[0]; 
     707    send_data.slen = 0; 
     708 
     709    if (with_return) 
     710        pj_strcat2(&send_data, "\r\n"); 
     711 
     712    len = fe->cfg.prompt_str.slen + info->err_pos; 
     713 
     714    for (i=0;i<len;++i) { 
     715        pj_strcat2(&send_data, " "); 
     716    } 
     717    pj_strcat2(&send_data, "^"); 
     718    pj_strcat2(&send_data, "\r\n"); 
     719    pj_strcat(&send_data, msg); 
     720    pj_strcat(&send_data, &fe->cfg.prompt_str); 
     721    if (with_last_cmd) 
     722        pj_strcat2(&send_data, (char *)&sess->rcmd->rbuf[0]); 
     723 
     724    telnet_sess_send(sess, &send_data); 
     725} 
     726 
     727static void send_inv_arg(cli_telnet_sess *sess,  
     728                         const pj_cli_exec_info *info, 
     729                         pj_bool_t with_return, 
     730                         pj_bool_t with_last_cmd) 
     731{ 
     732    static const pj_str_t ERR_MSG = {"%Error : Invalid Arguments\r\n", 28}; 
     733    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd); 
     734} 
     735 
     736static void send_too_many_arg(cli_telnet_sess *sess,  
     737                              const pj_cli_exec_info *info, 
     738                              pj_bool_t with_return, 
     739                              pj_bool_t with_last_cmd) 
     740{ 
     741    static const pj_str_t ERR_MSG = {"%Error : Too Many Arguments\r\n", 29}; 
     742    send_err_arg(sess, info, &ERR_MSG, with_return, with_last_cmd); 
     743} 
     744 
     745static void send_ambi_arg(cli_telnet_sess *sess,  
     746                          const pj_cli_exec_info *info, 
     747                          pj_bool_t with_return, 
     748                          pj_bool_t with_last_cmd) 
     749{ 
     750    unsigned i; 
     751    pj_ssize_t j; 
     752    unsigned len; 
     753    pj_str_t send_data; 
     754    char data[1028]; 
     755    struct cli_telnet_fe *fe = (struct cli_telnet_fe *)sess->base.fe; 
     756    const pj_cli_hint_info *hint = info->hint; 
     757    pj_bool_t is_process_sc = PJ_FALSE; 
     758    pj_bool_t valid_type = PJ_FALSE; 
     759    pj_ssize_t max_length = 0; 
     760    const pj_str_t sc_type = pj_str("SC");     
     761    send_data.ptr = &data[0]; 
     762    send_data.slen = 0; 
     763     
     764    if (with_return) 
     765        pj_strcat2(&send_data, "\r\n"); 
     766 
     767    len = fe->cfg.prompt_str.slen + info->err_pos; 
     768 
     769    for (i=0;i<len;++i) { 
     770        pj_strcat2(&send_data, " "); 
     771    } 
     772    pj_strcat2(&send_data, "^"); 
     773    /* Get the max length of the command name */ 
     774    for (i=0;i<info->hint_cnt;++i) { 
     775        if (hint[i].name.slen > max_length) { 
     776            max_length = hint[i].name.slen; 
     777        } 
     778    } 
     779 
     780    for (i=0;i<info->hint_cnt;++i) {     
     781        if ((&hint[i].type) && (hint[i].type.slen > 0)) { 
     782            valid_type = PJ_TRUE; 
     783            if (pj_stricmp(&hint[i].type, &sc_type) == 0) { 
     784                if (is_process_sc) { 
     785                    pj_strcat2(&send_data, ", "); 
     786                } else { 
     787                    pj_strcat2(&send_data, "\r\n\t Shorcut: "); 
     788                    is_process_sc = PJ_TRUE; 
     789                } 
     790                pj_strcat(&send_data, &hint[i].name); 
     791            } else { 
     792                is_process_sc = PJ_FALSE; 
     793            } 
     794        } else {             
     795            valid_type = PJ_FALSE; 
     796            is_process_sc = PJ_FALSE; 
     797        } 
     798 
     799        if (!is_process_sc) { 
     800            pj_strcat2(&send_data, "\r\n\t"); 
     801 
     802            if (valid_type) { 
     803                pj_strcat2(&send_data, "<"); 
     804                pj_strcat(&send_data, &hint[i].type); 
     805                pj_strcat2(&send_data, ">");     
     806            } else { 
     807                pj_strcat(&send_data, &hint[i].name);        
     808            } 
     809         
     810            if ((&hint[i].desc) && (hint[i].desc.slen > 0)) { 
     811                if (!valid_type) { 
     812                    for (j=0;j<(max_length-hint[i].name.slen);++j) { 
     813                        pj_strcat2(&send_data, " "); 
     814                    } 
     815                } 
     816                pj_strcat2(&send_data, "\t"); 
     817                pj_strcat(&send_data, &hint[i].desc);        
     818            } 
     819        } 
     820    }    
     821    pj_strcat2(&send_data, "\r\n");        
     822    pj_strcat(&send_data, &fe->cfg.prompt_str); 
     823    if (with_last_cmd) 
     824        pj_strcat2(&send_data, (char *)&sess->rcmd->rbuf[0]); 
     825 
     826    telnet_sess_send(sess, &send_data);     
     827} 
     828 
     829static void send_comp_arg(cli_telnet_sess *sess,  
     830                          pj_cli_exec_info *info) 
     831{ 
     832    pj_str_t send_data; 
     833    char data[128]; 
     834 
     835    pj_strcat2(&info->hint[0].name, " "); 
     836 
     837    send_data.ptr = &data[0]; 
     838    send_data.slen = 0; 
     839 
     840    pj_strcat(&send_data, &info->hint[0].name);         
     841 
     842    telnet_sess_send(sess, &send_data); 
     843} 
     844 
     845static pj_bool_t handle_alfa_num(cli_telnet_sess *sess, unsigned char *data) 
     846{         
     847    if (is_local_option_state_ena(sess, ECHO)) { 
     848        if (recv_buf_right_len(sess->rcmd) > 0) { 
     849            /* Insert character */           
     850            unsigned char echo[5] = {0x1b, 0x5b, 0x31, 0x40, 0x00}; 
     851            echo[4] = *data; 
     852            telnet_sess_send2(sess, &echo[0], 5); 
     853        } else { 
     854            /* Append character */ 
     855            telnet_sess_send2(sess, data, 1); 
     856        } 
     857        return PJ_TRUE; 
     858    }  
     859    return PJ_FALSE; 
     860} 
     861 
     862static pj_bool_t handle_backspace(cli_telnet_sess *sess, unsigned char *data) 
     863{ 
     864    unsigned rlen = recv_buf_right_len(sess->rcmd); 
     865    if (recv_buf_backspace(sess->rcmd)) { 
     866        if (rlen) { 
     867            /* Cursor is not at the end of line */ 
     868            unsigned char echo[5] = {0x00, 0x1b, 0x5b, 0x31, 0x50}; 
     869            echo[0] = *data; 
     870            telnet_sess_send2(sess, &echo[0], 5); 
     871        } else { 
     872            const static unsigned char echo[3] = {0x08, 0x20, 0x08};         
     873            telnet_sess_send2(sess, &echo[0], 3); 
     874        } 
     875        return PJ_TRUE; 
     876    } 
     877    return PJ_FALSE; 
     878} 
     879 
     880static pj_bool_t handle_tab(cli_telnet_sess *sess) 
     881{ 
     882    pj_status_t status; 
     883    pj_bool_t retval = PJ_TRUE; 
     884    unsigned len; 
     885     
     886    pj_pool_t *pool; 
     887    pj_cli_cmd_val *cmd_val; 
     888    pj_cli_exec_info info; 
     889    pool = pj_pool_create(sess->pool->factory, "handle_tab", 
     890                          PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC, 
     891                          NULL); 
     892 
     893    cmd_val = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_val); 
     894     
     895    status = pj_cli_sess_parse(&sess->base, (char *)&sess->rcmd->rbuf, cmd_val,  
     896                               pool, &info);     
     897 
     898    len = pj_ansi_strlen((char *)&sess->rcmd->rbuf[0]); 
     899 
     900    switch (status) { 
     901    case PJ_CLI_EINVARG: 
     902        send_inv_arg(sess, &info, PJ_TRUE, PJ_TRUE);             
     903        break; 
     904    case PJ_CLI_ETOOMANYARGS: 
     905        send_too_many_arg(sess, &info, PJ_TRUE, PJ_TRUE); 
     906        break; 
     907    case PJ_CLI_EMISSINGARG: 
     908    case PJ_CLI_EAMBIGUOUS: 
     909        send_ambi_arg(sess, &info, PJ_TRUE, PJ_TRUE); 
     910        break; 
     911    case PJ_SUCCESS: 
     912        if (len > sess->rcmd->cur_pos) 
     913        { 
     914            /* Send the cursor to EOL */ 
     915            unsigned rlen = len - sess->rcmd->cur_pos+1; 
     916            unsigned char *data_sent = &sess->rcmd->rbuf[sess->rcmd->cur_pos-1]; 
     917            telnet_sess_send2(sess, data_sent, rlen); 
     918        } 
     919        if (info.hint_cnt > 0) {         
     920            /* Complete command */ 
     921            send_comp_arg(sess, &info); 
     922 
     923            pj_memcpy(&sess->rcmd->rbuf[len],  
     924                      &info.hint[0].name.ptr[0], info.hint[0].name.slen); 
     925 
     926            len += info.hint[0].name.slen; 
     927            sess->rcmd->rbuf[len] = 0;               
     928        } else { 
     929            retval = PJ_FALSE; 
     930        } 
     931        break; 
     932    } 
     933    sess->rcmd->len = len; 
     934    sess->rcmd->cur_pos = sess->rcmd->len; 
     935 
     936    pj_pool_release(pool);       
     937    return retval;  
     938} 
     939 
     940static pj_bool_t handle_return(cli_telnet_sess *sess) 
     941{ 
     942    pj_status_t status; 
     943    pj_bool_t retval = PJ_TRUE; 
     944     
     945    pj_pool_t *pool;     
     946    pj_cli_exec_info info;     
     947     
     948    send_return_key(sess); 
     949    insert_history(sess, (char *)&sess->rcmd->rbuf); 
     950 
     951    pool = pj_pool_create(sess->pool->factory, "handle_return", 
     952                          PJ_CLI_TELNET_POOL_SIZE, PJ_CLI_TELNET_POOL_INC, 
     953                          NULL);     
     954     
     955    status = pj_cli_sess_exec(&sess->base, (char *)&sess->rcmd->rbuf,  
     956                              pool, &info);     
     957 
     958    switch (status) { 
     959    case PJ_CLI_EINVARG: 
     960        send_inv_arg(sess, &info, PJ_FALSE, PJ_FALSE);   
     961        break; 
     962    case PJ_CLI_ETOOMANYARGS: 
     963        send_too_many_arg(sess, &info, PJ_FALSE, PJ_FALSE); 
     964        break; 
     965    case PJ_CLI_EAMBIGUOUS: 
     966    case PJ_CLI_EMISSINGARG: 
     967        send_ambi_arg(sess, &info, PJ_FALSE, PJ_FALSE); 
     968        break; 
     969    case PJ_CLI_EEXIT: 
     970        retval = PJ_FALSE; 
     971        break; 
     972    case PJ_SUCCESS: 
     973        send_prompt_str(sess); 
     974        break; 
     975    }     
     976    if (retval) { 
     977        sess->rcmd->rbuf[0] = 0; 
     978        sess->rcmd->len = 0; 
     979        sess->rcmd->cur_pos = sess->rcmd->len; 
     980    } 
     981 
     982    pj_pool_release(pool);       
     983    return retval;  
     984} 
     985 
     986static pj_bool_t handle_right_key(cli_telnet_sess *sess) 
     987{ 
     988    if (recv_buf_right_len(sess->rcmd)) { 
     989        unsigned char *data = &sess->rcmd->rbuf[sess->rcmd->cur_pos++]; 
     990        telnet_sess_send2(sess, data, 1);                
     991        return PJ_TRUE; 
     992    } 
     993    return PJ_FALSE; 
     994} 
     995 
     996static pj_bool_t handle_left_key(cli_telnet_sess *sess) 
     997{ 
     998    const static unsigned char BACK_SPACE = 0x08; 
     999    if (sess->rcmd->cur_pos) { 
     1000        telnet_sess_send2(sess, &BACK_SPACE, 1); 
     1001        --sess->rcmd->cur_pos; 
     1002        return PJ_TRUE; 
     1003    } 
     1004    return PJ_FALSE; 
     1005} 
     1006 
     1007static pj_bool_t handle_up_down(cli_telnet_sess *sess, pj_bool_t is_up) 
     1008{ 
     1009    pj_str_t *history; 
     1010 
     1011    PJ_ASSERT_RETURN(sess, PJ_FALSE); 
     1012 
     1013    history = get_prev_history(sess, is_up); 
     1014    if (history) { 
     1015        pj_str_t send_data; 
     1016        char str[PJ_CLI_MAX_CMDBUF]; 
     1017        send_data.ptr = &str[0]; 
     1018        send_data.slen = 0; 
     1019 
     1020        if (sess->rcmd->cur_pos > 0) { 
     1021            pj_memset(send_data.ptr, 0x08, sess->rcmd->cur_pos); 
     1022            send_data.slen = sess->rcmd->cur_pos; 
     1023        } 
     1024 
     1025        if (sess->rcmd->len > (unsigned)history->slen) { 
     1026            unsigned buf_len = sess->rcmd->len; 
     1027            pj_memset(&send_data.ptr[send_data.slen], 0x20, buf_len); 
     1028            send_data.slen += buf_len; 
     1029            pj_memset(&send_data.ptr[send_data.slen], 0x08, buf_len); 
     1030            send_data.slen += buf_len; 
     1031        }  
     1032        /* Send data */ 
     1033        pj_strcat(&send_data, history); 
     1034        telnet_sess_send(sess, &send_data); 
     1035        pj_ansi_strncpy((char*)&sess->rcmd->rbuf, history->ptr, history->slen); 
     1036        sess->rcmd->rbuf[history->slen] = 0; 
     1037        sess->rcmd->len = history->slen; 
     1038        sess->rcmd->cur_pos = sess->rcmd->len; 
     1039        return PJ_TRUE; 
     1040    } 
     1041    return PJ_FALSE; 
     1042} 
     1043 
     1044static pj_status_t process_vt100_cmd(cli_telnet_sess *sess,  
     1045                                     unsigned char *cmd) 
     1046{     
     1047    pj_status_t status = PJ_TRUE; 
     1048    switch (*cmd) { 
     1049        case TC_ESC: 
     1050            break; 
     1051        case TC_UP: 
     1052            status = handle_up_down(sess, PJ_TRUE); 
     1053            break; 
     1054        case TC_DOWN: 
     1055            status = handle_up_down(sess, PJ_FALSE); 
     1056            break; 
     1057        case TC_RIGHT: 
     1058            status = handle_right_key(sess); 
     1059            break; 
     1060        case TC_LEFT: 
     1061            status = handle_left_key(sess); 
     1062            break; 
     1063        case TC_END: 
     1064            break; 
     1065        case TC_HOME: 
     1066            break; 
     1067        case TC_CTRL_C: 
     1068            break; 
     1069        case TC_CR: 
     1070            break; 
     1071        case TC_BS: 
     1072            break; 
     1073        case TC_TAB: 
     1074            break; 
     1075        case TC_QM: 
     1076            break; 
     1077        case TC_BELL: 
     1078            break; 
     1079        case TC_DEL: 
     1080            break; 
     1081    }; 
     1082    return status; 
     1083} 
     1084 
    731085PJ_DEF(void) pj_cli_telnet_cfg_default(pj_cli_telnet_cfg *param) 
    741086{ 
     
    801092} 
    811093 
    82  
    831094/* Send a message to a telnet session */ 
    84 static pj_status_t telnet_sess_send(struct cli_telnet_sess *sess, 
     1095static pj_status_t telnet_sess_send(cli_telnet_sess *sess, 
    851096                                    const pj_str_t *str) 
    861097{ 
     
    1101121            pj_memmove(sess->buf + sess->buf_len, str->ptr, clen); 
    1111122        if (clen < sz) { 
    112             pj_ansi_snprintf(sess->buf + CLI_TELNET_BUF_SIZE, 
     1123            pj_ansi_snprintf((char *)sess->buf + CLI_TELNET_BUF_SIZE, 
    1131124                             MAX_CUT_MSG_LEN, CUT_MSG); 
    1141125            sess->buf_len = CLI_TELNET_BUF_SIZE + 
    115                             pj_ansi_strlen(sess->buf + CLI_TELNET_BUF_SIZE); 
     1126                            pj_ansi_strlen((char *)sess->buf+CLI_TELNET_BUF_SIZE); 
    1161127        } else 
    1171128            sess->buf_len += clen; 
     
    1261137} 
    1271138 
    128 static pj_status_t telnet_sess_send2(struct cli_telnet_sess *sess, 
    129                                      const char *str, int len) 
     1139static pj_status_t telnet_sess_send2(cli_telnet_sess *sess, 
     1140                                     const unsigned char *str, int len) 
    1301141{ 
    1311142    pj_str_t s; 
     
    1371148static void cli_telnet_sess_destroy(pj_cli_sess *sess) 
    1381149{ 
    139     struct cli_telnet_sess *tsess = (struct cli_telnet_sess *)sess; 
     1150    cli_telnet_sess *tsess = (cli_telnet_sess *)sess; 
    1401151    pj_mutex_t *mutex = ((struct cli_telnet_fe *)sess->fe)->mutex; 
    1411152 
     
    1611172    sess = tfe->sess_head.next; 
    1621173    while (sess != &tfe->sess_head) { 
    163         struct cli_telnet_sess *tsess = (struct cli_telnet_sess *)sess; 
    164  
    165         sess = sess->next; 
    166         if (tsess->base.log_level > level && tsess->authorized) 
    167             telnet_sess_send2(tsess, data, len); 
     1174        cli_telnet_sess *tsess = (cli_telnet_sess *)sess; 
     1175 
     1176        sess = sess->next;         
     1177        if (tsess->base.log_level > level) 
     1178            telnet_sess_send2(tsess, (unsigned char *)data, len); 
    1681179    } 
    1691180     
     
    2141225                                          pj_ssize_t sent) 
    2151226{ 
    216     struct cli_telnet_sess *sess = (struct cli_telnet_sess *) 
    217                                     pj_activesock_get_user_data(asock); 
     1227    cli_telnet_sess *sess = (cli_telnet_sess *) 
     1228                            pj_activesock_get_user_data(asock); 
    2181229 
    2191230    PJ_UNUSED_ARG(op_key); 
    2201231 
    2211232    if (sent <= 0) { 
    222         pj_cli_end_session(&sess->base); 
     1233        pj_cli_sess_end_session(&sess->base); 
    2231234        return PJ_FALSE; 
    2241235    } 
     
    2321243        if (telnet_sess_send2(sess, sess->buf, len) != PJ_SUCCESS) { 
    2331244            pj_mutex_unlock(sess->smutex); 
    234             pj_cli_end_session(&sess->base); 
     1245            pj_cli_sess_end_session(&sess->base); 
    2351246            return PJ_FALSE; 
    2361247        } 
     
    2481259                                          pj_size_t *remainder) 
    2491260{ 
    250     struct cli_telnet_sess *sess = (struct cli_telnet_sess *) 
    251                                     pj_activesock_get_user_data(asock); 
     1261    cli_telnet_sess *sess = (cli_telnet_sess *) 
     1262                            pj_activesock_get_user_data(asock); 
    2521263    struct cli_telnet_fe *tfe = (struct cli_telnet_fe *)sess->base.fe; 
    253     char *cdata = (char*)data; 
     1264    unsigned char *cdata = (unsigned char*)data;     
     1265    pj_status_t is_valid = PJ_TRUE; 
    2541266 
    2551267    PJ_UNUSED_ARG(size); 
     
    2571269 
    2581270    if (status != PJ_SUCCESS && status != PJ_EPENDING) { 
    259         pj_cli_end_session(&sess->base); 
     1271        pj_cli_sess_end_session(&sess->base); 
    2601272        return PJ_FALSE; 
    2611273    } 
     
    2661278    pj_mutex_lock(sess->smutex); 
    2671279 
    268     if (*cdata == 8) {                          // Backspace 
    269         if (sess->len > 0) 
    270             sess->len--; 
    271         if (telnet_sess_send2(sess, " \b", 2) != PJ_SUCCESS) 
    272             goto on_exit; 
    273     } else { 
    274         if (sess->len < PJ_CLI_MAX_CMDBUF - 1) 
    275             sess->rcmd[sess->len++] = *cdata; 
    276         if (*cdata == '\n') { 
    277             /* Trim trailing newlines */ 
    278             for (; sess->len > 0;) { 
    279                 if (sess->rcmd[sess->len - 1] == '\n' || 
    280                     sess->rcmd[sess->len - 1] == '\r') 
    281                     sess->rcmd[--sess->len] = 0; 
    282                 else 
    283                     break; 
    284             } 
    285             if (sess->len > 0 && sess->rcmd[sess->len - 1] != 0) 
    286                 sess->rcmd[sess->len++] = 0; 
    287  
    288             /* If a password is necessary, check whether the supplied password 
    289              * is correct. 
    290              */ 
    291             if (!sess->authorized &&  
    292                 ((struct cli_telnet_fe *)sess->base.fe)->cfg.passwd.slen > 0) 
    293             { 
    294                 if (pj_strcmp2(&tfe->cfg.passwd, sess->rcmd)) { 
    295                     pj_str_t str = pj_str("Wrong password!\r\n"); 
    296  
    297                     telnet_sess_send(sess, &str); 
    298                     goto on_exit; 
    299                 } else { 
    300                     /* Password is correct, send welcome message */ 
    301                     if (telnet_sess_send(sess, &tfe->cfg.welcome_msg) 
    302                         != PJ_SUCCESS) 
    303                     { 
    304                         goto on_exit; 
    305                     } 
    306                     sess->authorized = PJ_TRUE; 
    307                 } 
    308             } else { 
    309                 pj_status_t status; 
    310                  
    311                 pj_mutex_unlock(sess->smutex); 
    312                 status = pj_cli_exec(&sess->base, sess->rcmd, NULL); 
    313                 if (status == PJ_CLI_EEXIT) 
    314                     return PJ_FALSE; 
    315                 pj_mutex_lock(sess->smutex); 
    316             } 
    317             sess->len = 0; 
    318         } else if (!sess->authorized &&  
    319             ((struct cli_telnet_fe *)sess->base.fe)->cfg.passwd.slen > 0 && 
    320             *cdata != '\r') 
    321         { 
    322             if (telnet_sess_send2(sess, "\b ", 2) != PJ_SUCCESS) 
    323                 goto on_exit; 
    324         } 
    325  
     1280    switch (sess->parse_state) { 
     1281        case ST_CR: 
     1282            sess->parse_state = ST_NORMAL; 
     1283            if (*cdata == 0 || *cdata == '\n')                           
     1284                pj_mutex_unlock(sess->smutex); 
     1285                is_valid = handle_return(sess); 
     1286                if (!is_valid)               
     1287                    return PJ_FALSE; 
     1288                pj_mutex_lock(sess->smutex);     
     1289                break; 
     1290        case ST_NORMAL: 
     1291            if (*cdata == IAC) { 
     1292                sess->parse_state = ST_IAC; 
     1293            } else if (*cdata == '\b') { 
     1294                is_valid = handle_backspace(sess, cdata); 
     1295            } else if (*cdata == 27) {               
     1296                sess->parse_state = ST_ESC; 
     1297            } else {  
     1298                if (recv_buf_insert(sess->rcmd, cdata)) { 
     1299                    if (*cdata == '\r') { 
     1300                        sess->parse_state = ST_CR; 
     1301                    } else if ((*cdata == '\t') || (*cdata == '?')) { 
     1302                        is_valid = handle_tab(sess); 
     1303                    } else if (*cdata > 31 && *cdata < 127) { 
     1304                        is_valid = handle_alfa_num(sess, cdata); 
     1305                    } 
     1306                } else { 
     1307                    is_valid = PJ_FALSE; 
     1308                } 
     1309            } 
     1310            break; 
     1311        case ST_ESC: 
     1312            if (*cdata == 91) {          
     1313                sess->parse_state = ST_VT100; 
     1314            } else { 
     1315                sess->parse_state = ST_NORMAL; 
     1316            } 
     1317            break; 
     1318        case ST_VT100: 
     1319            sess->parse_state = ST_NORMAL; 
     1320            is_valid = process_vt100_cmd(sess, cdata); 
     1321            break; 
     1322        case ST_IAC: 
     1323            switch ((unsigned) *cdata) { 
     1324                case DO: 
     1325                    sess->parse_state = ST_DO; 
     1326                    break; 
     1327                case DONT: 
     1328                    sess->parse_state = ST_DONT; 
     1329                    break; 
     1330                case WILL: 
     1331                    sess->parse_state = ST_WILL; 
     1332                    break; 
     1333                case WONT: 
     1334                    sess->parse_state = ST_WONT; 
     1335                    break; 
     1336                default: 
     1337                    sess->parse_state = ST_NORMAL; 
     1338                    break; 
     1339            } 
     1340            break; 
     1341        case ST_DO: 
     1342            receive_do(sess, *cdata); 
     1343            sess->parse_state = ST_NORMAL; 
     1344            break; 
     1345        case ST_DONT: 
     1346            receive_dont(sess, *cdata); 
     1347            sess->parse_state = ST_NORMAL; 
     1348            break; 
     1349        case ST_WILL: 
     1350            receive_will(sess, *cdata); 
     1351            sess->parse_state = ST_NORMAL; 
     1352            break; 
     1353        case ST_WONT: 
     1354            receive_wont(sess, *cdata); 
     1355            sess->parse_state = ST_NORMAL; 
     1356            break; 
     1357        default: 
     1358            sess->parse_state = ST_NORMAL; 
     1359            break; 
     1360    } 
     1361    if (!is_valid) { 
     1362        send_bell(sess); 
    3261363    } 
    3271364 
     
    3291366 
    3301367    return PJ_TRUE; 
    331  
    332 on_exit: 
    333     pj_mutex_unlock(sess->smutex); 
    334     pj_cli_end_session(&sess->base); 
    335     return PJ_FALSE; 
    3361368} 
    3371369 
     
    3451377    pj_status_t sstatus; 
    3461378    pj_pool_t *pool; 
    347     struct cli_telnet_sess *sess; 
     1379    cli_telnet_sess *sess; 
    3481380    pj_activesock_cb asock_cb; 
    3491381 
     
    3641396    } 
    3651397 
    366     sess = PJ_POOL_ZALLOC_T(pool, struct cli_telnet_sess); 
     1398    sess = PJ_POOL_ZALLOC_T(pool, cli_telnet_sess); 
    3671399    sess->pool = pool; 
    3681400    sess->base.fe = &fe->base; 
     
    3731405    asock_cb.on_data_read = &telnet_sess_on_data_read; 
    3741406    asock_cb.on_data_sent = &telnet_sess_on_data_sent; 
     1407    sess->rcmd = PJ_POOL_ZALLOC_T(pool, telnet_recv_buf); 
     1408    sess->history = PJ_POOL_ZALLOC_T(pool, struct cmd_history); 
     1409    pj_list_init(sess->history); 
     1410    sess->active_history = sess->history; 
    3751411 
    3761412    sstatus = pj_mutex_create_recursive(pool, "mutex_telnet_sess", 
     
    3871423    } 
    3881424 
    389     /* Prompt for password if required, otherwise directly send 
    390      * a welcome message. 
    391      */ 
    392     if (fe->cfg.passwd.slen) { 
    393         pj_str_t pwd = pj_str("Password: "); 
    394         if (telnet_sess_send(sess, &pwd) != PJ_SUCCESS) 
    395             goto on_exit; 
    396     } else { 
    397         if (pj_strlen(&fe->cfg.welcome_msg)) { 
    398             if (telnet_sess_send(sess, &fe->cfg.welcome_msg) != PJ_SUCCESS) 
    399                 goto on_exit; 
    400         } else { 
    401             if (telnet_sess_send2(sess, " \b", 2) != PJ_SUCCESS) 
    402                 goto on_exit; 
    403         } 
    404         sess->authorized = PJ_TRUE; 
    405     } 
     1425    pj_memset(sess->telnet_option, 0, sizeof(sess->telnet_option)); 
     1426    set_local_option(sess, TRANSMIT_BINARY, PJ_TRUE); 
     1427    set_local_option(sess, STATUS, PJ_TRUE); 
     1428    set_local_option(sess, SUPPRESS_GA, PJ_TRUE); 
     1429    set_local_option(sess, TIMING_MARK, PJ_TRUE); 
     1430    set_local_option(sess, TERM_SPEED, PJ_TRUE); 
     1431    set_local_option(sess, TERM_TYPE, PJ_TRUE); 
     1432 
     1433    set_peer_option(sess, TRANSMIT_BINARY, PJ_TRUE); 
     1434    set_peer_option(sess, SUPPRESS_GA, PJ_TRUE); 
     1435    set_peer_option(sess, STATUS, PJ_TRUE); 
     1436    set_peer_option(sess, TIMING_MARK, PJ_TRUE); 
     1437    set_peer_option(sess, ECHO, PJ_TRUE);     
     1438 
     1439    send_cmd_do(sess, SUPPRESS_GA); 
     1440    send_cmd_will(sess, ECHO); 
     1441    send_cmd_will(sess, STATUS);    
     1442    send_cmd_will(sess, SUPPRESS_GA); 
     1443 
     1444    /* Send prompt string */ 
     1445    telnet_sess_send(sess, &fe->cfg.prompt_str); 
    4061446 
    4071447    /* Start reading for input from the new telnet session */ 
     
    4621502    fe->base.type = PJ_CLI_TELNET_FRONT_END; 
    4631503    fe->base.op->on_write_log = &cli_telnet_fe_write_log; 
    464 //    fe->base.op->on_quit = &cli_telnet_fe_quit; 
    4651504    fe->base.op->on_destroy = &cli_telnet_fe_destroy; 
    4661505    fe->pool = pool; 
     
    4971536        PJ_LOG(3, (THIS_FILE, "CLI telnet daemon listening at port %d", 
    4981537               fe->cfg.port)); 
     1538         
     1539        if (fe->cfg.prompt_str.slen == 0) { 
     1540            pj_str_t prompt_sign = {"> ", 2}; 
     1541            char *prompt_data = pj_pool_alloc(fe->pool,  
     1542                                              pj_gethostname()->slen+2); 
     1543            fe->cfg.prompt_str.ptr = prompt_data; 
     1544 
     1545            pj_strcpy(&fe->cfg.prompt_str, pj_gethostname()); 
     1546            pj_strcat(&fe->cfg.prompt_str, &prompt_sign); 
     1547        }        
    4991548    } else { 
    5001549        PJ_LOG(3, (THIS_FILE, "Failed binding the socket")); 
  • pjproject/branches/projects/cli/pjsip-apps/src/samples/clidemo.c

    r3232 r4299  
    3434 * Default: 1 
    3535 */ 
    36 #define USE_RANDOM_PORT 1 
     36#define USE_RANDOM_PORT 0 
    3737 
    3838struct cmd_xml_t { 
     
    8282} 
    8383 
    84 static pj_status_t quit(pj_cli_cmd_val *cval) 
     84static pj_status_t quit_app(pj_cli_cmd_val *cval) 
    8585{ 
    8686    PJ_UNUSED_ARG(cval); 
    87     pj_cli_end_session(cval->sess); 
     87    pj_cli_quit(cval->sess->fe->cli, cval->sess, PJ_FALSE); 
    8888 
    8989    return PJ_CLI_EEXIT; 
     
    9292static struct cmd_xml_t cmd_xmls[] = { 
    9393    {"<CMD name='sayhello' id='1' sc='  ,h , ,, sh  ,' desc='Will say hello'>" 
    94      "  <ARGS>" 
    9594     "    <ARG name='whom' type='text' desc='Whom to say hello to'/>" 
    96      "  </ARGS>" 
    9795     "</CMD>", 
    9896     &sayhello}, 
    9997    {"<CMD name='saybye' id='2' sc='b,sb' desc='Will say bye'>" 
    100      "  <ARGS>" 
    10198     "    <ARG name='whom' type='text' desc='Whom to say bye to'/>" 
    102      "  </ARGS>" 
    10399     "</CMD>", 
    104100     &saybye}, 
    105     {"<CMD name=' say ' id='3' sc='s' desc='Will say something'>" 
    106      "  <ARGS>" 
     101    {"<CMD name='saymsg' id='3' sc='s' desc='Will say something'>" 
    107102     "    <ARG name='msg' type='text' desc='Message to say'/>" 
    108103     "    <ARG name='whom' type='text' desc='Whom to say to'/>" 
    109      "  </ARGS>" 
    110104     "</CMD>", 
    111105     &say}, 
    112     {"<CMD name='quit' id='999' sc='q' desc='Quit the application'>" 
    113      "</CMD>", 
    114      &quit}, 
     106    {"<CMD name='vid' id='1' desc='Video Command'>" 
     107     "   <CMD name='help' id='2' desc='Show Help' />" 
     108     "   <CMD name='enable' id='3' desc='Enable Video' />" 
     109     "   <CMD name='disable' id='4' desc='Disable Video' />" 
     110     "   <CMD name='call' id='5' desc='Video call' >" 
     111     "            <CMD name='add' id='6' desc='Add Call' />" 
     112     "            <CMD name='cap' id='7' desc='Capture Call' >" 
     113     "               <ARG name='streamno' type='int' desc='Stream Number' id='1'/>" 
     114     "               <ARG name='devid' type='int' desc='Device Id' optional='1' id='2'/>" 
     115     "            </CMD>" 
     116     "   </CMD>"      
     117     "</CMD>", 
     118     NULL}, 
     119    {"<CMD name='quit_app' id='999' sc='qa' desc='Quit the application'>" 
     120     "</CMD>", 
     121     &quit_app}, 
    115122}; 
    116123 
     
    128135    pj_str_t xml; 
    129136    pj_status_t status; 
    130     int i; 
     137    int i;         
    131138 
    132139    pj_init(); 
     
    150157     */ 
    151158    for (i = 0; i < sizeof(cmd_xmls)/sizeof(cmd_xmls[0]); i++) { 
    152         xml = pj_str(cmd_xmls[i].xml); 
     159        xml = pj_str(cmd_xmls[i].xml);   
    153160        status = pj_cli_add_cmd_from_xml(cli, NULL, &xml,  
    154                                          cmd_xmls[i].handler, NULL); 
     161                                         cmd_xmls[i].handler, NULL, NULL); 
    155162        if (status != PJ_SUCCESS) 
    156163            goto on_return; 
     
    210217     */ 
    211218    for (;;) { 
    212         char cmdline[PJ_CLI_MAX_CMDBUF]; 
    213219        pj_status_t status; 
    214220 
    215         status = pj_cli_console_readline(sess, cmdline, sizeof(cmdline)); 
     221        status = pj_cli_console_process(sess); 
    216222        if (status != PJ_SUCCESS) 
    217223            break; 
    218224 
    219 //        pj_ansi_strcpy(cmdline, "sayhello {Teluu Inc.}"); 
    220         status = pj_cli_exec(sess, cmdline, NULL); 
     225        //pj_ansi_strcpy(cmdline, "sayhello {Teluu Inc.}");      
    221226        if (status == PJ_CLI_EEXIT) { 
    222227            /* exit is called */ 
  • pjproject/branches/projects/cli/pjsip-apps/src/samples/debug.c

    r3028 r4299  
    2929 *  #include "playfile.c" 
    3030 */ 
    31 #include "icedemo.c" 
     31#include "clidemo.c" 
    3232 
  • pjproject/branches/projects/cli/third_party

    • Property svn:externals
      •  

        old new  
        1 portaudio       -r1433 https://www.portaudio.com/repos/portaudio/trunk 
         1portaudio -r 1433 https://subversion.assembla.com/svn/portaudio/portaudio/trunk 
Note: See TracChangeset for help on using the changeset viewer.