autocomplete.c (10695B)
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 * Broadcom autocomplete 8 */ 9 10 11 #ifdef INCLUDE_AUTOCOMPLETE 12 13 #include <sal/appl/editline/autocomplete.h> 14 #include <sal/appl/sal.h> 15 #include <sal/core/libc.h> 16 #include <sal/appl/io.h> 17 #include <sal/core/alloc.h> 18 #include <soc/drv.h> 19 #include <sys/ioctl.h> 20 21 char autocomplete_string[SOC_MAX_NUM_DEVICES][AUTOCOMPLETE_MAX_STRING_LEN]; 22 23 static autocomplete_node_t autocomplete_root[SOC_MAX_NUM_DEVICES]; 24 25 autocomplete_node_t *autocomplete_find_root(int unit, char *keyword) { 26 27 autocomplete_node_t *node = autocomplete_root[unit].first_child; 28 29 while (node != NULL) { 30 if (sal_strcasecmp(node->keyword, keyword) == 0) { 31 return node; 32 } 33 node = node->next_sibling; 34 } 35 36 return NULL; 37 } 38 39 40 autocomplete_node_t *autocomplete_find_node(int unit, char **keywords) { 41 autocomplete_node_t *node = autocomplete_root[unit].first_child; 42 int i = 0; 43 44 while (keywords[i] != NULL) { 45 while (node != NULL) { 46 if (sal_strcasecmp(node->keyword, keywords[i]) == 0) { 47 if(keywords[i+1] == NULL) { 48 return node; 49 } 50 node = node->first_child; 51 break; 52 } 53 node = node->next_sibling; 54 } 55 if (node == NULL) return NULL; 56 i++; 57 } 58 59 return NULL; 60 } 61 62 /* for description see the header include/sal/appl/editline/autocomplete.h */ 63 64 autocomplete_node_t *autocomplete_node_add(int unit, autocomplete_node_t *parent, char *keyword, char is_option) { 65 66 autocomplete_node_t *node = (autocomplete_node_t*) sal_alloc(sizeof(*node), "autocomplete node"); 67 68 node->keyword = sal_strdup(keyword); 69 node->first_child = NULL; 70 node->next_sibling = NULL; 71 node->parent = parent; 72 node->is_option = is_option; 73 74 /* check if parent is root */ 75 if (parent == NULL) { 76 parent = &autocomplete_root[unit]; 77 node->parent = parent; 78 } 79 80 /* check if it should be the first child of parent */ 81 if (parent->first_child == NULL) { 82 parent->first_child = node; 83 } else { 84 autocomplete_node_t* last_child = parent->first_child; 85 while(last_child->next_sibling != NULL) { 86 last_child = last_child->next_sibling; 87 } 88 last_child->next_sibling = node; 89 } 90 91 return node; 92 } 93 94 /* for description see the header include/sal/appl/editline/autocomplete.h */ 95 96 void autocomplete_delete_all(int unit) { 97 98 autocomplete_node_t *node = autocomplete_root[unit].first_child; 99 autocomplete_node_t *next_node; 100 while (node != NULL) { 101 next_node = node->next_sibling; 102 autocomplete_node_delete(unit, node); 103 node = next_node; 104 } 105 autocomplete_root[unit].first_child = NULL; 106 107 } 108 109 /* for description see the header include/sal/appl/editline/autocomplete.h */ 110 111 void autocomplete_node_delete(int unit, autocomplete_node_t* node) { 112 113 autocomplete_node_t *parent; 114 autocomplete_node_t *child; 115 116 if (node == NULL) 117 return; 118 119 parent = node->parent; 120 child = node->first_child; 121 122 /* check if the node is the first child of it's parent */ 123 if (parent->first_child == node) { 124 parent->first_child = node->next_sibling; 125 } else { 126 autocomplete_node_t* older_sibling = parent->first_child; 127 while(older_sibling->next_sibling != node) { 128 older_sibling = older_sibling->next_sibling; 129 } 130 older_sibling->next_sibling = node->next_sibling; 131 } 132 133 /* delete all of the node's children */ 134 while (child != NULL) { 135 autocomplete_node_t* next_child = child->next_sibling; 136 autocomplete_node_delete(unit, child); 137 child = next_child; 138 } 139 140 /* delete the node */ 141 sal_free(node->keyword); 142 sal_free(node); 143 144 return; 145 } 146 147 /* get a prefix as a parameter and a new word, calcuate the largest common prefix and save it in prefix */ 148 149 static void update_common_prefix(int index, char *prefix, char *new_word) { 150 /* if the new_word is the first word */ 151 if (index == 0) { 152 sal_strncpy(prefix, new_word, AUTOCOMPLETE_MAX_STRING_LEN-1); 153 } else { 154 int i=0; 155 while (prefix[i] == new_word[i]) { 156 i++; 157 } 158 /* truncate the prefix */ 159 sal_memset(prefix+i, 0, sal_strlen(prefix+i)); 160 } 161 } 162 163 164 static void uncheck_options (autocomplete_node_t *options_node) { 165 while(options_node != NULL) { 166 options_node->is_checked = 0; 167 options_node = options_node->next_sibling; 168 } 169 return; 170 } 171 172 /* for description see the header include/sal/appl/editline/autocomplete.h */ 173 174 char* autocomplete_print(int unit, char* input, char* prompt) { 175 autocomplete_node_t *prev_node = NULL; 176 autocomplete_node_t *options_node = NULL; 177 autocomplete_node_t *node = NULL; 178 char new_line[AUTOCOMPLETE_MAX_STRING_LEN] = {0}; 179 char str_to_print[AUTOCOMPLETE_MAX_STRING_LEN] = {0}; 180 char str_tmp[AUTOCOMPLETE_MAX_STRING_LEN] = {0}; 181 char common_prefix[AUTOCOMPLETE_MAX_STRING_LEN] = {0}; 182 char *prefix; 183 const char s[2] = " "; 184 char *token; 185 int token_idx=0; 186 int node_depth=0; 187 int num_of_suggestions = 0; 188 char str_cpy[AUTOCOMPLETE_MAX_STRING_LEN] = {0}; 189 int max_autocomplete_str_len = 0; 190 int accumulative_line_len = 0; 191 struct winsize window_size; 192 int window_len = 0; 193 char is_last_token = 0; 194 char *last_token; 195 char is_option = 0; 196 char print_options = 0; 197 198 if (unit == -1) { 199 sal_printf("\nError: autocomplete failed. No attached unit found.\n"); 200 sal_printf("%s", prompt); 201 return ""; 202 } 203 204 node = prev_node = autocomplete_root[unit].first_child; 205 206 if (node == NULL) { 207 return ""; 208 } 209 210 /* autocomplete only in BCM shell */ 211 if (sal_strstr(prompt, "BCM") == NULL) { 212 return ""; 213 } 214 215 sal_strncpy(str_cpy, input, AUTOCOMPLETE_MAX_STRING_LEN-1); 216 217 /* get the first token */ 218 token = strtok(str_cpy, s); 219 220 /* walk through other tokens */ 221 while( token != NULL ) 222 { 223 char* assignment = NULL; 224 token_idx++; 225 prev_node = node; 226 is_option = 0; 227 if (node != NULL && node->is_option) { 228 print_options = 1; 229 options_node = node; 230 } 231 while(node != NULL && node->keyword != NULL) { 232 if (sal_strcasecmp(node->keyword, token) == 0) { 233 234 if(node->is_option) { 235 node->is_checked = 1; 236 } 237 238 if (print_options && node->first_child == NULL) { 239 node = options_node; 240 } else { 241 node = node->first_child; 242 } 243 244 node_depth++; 245 break; 246 } 247 assignment = strchr(token, '='); 248 if (assignment != NULL) { 249 if (sal_strncasecmp(node->keyword, token, assignment - token + 1) == 0) { 250 is_option = 1; 251 252 node->is_checked = 1; 253 254 if (node->first_child != NULL) { 255 node = node->first_child; 256 } else { 257 node = options_node; 258 } 259 260 node_depth++; 261 break; 262 } 263 } 264 node = node->next_sibling; 265 } 266 if (token_idx != node_depth) break; 267 if(is_option) { 268 last_token = token; 269 token = strtok(NULL, s); 270 /* if last token */ 271 if (token == NULL && node != options_node) { 272 sal_strncat(new_line, last_token , assignment - last_token + 1); 273 token = assignment + 1; 274 is_last_token = 1; 275 } else { 276 sal_strncat(new_line, last_token, AUTOCOMPLETE_MAX_STRING_LEN - sal_strlen(new_line) - 1); 277 node = options_node; 278 } 279 continue; 280 } else { 281 sal_strncat(new_line,token, AUTOCOMPLETE_MAX_STRING_LEN - sal_strlen(new_line) - 1); 282 } 283 284 if(token[strlen(token)-1] != '=') { 285 sal_strncat(new_line," ", AUTOCOMPLETE_MAX_STRING_LEN - sal_strlen(new_line) -1); 286 } 287 288 if (is_last_token) { 289 token = NULL; 290 } else { 291 token = strtok(NULL, s); 292 } 293 } 294 295 /* no suggestions */ 296 if (token_idx != node_depth && strtok(NULL, s)) { 297 uncheck_options(options_node); 298 return ""; 299 } 300 /* show all options */ 301 if (token_idx == node_depth) { 302 prefix = ""; 303 } else { /* complete by the prefix */ 304 node = prev_node; 305 prefix = token; 306 } 307 308 /* walk through the suggestions */ 309 while(node != NULL && node->keyword != NULL) { 310 if (sal_strncasecmp(node->keyword, prefix, sal_strlen(prefix)) == 0 && !node->is_checked) { 311 update_common_prefix(num_of_suggestions, common_prefix, node->keyword); 312 sal_sprintf(str_tmp, "%s ", node->keyword); 313 if (max_autocomplete_str_len < sal_strlen(str_tmp)) { 314 max_autocomplete_str_len = sal_strlen(str_tmp); 315 } 316 sal_strncat(str_to_print, str_tmp, AUTOCOMPLETE_MAX_STRING_LEN - sal_strlen(str_to_print)-1); 317 sal_strncat(new_line,str_tmp, AUTOCOMPLETE_MAX_STRING_LEN - sal_strlen(new_line)-1); 318 num_of_suggestions++; 319 } 320 node = node->next_sibling; 321 } 322 uncheck_options(options_node); 323 324 sal_strcpy(autocomplete_string[unit],""); 325 326 if (num_of_suggestions > 0 && token_idx == node_depth && 327 sal_strlen(input) > 0 && input[sal_strlen(input)-1] != ' ' && 328 input[sal_strlen(input)-1] != '=') { 329 sal_strncat(autocomplete_string[unit]," ", AUTOCOMPLETE_MAX_STRING_LEN - sal_strlen(autocomplete_string[unit]) - 1); 330 } 331 332 if (num_of_suggestions > 1) { 333 ioctl(0, TIOCGWINSZ, &window_size); 334 window_len = window_size.ws_col; 335 sal_printf("\n"); 336 token = strtok(str_to_print, s); 337 while( token != NULL ) 338 { 339 accumulative_line_len += max_autocomplete_str_len; 340 if (accumulative_line_len >= window_len) { 341 sal_printf("\n"); 342 accumulative_line_len = max_autocomplete_str_len; 343 } 344 sal_printf("%*s", -max_autocomplete_str_len, token); 345 token = strtok(NULL, s); 346 } 347 sal_printf("\n"); 348 sal_printf("%s%s", prompt, input); 349 } 350 sal_strncat(autocomplete_string[unit], common_prefix + sal_strlen(prefix), AUTOCOMPLETE_MAX_STRING_LEN - sal_strlen(autocomplete_string[unit]) - 1); 351 352 return autocomplete_string[unit]; 353 } 354 355 #else /* INCLUDE_AUTOCOMPLETE */ 356 int _autocomplete_autocomplete_not_empty; 357 #endif /* INCLUDE_AUTOCOMPLETE */ 358