Ignore:
Timestamp:
May 3, 2013 8:47:14 AM (11 years ago)
Author:
riza
Message:

Re #1643: - Modification to shortcut handling(execution&display).

  • Add exact match check to the parse input command process.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib-util/src/pjlib-util/cli.c

    r4440 r4513  
    3535#define CLI_CMD_EXIT        30001 
    3636 
     37#define MAX_CMD_HASH_NAME_LENGTH 64 
     38#define MAX_CMD_ID_LENGTH 16 
     39 
    3740#if 1 
    3841    /* Enable some tracing */ 
     
    127130    pj_cli_cmd_spec     root;           /* Root of command tree structure */ 
    128131    pj_cli_front_end    fe_head;        /* List of front-ends */ 
    129     pj_hash_table_t    *cmd_name_hash;  /* Command name hash table */ 
     132    pj_hash_table_t    *cmd_name_hash;  /* Command name hash table, this will  
     133                                           include the command name and shortcut  
     134                                           as hash key */ 
    130135    pj_hash_table_t    *cmd_id_hash;    /* Command id hash table */ 
    131136 
     
    485490} 
    486491 
    487 /* Check if command already added to command hash */ 
    488 static pj_bool_t cmd_name_exists(pj_cli_t *cli, pj_cli_cmd_spec *group,  
    489                                  pj_str_t *cmd) 
     492/* Get the command from the command hash */ 
     493static pj_cli_cmd_spec *get_cmd_name(const pj_cli_t *cli,  
     494                                     const pj_cli_cmd_spec *group,  
     495                                     const pj_str_t *cmd) 
    490496{ 
    491497    pj_str_t cmd_val; 
    492     char cmd_ptr[64]; 
    493  
    494     cmd_val.ptr = &cmd_ptr[0]; 
     498    char cmd_ptr[MAX_CMD_HASH_NAME_LENGTH]; 
     499 
     500    cmd_val.ptr = cmd_ptr; 
    495501    cmd_val.slen = 0; 
    496502 
    497503    if (group) { 
    498         char cmd_str[16]; 
    499         pj_ansi_sprintf(&cmd_str[0], "%d", group->id); 
    500         pj_strcat2(&cmd_val, &cmd_str[0]);       
     504        char cmd_str[MAX_CMD_ID_LENGTH]; 
     505        pj_ansi_sprintf(cmd_str, "%d", group->id); 
     506        pj_strcat2(&cmd_val, cmd_str);   
    501507    } 
    502508    pj_strcat(&cmd_val, cmd); 
    503     return (pj_hash_get(cli->cmd_name_hash,  
    504                         cmd_val.ptr, cmd_val.slen, NULL) != 0); 
     509    return (pj_cli_cmd_spec *)pj_hash_get(cli->cmd_name_hash, cmd_val.ptr,  
     510                                          cmd_val.slen, NULL); 
    505511} 
    506512 
     
    511517    pj_str_t cmd_val; 
    512518    pj_str_t add_cmd; 
    513     char cmd_ptr[64]; 
    514  
    515     cmd_val.ptr = &cmd_ptr[0]; 
     519    char cmd_ptr[MAX_CMD_HASH_NAME_LENGTH]; 
     520 
     521    cmd_val.ptr = cmd_ptr; 
    516522    cmd_val.slen = 0; 
    517523 
    518524    if (group) { 
    519         pj_strcat(&cmd_val, &group->name); 
     525        char cmd_str[MAX_CMD_ID_LENGTH]; 
     526        pj_ansi_sprintf(cmd_str, "%d", group->id); 
     527        pj_strcat2(&cmd_val, cmd_str);   
    520528    } 
    521529    pj_strcat(&cmd_val, cmd_name); 
    522530    pj_strdup(cli->pool, &add_cmd, &cmd_val); 
    523531     
    524     pj_hash_set(cli->pool, cli->cmd_name_hash,  
    525                 cmd_val.ptr, cmd_val.slen, 0, cmd); 
     532    pj_hash_set(cli->pool, cli->cmd_name_hash, cmd_val.ptr,  
     533                cmd_val.slen, 0, cmd); 
    526534} 
    527535 
     
    665673            pj_strltrim(&attr->value); 
    666674            if (!attr->value.slen ||  
    667                 cmd_name_exists(cli, group, &attr->value))                 
     675                (get_cmd_name(cli, group, &attr->value)))                 
    668676            { 
    669677                return PJ_CLI_EBADNAME; 
     
    703711                    } 
    704712                    /* Check whether the shortcuts are already used */ 
    705                     if (cmd_name_exists(cli, group, &str)) { 
     713                    if (get_cmd_name(cli, &cli->root, &str)) { 
    706714                        PJ_THROW(PJ_CLI_EBADNAME); 
    707715                    } 
     
    773781                                             sizeof(pj_str_t)); 
    774782        for (i = 0; i < cmd->sc_cnt; i++) { 
    775             pj_strdup(cli->pool, &cmd->sc[i], &sc[i]); 
    776             add_cmd_name(cli, group, cmd, &sc[i]); 
     783            pj_strdup(cli->pool, &cmd->sc[i], &sc[i]);   
     784            /** Add shortcut to root command **/ 
     785            add_cmd_name(cli, &cli->root, cmd, &sc[i]); 
    777786        } 
    778787    } 
     
    881890                info->err_pos = scanner.curptr - scanner.begin; 
    882891                if (*scanner.curptr == '\'' || *scanner.curptr == '"' || 
    883                     *scanner.curptr == '[' || *scanner.curptr == '{') 
     892                    *scanner.curptr == '{') 
    884893                { 
    885                     pj_scan_get_quotes(&scanner, "'\"[{", "'\"]}", 4, &str); 
     894                    pj_scan_get_quotes(&scanner, "'\"{", "'\"}", 3, &str); 
    886895                    /* Remove the quotes */ 
    887896                    str.ptr++; 
     
    983992} 
    984993 
     994static pj_bool_t hint_inserted(const pj_str_t *name,  
     995                               const pj_str_t *desc,  
     996                               const pj_str_t *type,  
     997                               pj_cli_exec_info *info) 
     998{ 
     999    unsigned i; 
     1000    for(i=0; i<info->hint_cnt; ++i) { 
     1001        pj_cli_hint_info *hint = &info->hint[i]; 
     1002        if ((!pj_strncmp(&hint->name, name, hint->name.slen)) && 
     1003            (!pj_strncmp(&hint->desc, desc, hint->desc.slen)) && 
     1004            (!pj_strncmp(&hint->type, type, hint->type.slen))) 
     1005        { 
     1006            return PJ_TRUE; 
     1007        } 
     1008    } 
     1009    return PJ_FALSE; 
     1010} 
     1011 
     1012/** This will insert new hint with the option to check for the same  
     1013    previous entry **/ 
     1014static pj_status_t insert_new_hint2(pj_pool_t *pool,  
     1015                                    pj_bool_t unique_insert, 
     1016                                    const pj_str_t *name,  
     1017                                    const pj_str_t *desc,  
     1018                                    const pj_str_t *type,  
     1019                                    pj_cli_exec_info *info) 
     1020{ 
     1021    pj_cli_hint_info *hint; 
     1022    PJ_ASSERT_RETURN(pool && info, PJ_EINVAL); 
     1023    PJ_ASSERT_RETURN((info->hint_cnt < PJ_CLI_MAX_HINTS), PJ_EINVAL); 
     1024 
     1025    if ((unique_insert) && (hint_inserted(name, desc, type, info))) 
     1026        return PJ_SUCCESS; 
     1027 
     1028    hint = &info->hint[info->hint_cnt]; 
     1029 
     1030    pj_strdup(pool, &hint->name, name); 
     1031 
     1032    if (desc && (desc->slen > 0))  { 
     1033        pj_strdup(pool, &hint->desc, desc); 
     1034    } else { 
     1035        hint->desc.slen = 0; 
     1036    } 
     1037     
     1038    if (type && (type->slen > 0)) { 
     1039        pj_strdup(pool, &hint->type, type); 
     1040    } else { 
     1041        hint->type.slen = 0; 
     1042    } 
     1043 
     1044    ++info->hint_cnt;     
     1045    return PJ_SUCCESS; 
     1046} 
     1047 
     1048/** This will insert new hint without checking for the same previous entry **/ 
    9851049static pj_status_t insert_new_hint(pj_pool_t *pool,  
    9861050                                   const pj_str_t *name,  
     
    9891053                                   pj_cli_exec_info *info) 
    9901054{         
    991     pj_cli_hint_info *hint; 
    992     PJ_ASSERT_RETURN(pool && info, PJ_EINVAL); 
    993     PJ_ASSERT_RETURN((info->hint_cnt < PJ_CLI_MAX_HINTS), PJ_EINVAL); 
    994     hint = &info->hint[info->hint_cnt]; 
    995  
    996     pj_strdup(pool, &hint->name, name); 
    997  
    998     if (desc && (desc->slen > 0))  { 
    999         pj_strdup(pool, &hint->desc, desc); 
    1000     } else { 
    1001         hint->desc.slen = 0; 
    1002     } 
    1003      
    1004     if (type && (type->slen > 0)) { 
    1005         pj_strdup(pool, &hint->type, type); 
    1006     } else { 
    1007         hint->type.slen = 0; 
    1008     } 
    1009  
    1010     ++info->hint_cnt;     
     1055    return insert_new_hint2(pool, PJ_FALSE, name, desc, type, info); 
     1056} 
     1057 
     1058/** This will get a complete/exact match of a command from the cmd hash **/ 
     1059static pj_status_t get_comp_match_cmds(const pj_cli_t *cli,  
     1060                                       const pj_cli_cmd_spec *group, 
     1061                                       const pj_str_t *cmd_val,  
     1062                                       pj_pool_t *pool,  
     1063                                       pj_cli_cmd_spec **p_cmd, 
     1064                                       pj_cli_exec_info *info) 
     1065{ 
     1066    pj_cli_cmd_spec *cmd;     
     1067    PJ_ASSERT_RETURN(cli && group && cmd_val && pool && info, PJ_EINVAL);    
     1068 
     1069    cmd = get_cmd_name(cli, group, cmd_val); 
     1070 
     1071    if (cmd) { 
     1072        pj_status_t status;     
     1073        status = insert_new_hint(pool, cmd_val, &cmd->desc, NULL, info); 
     1074 
     1075        if (status != PJ_SUCCESS) 
     1076            return status; 
     1077 
     1078        *p_cmd = cmd; 
     1079    } 
     1080 
    10111081    return PJ_SUCCESS; 
    10121082} 
    10131083 
    1014 static pj_status_t get_match_cmds(pj_cli_cmd_spec *cmd,  
    1015                                   const pj_str_t *cmd_val,                                     
    1016                                   pj_pool_t *pool,  
    1017                                   pj_cli_cmd_spec **p_cmd,                              
    1018                                   pj_cli_exec_info *info) 
    1019 { 
    1020     pj_status_t status = PJ_SUCCESS; 
    1021     PJ_ASSERT_RETURN(cmd && pool && info && cmd_val, PJ_EINVAL);     
     1084/** This method will search for any shortcut with pattern match to the input 
     1085    command. This method should be called from root command, as shortcut could 
     1086    only be executed from root **/ 
     1087static pj_status_t get_pattern_match_shortcut(const pj_cli_t *cli, 
     1088                                              const pj_str_t *cmd_val, 
     1089                                              pj_pool_t *pool,  
     1090                                              pj_cli_cmd_spec **p_cmd, 
     1091                                              pj_cli_exec_info *info) 
     1092{ 
     1093    pj_hash_iterator_t it_buf, *it; 
     1094    pj_status_t status; 
     1095    PJ_ASSERT_RETURN(cli && pool && cmd_val && info, PJ_EINVAL); 
     1096   
     1097    it = pj_hash_first(cli->cmd_name_hash, &it_buf); 
     1098    while (it) { 
     1099        unsigned i; 
     1100        pj_cli_cmd_spec *cmd = (pj_cli_cmd_spec *) 
     1101                               pj_hash_this(cli->cmd_name_hash, it); 
     1102 
     1103        PJ_ASSERT_RETURN(cmd, PJ_EINVAL);            
     1104         
     1105        for (i=0; i < cmd->sc_cnt; ++i) { 
     1106            static const pj_str_t SHORTCUT = {"SC", 2}; 
     1107            pj_str_t *sc = &cmd->sc[i]; 
     1108            PJ_ASSERT_RETURN(sc, PJ_EINVAL); 
     1109 
     1110            if (!pj_strncmp(sc, cmd_val, cmd_val->slen)) { 
     1111                /** Unique hints needed because cmd hash contain command name 
     1112                    and shortcut referencing to the same command **/ 
     1113                status = insert_new_hint2(pool, PJ_TRUE, sc, &cmd->desc,  
     1114                                          &SHORTCUT, info); 
     1115                if (status != PJ_SUCCESS) 
     1116                    return status; 
     1117 
     1118                if (p_cmd) 
     1119                    *p_cmd = cmd; 
     1120            } 
     1121        } 
     1122         
     1123        it = pj_hash_next(cli->cmd_name_hash, it); 
     1124    } 
     1125 
     1126    return PJ_SUCCESS; 
     1127} 
     1128 
     1129/** This method will search a pattern match to the input command from the child 
     1130    command list of the current/active command. **/ 
     1131static pj_status_t get_pattern_match_cmds(pj_cli_cmd_spec *cmd,  
     1132                                          const pj_str_t *cmd_val,                                     
     1133                                          pj_pool_t *pool,  
     1134                                          pj_cli_cmd_spec **p_cmd,  
     1135                                          pj_cli_parse_mode parse_mode, 
     1136                                          pj_cli_exec_info *info) 
     1137{ 
     1138    pj_status_t status; 
     1139    PJ_ASSERT_RETURN(cmd && pool && info && cmd_val, PJ_EINVAL);    
    10221140 
    10231141    if (p_cmd) 
     
    10281146        pj_cli_cmd_spec *child_cmd = cmd->sub_cmd->next; 
    10291147        while (child_cmd != cmd->sub_cmd) { 
    1030             unsigned i;     
    10311148            pj_bool_t found = PJ_FALSE; 
    10321149            if (!pj_strncmp(&child_cmd->name, cmd_val, cmd_val->slen)) {                 
     
    10381155                found = PJ_TRUE; 
    10391156            } 
    1040             for (i=0; i < child_cmd->sc_cnt; ++i) { 
    1041                 static const pj_str_t SHORTCUT = {"SC", 2}; 
    1042                 pj_str_t *sc = &child_cmd->sc[i]; 
    1043                 PJ_ASSERT_RETURN(sc, PJ_EINVAL); 
    1044  
    1045                 if (!pj_strncmp(sc, cmd_val, cmd_val->slen)) {           
    1046                     status = insert_new_hint(pool, sc, &child_cmd->desc,  
    1047                                              &SHORTCUT, info); 
    1048                     if (status != PJ_SUCCESS) 
    1049                         return status; 
    1050  
    1051                     found = PJ_TRUE; 
    1052                 }                
    1053             } 
    1054             if (found && p_cmd)  
    1055                 *p_cmd = child_cmd;                          
    1056  
     1157            if (found) { 
     1158                if (parse_mode == PARSE_NEXT_AVAIL) { 
     1159                    /** Only insert shortcut on next available commands mode **/ 
     1160                    unsigned i; 
     1161                    for (i=0; i < child_cmd->sc_cnt; ++i) { 
     1162                        static const pj_str_t SHORTCUT = {"SC", 2}; 
     1163                        pj_str_t *sc = &child_cmd->sc[i]; 
     1164                        PJ_ASSERT_RETURN(sc, PJ_EINVAL); 
     1165 
     1166                        status = insert_new_hint(pool, sc,  
     1167                                                 &child_cmd->desc, &SHORTCUT,  
     1168                                                 info); 
     1169                        if (status != PJ_SUCCESS) 
     1170                            return status; 
     1171                    } 
     1172                } 
     1173 
     1174                if (p_cmd) 
     1175                    *p_cmd = child_cmd;                      
     1176            }    
    10571177            child_cmd = child_cmd->next; 
    10581178        } 
    10591179    } 
    1060     return status; 
    1061 } 
    1062  
     1180    return PJ_SUCCESS; 
     1181} 
     1182 
     1183/** This will match the arguments passed to the command with the argument list 
     1184    of the specified command list. **/ 
    10631185static pj_status_t get_match_args(pj_cli_sess *sess, 
    10641186                                  pj_cli_cmd_spec *cmd,  
     
    10891211            if ((parse_mode == PARSE_EXEC) && (!arg->validate)) { 
    10901212                /* If no validation needed, then insert the values */ 
    1091                 status = insert_new_hint(pool,  
    1092                                          cmd_val,  
    1093                                          NULL,  
    1094                                          NULL, 
    1095                                          info);          
     1213                status = insert_new_hint(pool, cmd_val, NULL, NULL, info);               
    10961214                return status; 
    10971215            } 
     
    11651283} 
    11661284 
     1285/** This will check for a match of the commands/arguments input. Any match  
     1286    will be inserted to the hint data. **/ 
    11671287static pj_status_t get_available_cmds(pj_cli_sess *sess, 
    11681288                                      pj_cli_cmd_spec *cmd,  
     
    11831303    info->hint_cnt = 0;     
    11841304 
    1185     if (get_cmd) 
    1186         status = get_match_cmds(cmd, prefix, pool, p_cmd, info); 
     1305    if (get_cmd) { 
     1306        status = get_comp_match_cmds(sess->fe->cli, cmd, prefix, pool, p_cmd,  
     1307                                     info); 
     1308        if (status != PJ_SUCCESS) 
     1309            return status; 
     1310         
     1311        /** If exact match found, then no need to search for pattern match **/ 
     1312        if (info->hint_cnt == 0) { 
     1313            if ((parse_mode != PARSE_NEXT_AVAIL) &&  
     1314                (cmd == &sess->fe->cli->root))  
     1315            { 
     1316                /** Pattern match for shortcut needed on root command only **/ 
     1317                status = get_pattern_match_shortcut(sess->fe->cli, prefix, pool, 
     1318                                                    p_cmd, info); 
     1319 
     1320                if (status != PJ_SUCCESS) 
     1321                    return status; 
     1322            } 
     1323 
     1324            status = get_pattern_match_cmds(cmd, prefix, pool, p_cmd,  
     1325                                            parse_mode, info); 
     1326        } 
     1327 
     1328        if (status != PJ_SUCCESS) 
     1329            return status; 
     1330    } 
     1331 
    11871332    if (argc > 0) 
    11881333        status = get_match_args(sess, cmd, prefix, argc,  
     
    11911336    if (status == PJ_SUCCESS) {  
    11921337        if (prefix->slen > 0) { 
     1338            /** If a command entered is not a an empty command, and have a  
     1339                single match in the command list then it is a valid command **/ 
    11931340            if (info->hint_cnt == 0) { 
    11941341                status = PJ_CLI_EINVARG; 
Note: See TracChangeset for help on using the changeset viewer.