openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

completion.c (14403B)


      1 /* 
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * File:        completion.c
      8  * Purpose:     API mode completion
      9  */
     10 
     11 #include "bcm/port.h"
     12 #include "sal/core/alloc.h"
     13 #include "sal/core/libc.h"
     14 #include "sal/appl/io.h"
     15 #include "shared/util.h"
     16 #include "appl/diag/shell.h"
     17 #include "appl/diag/system.h"
     18 #include "sal/appl/editline/editline.h"
     19 #include "sal/appl/sal.h"
     20 #include "shared/util.h"
     21 #include "context.h"
     22 #include "completion.h"
     23 #include "cint_porting.h"
     24 
     25 #include <cint_porting.h>
     26 #include <cint_internal.h>
     27 #include <cint_ast.h>
     28 #include <cint_eval_asts.h>
     29 #include <cint_eval_ast_print.h>
     30 
     31 /* forward references */
     32 
     33 STATIC char *no_complete(char *pathname, int *unique);
     34 STATIC int no_list(char *pathname, char ***avp);
     35 STATIC char *cmd_api_complete(char *pathname, int *unique);
     36 STATIC int cmd_api_list_possib(char *pathname, char ***avp);
     37 
     38 
     39 /* Area to save editline info */
     40 
     41 STATIC char *(*cmd_api_rl_complete_save)(char *, int *);
     42 STATIC int  (*cmd_api_rl_list_possib_save)(char *, char ***);
     43 
     44 typedef char *(*rl_complete_f)(char *, int *);
     45 typedef int (*rl_list_possib_f)(char *, char ***);
     46 
     47 typedef struct cmd_api_ctx_s {
     48     api_mode_completion_type_t ctype; /* completion type */
     49 } cmd_api_ctx_t;
     50 
     51 typedef struct rl_completion_handler_s {
     52     rl_complete_f       complete;
     53     rl_list_possib_f    list;
     54 } rl_completion_handler_t;
     55 
     56 STATIC rl_completion_handler_t rl_handler[] = {
     57     { no_complete,              no_list },
     58     { cmd_api_complete,         cmd_api_list_possib }
     59 };
     60 
     61 STATIC cmd_api_ctx_t cmd_api_context;
     62 
     63 #define MAX_FUNC_LEN CINT_CONFIG_MAX_VARIABLE_NAME
     64 
     65 typedef struct {
     66     char *alloc;        /* memory allocation */
     67     char *line;         /* input line to complete */
     68     char *match;        /* tokenized and reassembled input line */
     69     int ilen;           /* length of input line */
     70     int ispace;         /* input line space terminated */
     71     int mlen;           /* length of match */
     72     int *start;         /* completion stem start */
     73     int *end;           /* completion stem end */
     74     char *func;         /* func scratch buffer */
     75     api_mode_scanner_t scan;    /* tokens */
     76     api_mode_context_t ctx;     /* token context */
     77 } completion_data_t;
     78 
     79 STATIC int
     80 completion_func(api_mode_cint_dt_db_entry_t *entry)
     81 {
     82     return
     83         (entry->private != NULL) ||
     84         ((entry->dt.flags & CINT_DATATYPE_FLAGS_FUNC) != 0);
     85 }
     86 
     87 STATIC int
     88 completion_data_free(completion_data_t *cd)
     89 {
     90     if (cd->alloc != NULL) {
     91         sal_free(cd->alloc);
     92         cd->alloc = NULL;
     93     }
     94     api_mode_contextualizer_free(&cd->ctx);
     95     (void)api_mode_tokenizer_free(&cd->scan.tok);
     96 
     97     return 0;
     98 }
     99 
    100 STATIC int
    101 completion_data_init(completion_data_t *cd)
    102 {
    103     sal_memset(cd, 0, sizeof(*cd));
    104     cd->alloc = sal_alloc(MAX_FUNC_LEN, "api_mode");
    105 
    106     if (cd->alloc != NULL) {
    107         cd->func = cd->alloc;
    108     }
    109 
    110     return (cd->alloc == NULL) ? -1 : 0;
    111 }
    112 
    113 STATIC void
    114 strtr(char *s, int from, int to)
    115 {
    116     for (;*s != 0;s++) {
    117         if (*s == from) {
    118             *s = to;
    119         }
    120     }
    121 }
    122 
    123 STATIC int
    124 try_match(const char *s1, const char *s2, int start, int end)
    125 {
    126     int try;
    127 
    128     for (try = start; try < end; try++) {
    129         if (s1[try] != s2[try]) {
    130             /* char didn't match. back up and try the next name. */
    131             end = try;
    132             break;
    133         }
    134     }
    135 
    136     return end;
    137 }
    138 
    139 STATIC char *
    140 cmd_api_try_completion(completion_data_t *cd)
    141 {
    142     int start, end, idx, midx;
    143     const char *p;
    144     char *cpl;
    145 
    146     idx = cd->ctx.idx;
    147     cpl = NULL;
    148     sal_strncpy(cd->func, cd->ctx.db->entry[idx].name, MAX_FUNC_LEN-1);
    149     start = cd->mlen;
    150     end = sal_strlen(cd->func);
    151 
    152     midx = -1;
    153     for (;idx < cd->ctx.db->count; idx++) {
    154         if (!completion_func(cd->ctx.db->entry + idx)) {
    155             continue;
    156         }
    157         p = cd->ctx.db->entry[idx].name;
    158         /*
    159           If this is still a substring of what is being completed,
    160           then find a completion suffix. This will be the longest
    161           substring in common with all function names that have the
    162           string being completed as a prefix.
    163 
    164           The completion will not be longer than the first function
    165           name at the search point, so it is sufficient to base the
    166           completion buffer size string on that. The reason it won't
    167           be any longer is because any longer completion would
    168           necessarily have the current name as a substring.
    169            
    170         */
    171         if (strncmp(p, cd->match, cd->mlen)) {
    172             /*
    173              Input is not a substring of the current function at all,
    174              so a match is no longer possible.
    175             */
    176             break;
    177         }
    178 
    179         end = try_match(cd->func, p, start, end);
    180 
    181         if (end <= start) {
    182             /* nothing common, so nothing to complete */
    183             cpl = NULL;
    184             break;
    185         } else {
    186             /* found something that will complete. terminate and return
    187                the substring */
    188             cd->func[end] = 0;
    189             cpl = cd->func + start;
    190             midx = idx; /* save index of match */
    191         }
    192     }
    193 
    194     if (cd->line && cpl) {
    195         int trim = 1;
    196         /* if this completion fully matched something, then append a
    197            trailing space */
    198         if (cpl && midx >= 0) {
    199             /* if the function is fully the same as the function matched,
    200                and is not a substring of a subsequent match, ass a separator */
    201             if (!sal_strcmp(cd->func, cd->ctx.db->entry[midx].name) &&
    202                 strncmp(cd->func, cd->ctx.db->entry[midx+1].name,
    203                         sal_strlen(cd->func))) {
    204                     CINT_STRCAT(cd->func, " ");
    205                     trim = 0;
    206             }
    207         }
    208         
    209         /* Sorry, there *was* nothing to complete if the input line
    210            ended in whitespace and the completion was more
    211            whitespace. */
    212         if (trim && cd->ispace && cpl[0] == '_') {
    213             cpl = NULL;
    214         }
    215     }
    216 
    217     return cpl;
    218 }
    219 
    220 STATIC int
    221 near_match(completion_data_t *cd, char *line)
    222 {
    223     int err;
    224 
    225     err = api_mode_tokenizer(line, &cd->scan.tok);
    226     if (!err) {
    227         /* contextualizer may not match anything, but that's OK;
    228            it returns enough information to allow listing and
    229            completion to work. */
    230         (void)api_mode_contextualizer(&cd->scan.tok, &cd->ctx);
    231     }
    232 
    233     if (cd->ctx.dt0 == NULL || !completion_func(cd->ctx.dt0)) {
    234         /* no datatype found, or datatype was not a function */
    235         return -1;
    236     }
    237 
    238     cd->line = line;
    239     cd->ilen = sal_strlen(cd->line);
    240     cd->match = cd->ctx.match;
    241     cd->mlen = sal_strlen(cd->match);
    242     cd->start = NULL;
    243     cd->end = NULL;
    244     cd->ispace = (cd->ilen > 0) && (line[cd->ilen - 1] == ' ');
    245     return 0;
    246 }
    247 
    248 STATIC int
    249 over_match(completion_data_t *cd, char ***listp)
    250 {
    251     char **list;
    252     int len;
    253 
    254     len = 0;
    255     list = sal_alloc(sizeof(char *)*1, "api");
    256     if (list) {
    257         list[0] = sal_strdup("complete but not unique");
    258         len++;
    259         *listp = list;
    260     }
    261     return len;
    262 }
    263 
    264 
    265 
    266 STATIC int
    267 exact_match(completion_data_t *cd, char ***listp)
    268 {
    269     char **list;
    270     int len;
    271 
    272     len = 0;
    273     list = sal_alloc(sizeof(char *)*1, "api");
    274     if (list) {
    275         list[0] = sal_strdup("complete");
    276         len++;
    277         *listp = list;
    278     }
    279 
    280     return len;
    281 }
    282 
    283 #define MAXMATCH 100
    284 
    285 STATIC int
    286 list_collect(completion_data_t *cd, int start, char **list)
    287 {
    288     int idx, g_start, fnlen;
    289     int add_group, g_end;
    290     char *group;
    291     const char *fn;
    292     int count;
    293 
    294     group = NULL;
    295     g_start = -1;
    296     count = 0;
    297     for (idx = start; idx < cd->ctx.db->count; idx++) {
    298 
    299         /* only match functions */
    300         if (!completion_func(cd->ctx.db->entry + idx)) {
    301             continue;
    302         }
    303         fn = cd->ctx.db->entry[idx].name;
    304         
    305         if (strncmp(fn, cd->match, cd->mlen)) {
    306             /* no longer matching prefix */
    307             break;
    308         }
    309 
    310         fnlen = sal_strlen(fn);
    311         if (fnlen <= cd->mlen) {
    312             /* function is the same length or shorter than the input,
    313                to there's nothing additional that can match. */
    314             continue;
    315         }
    316 
    317         g_start = cd->mlen;
    318         /* if not at the end of a word */
    319         if (fn[g_start] != '_') {
    320             /* search backwards for beginning of word */
    321             for (; g_start > 0; g_start--) {
    322                 if (fn[g_start] == '_') {
    323                     g_start++;
    324                     break;
    325                 }
    326             }
    327         } else /* if (cd->ispace) */ {
    328             /* at a word break, so skip the break and start on the
    329                following word. */
    330             g_start++;
    331         }
    332         add_group = 0;
    333 
    334         /*
    335 
    336         Find the end of the word in the current function. Match
    337           against the current group if there is one to see if the word
    338           in this function needs to be added as a new group.
    339          */
    340         for (g_end = g_start; g_end < fnlen; g_end++) {
    341             if (fn[g_end] == 0 || fn[g_end] == '_') {
    342                 /* end-of-string or word boundary */
    343                 break;
    344             } else if (!add_group &&
    345                        (group == NULL ||
    346                         group[g_end - g_start] != fn[g_end])) {
    347                 add_group = 1;
    348             }
    349         }
    350 
    351         if (add_group) {
    352             sal_strncpy(cd->func, fn, MAX_FUNC_LEN-1);
    353             cd->func[g_end] = 0;
    354             group = sal_strdup(cd->func + g_start);
    355             list[count] = group;
    356             count++;
    357             if (count >= MAXMATCH) {
    358                 break;
    359             }
    360         }
    361     }
    362 
    363     return count;
    364 }
    365 
    366 
    367 /* Function names are composed of words separated by '_' characters.
    368    List are either lists of suffix words that are at the same level
    369    as the input being matched, or lists at the next level.
    370 
    371 */
    372 
    373 STATIC int
    374 list_matches(completion_data_t *cd, char ***listp)
    375 {
    376     int count;
    377     char **list;
    378 
    379     *cd->func = 0; /* terminate 'func' buffer */
    380     list = sal_alloc(sizeof(char *)*MAXMATCH, "api");
    381     if (list == NULL) {
    382         return 0;
    383     }
    384 
    385     count = list_collect(cd, cd->ctx.idx, list);
    386 
    387     *listp = list;
    388     return count;
    389 }
    390 
    391 /* Called from <TAB> */
    392 STATIC char *
    393 cmd_api_complete(char *pathname, int *unique)
    394 {
    395     rl_input_state_t state;
    396     char *cpl;
    397     const char *target;
    398     completion_data_t cd;
    399     int err;
    400 
    401     COMPILER_REFERENCE(pathname);
    402     rl_input_state(&state);
    403 
    404     if (completion_data_init(&cd) < 0) {
    405         return NULL;
    406     }
    407 
    408     cpl = NULL;
    409     err = near_match(&cd, (char *)state.line);
    410 
    411     if (err) {
    412         completion_data_free(&cd);
    413         return NULL;
    414     }
    415 
    416     if (!completion_func(cd.ctx.dt0)) {
    417         /* matched, but not a function */
    418         return NULL;
    419     }
    420 
    421     if (!cd.ctx.exact) {
    422         /* No match; determine if there is a completion possible */
    423         target = cd.ctx.dt0->name;
    424         if (!strncmp(target, cd.match, cd.mlen)) {
    425             cpl = cmd_api_try_completion(&cd);
    426             if (cpl) {
    427                 /* copy and convert to 'command line' format */
    428                 cpl = sal_strdup(cpl);
    429                 if (cpl) {
    430                     strtr(cpl, '_', ' ');
    431                 }
    432             }
    433         }
    434     } else {
    435         /* there is an exact match, so expand the rest of the entry,
    436            and add a trailing space. */
    437         char *rest = (char *)cd.ctx.dt0->name + cd.mlen;
    438         int rlen = sal_strlen(rest) + 1 + 1;
    439 
    440         cpl = sal_alloc(rlen, "apimode");
    441         sal_strncpy(cpl,rest,rlen-1);
    442         if (cd.line[cd.ilen - 1] != ' ') {
    443             CINT_STRCAT(cpl, " ");
    444         }
    445     }
    446     *unique = 0;
    447     completion_data_free(&cd);
    448     return cpl;
    449 }
    450 
    451 /* Called from ^[^[ */
    452 STATIC int
    453 cmd_api_list_possib(char *pathname, char ***avp)
    454 {
    455     rl_input_state_t state;
    456     char **list = NULL;
    457     completion_data_t cd;
    458     int llen, err;
    459 
    460     COMPILER_REFERENCE(pathname);
    461     rl_input_state(&state);
    462 
    463     if (completion_data_init(&cd) < 0) {
    464         return 0;
    465     }
    466 
    467     err = near_match(&cd, (char *)state.line);
    468     if (err) {
    469         completion_data_free(&cd);
    470         return 0;
    471     }
    472     if (!cd.ctx.exact) {
    473         /* inexact match */
    474         llen = list_matches(&cd, &list);
    475     } else if (cd.ctx.more) {
    476         llen = over_match(&cd, &list);
    477     } else {
    478         llen = exact_match(&cd, &list);
    479     }
    480     *avp = list;
    481     completion_data_free(&cd);
    482     return llen;
    483 }
    484 
    485 STATIC char *
    486 no_complete(char *pathname, int *unique)
    487 {
    488     COMPILER_REFERENCE(pathname);
    489     COMPILER_REFERENCE(unique);
    490     return NULL;
    491 }
    492 
    493 /* Called from ^[^[ */
    494 STATIC int
    495 no_list(char *pathname, char ***avp)
    496 {
    497     COMPILER_REFERENCE(pathname);
    498     COMPILER_REFERENCE(avp);
    499     return 0;
    500 }
    501 
    502 STATIC char *
    503 call_complete(char *pathname, int *unique)
    504 {
    505     return rl_handler[cmd_api_context.ctype].complete(pathname, unique);
    506 }
    507 
    508 /* Called from ^[^[ */
    509 STATIC int
    510 call_list_possib(char *pathname, char ***avp)
    511 {
    512     return rl_handler[cmd_api_context.ctype].list(pathname, avp);
    513 }
    514 
    515 int
    516 api_mode_completion_initialize(void)
    517 {
    518      /* Save readline state. This is called during initialization and
    519         after command processing (in case other commands use their own
    520         completion functions), so this function is very conservative
    521         and only saves an existing completion function if it is not a
    522         API mode function.  */
    523 
    524     if (rl_complete != call_complete) {
    525         /* Use our completion function */
    526         cmd_api_rl_complete_save = rl_complete;
    527         rl_complete = call_complete;
    528     }
    529 
    530     if (rl_list_possib != call_list_possib) {
    531         /* Use our listing function */
    532         cmd_api_rl_list_possib_save = rl_list_possib;
    533         rl_list_possib = call_list_possib;
    534     }
    535 
    536     api_mode_completion_set(api_mode_completion_api);
    537 
    538     return BCM_E_NONE;
    539 }
    540 
    541 int
    542 api_mode_completion_uninitialize(void)
    543 {
    544     /* Restore readline state */
    545     rl_complete = cmd_api_rl_complete_save;
    546     rl_list_possib = cmd_api_rl_list_possib_save;
    547 
    548     return BCM_E_NONE;
    549 }
    550 
    551 /* set completion type to that indicated, returning previous */
    552 int
    553 api_mode_completion_set(api_mode_completion_type_t ctype)
    554 {
    555     api_mode_completion_type_t current = cmd_api_context.ctype;
    556     cmd_api_context.ctype = ctype;
    557 
    558     return current;
    559 }
    560