1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <dirent.h> 30 #include <strings.h> 31 32 #include "filebench.h" 33 #include "auto_comp.h" 34 35 #define VARNAME_MAXLEN 128 36 #define FILENAME_MAXLEN 128 37 #define MALLOC_STEP 64 38 39 #define CSUF_CMD " " 40 #define CSUF_ARG " " 41 #define CSUF_LVARNAME "=" 42 #define CSUF_RVARNAME "," 43 #define CSUF_ATTRNAME "=" 44 45 #define ATTR_LIST_SEP ',' 46 #define ATTR_ASSIGN_OP '=' 47 #define VAR_ASSIGN_OP '=' 48 #define VAR_PREFIX '$' 49 50 typedef char ac_fname_t[FILENAME_MAXLEN]; 51 52 typedef struct ac_fname_cache { 53 ac_fname_t *fnc_buf; 54 int fnc_bufsize; 55 time_t fnc_mtime; 56 } ac_fname_cache_t; 57 58 typedef enum ac_match_result { 59 MATCH_DONE, 60 MATCH_CONT 61 } ac_match_result_t; 62 63 /* 64 * We parse an user input line into multiple blank separated strings. 65 * The last string is always the one user wants to complete, the other 66 * preceding strings set up the context on how to complete the last one. 67 * 68 * ac_str_t repsents one such a string, which can be of the following 69 * types: 70 * 71 * STRTYPE_COMPLETE - the string is one of the preceding strings. 72 * STRTYPE_INCOMPLETE - the string is the one being completed, user 73 * has inputted at least one character for it. 74 * STRTYPE_NULL - the string is the one being completed, user 75 * has inputted nothing for it. 76 * 77 * ac_str_t structure has the following members: 78 * 79 * startp - the start position of the string in the user input buffer 80 * endp - the end position of the string in the user input buffer 81 * strtype - the type of the string. It can be of the following values: 82 * STRTYPE_COMPLETE, STRTYPE_INCOMPLETE, STRTYPE_NULL, 83 * and STRTYPE_INVALID. 84 */ 85 86 typedef enum ac_strtype { 87 STRTYPE_COMPLETE, 88 STRTYPE_INCOMPLETE, 89 STRTYPE_NULL, 90 STRTYPE_INVALID 91 } ac_strtype_t; 92 93 typedef struct ac_str { 94 const char *startp; 95 const char *endp; 96 ac_strtype_t strtype; 97 } ac_str_t; 98 99 #define STR_NUM 3 100 101 typedef struct ac_inputline { 102 ac_str_t strs[STR_NUM]; 103 } ac_inputline_t; 104 105 /* 106 * ac_iter represents a general interface to access a list of values for 107 * matching user input string. The structure has the following methods: 108 * 109 * bind - bind the iterator to a list, and save a pointer to user 110 * passed space in nlistpp. 111 * reset - reset internal index pointer to point to list head. 112 * get_nextstr - this is the method that does the real work. It 113 * walks through the list and returns string associated with 114 * the current item. It can also return some other thing the 115 * caller is interested via the user passed space pointed by 116 * nlistpp. In our case, that is a pointer to a list which 117 * contains all possible values for the next string in user 118 * input. 119 * 120 * It has the following data members: 121 * 122 * listp - a pointer to the list to be iterated through 123 * curp - index pointer to maintain position when iterating 124 * nlistpp - a pointer to user passed space for returning a list of 125 * values for the next string in user input 126 */ 127 128 typedef struct ac_iter { 129 void *listp; 130 void *curp; 131 void *nlistpp; 132 void (*bind)(struct ac_iter *, void *, void *); 133 void (*reset)(struct ac_iter *); 134 const char *(*get_nextstr)(struct ac_iter *); 135 } ac_iter_t; 136 137 /* 138 * We consider a filebench command is composed of a sequence of tokens 139 * (ie., command name, argument name, attribute name, etc.). Many of 140 * these tokens have limited string values. These values, as well as 141 * their dependencies, are used to complete user input string. 142 * 143 * There are the following tokens: 144 * 145 * TOKTYPE_CMD - command name 146 * TOKTYPE_ARG - argument name 147 * TOKTYPE_ATTRNAME - attribute name 148 * TOKTYPE_ATTRVAL - attribute value 149 * TOKTYPE_LVARNAME - variable name, used on left side of assign 150 * operator 151 * TOKTYPE_RVARNAME - variable name, used on right side of assign 152 * operator 153 * TOKTYPE_VARVAL - variable value 154 * TOKTYPE_LOADFILE - load file name 155 * TOKTYPE_ATTRLIST - pseudo token type for attribute list 156 * TOKTYPE_VARLIST - pseudo token type for variable list 157 * TOKTYPE_NULL - pseudo token type for aborting auto-completion 158 * 159 * The reason why there are two different token types for variable name 160 * is because, depending on its position, there are different requirements 161 * on how to do completion and display matching results. See more details 162 * in lvarname_iter and rvarname_iter definition. 163 * 164 * Attribute list and variable list are not really a single token. Instead 165 * they contain multiple tokens, and thus have different requirements on 166 * how to complete them. TOKTYPE_ATTRLIST and TOKTYPE_VARLIST are 167 * introduced to to solve this issue. See more details below on 168 * get_curtok() function in ac_tokinfo_t structure. 169 * 170 * ac_tokval_t represents a string value a token can have. The structure 171 * also contains a pointer to a ac_tvlist_t structure, which represents 172 * all possible values for the next token in the same command. 173 * 174 * str - the token's string value 175 * nlistp - a list which contains string values for the token 176 * that follows 177 * 178 * ac_tvlist_t represents all possible values for a token. These values 179 * are stored in an ac_tokval_t array. The structure also has a member 180 * toktype, which is used to index an ac_tokinfo_t array to get the 181 * information on how to access and use the associated value list. 182 * 183 * vals - a list of string values for this token 184 * toktype - the token's type 185 * 186 * ac_tokinfo_t contains information on how to access and use the 187 * string values of a specific token. Among them, the most important 188 * thing is an iterator to access the value list. The reason to use 189 * iterator is to encapsulate list implementation details. That is 190 * necessary because some tokens have dynamic values(for example, 191 * argument of load command), which cannot be predefined using 192 * ac_tokval_t array. 193 * 194 * ac_tokinfo_t structure has the following members: 195 * 196 * toktype - token type 197 * iter - iterator to access the token's value list 198 * cont_suffix - continuation suffix for this token. See note 1 199 * below on what is continuation suffix. 200 * get_curtok - a function to parse a multi-token user string. 201 * It parse that string and returns the word being 202 * completed and its token type. See note 2 below. 203 * 204 * Notes: 205 * 206 * 1) Continuation suffix is a convenient feature provided by libtecla. 207 * A continuation suffix is a string which is automatically appended 208 * to a fully completed string. For example, if a command name is 209 * fully completed, a blank space will be appended to it. This is 210 * very convenient because it not only saves typing, but also gives 211 * user an indication to continue. 212 * 213 * 2) get_curtok() function is a trick to support user input strings 214 * which have multiple tokens. Take attribute list as an example, 215 * although we defined a token type TOKTYPE_ATTRLIST for it, it is 216 * not a token actually, instead it contains multiple tokens like 217 * attribute name, attribute value, etc., and attribute value can 218 * be either a literal string or a variable name prefixed with a 219 * '$' sign. For this reason, get_curtok() function is needed to 220 * parse that string to get the word being completed and its token 221 * type so that we can match the word against with the proper value 222 * list. 223 */ 224 225 typedef enum ac_toktype { 226 TOKTYPE_CMD, 227 TOKTYPE_ARG, 228 TOKTYPE_ATTRNAME, 229 TOKTYPE_ATTRVAL, 230 TOKTYPE_LVARNAME, 231 TOKTYPE_RVARNAME, 232 TOKTYPE_VARVAL, 233 TOKTYPE_LOADFILE, 234 TOKTYPE_ATTRLIST, 235 TOKTYPE_VARLIST, 236 TOKTYPE_NULL 237 } ac_toktype_t; 238 239 typedef ac_toktype_t (*ac_get_curtok_func_t)(ac_str_t *); 240 241 typedef struct ac_tokinfo { 242 ac_toktype_t toktype; 243 ac_iter_t *iter; 244 char *cont_suffix; 245 ac_get_curtok_func_t get_curtok; 246 } ac_tokinfo_t; 247 248 typedef struct ac_tokval { 249 char *str; 250 struct ac_tvlist *nlistp; 251 } ac_tokval_t; 252 253 typedef struct ac_tvlist { 254 ac_tokval_t *vals; 255 ac_toktype_t toktype; 256 } ac_tvlist_t; 257 258 /* 259 * Variables and prototypes 260 */ 261 262 static void common_bind(ac_iter_t *, void *, void *); 263 static void common_reset(ac_iter_t *); 264 static void varname_bind(ac_iter_t *, void *, void *); 265 static void loadfile_bind(ac_iter_t *, void *, void *); 266 static const char *get_next_tokval(ac_iter_t *); 267 static const char *get_next_lvarname(ac_iter_t *); 268 static const char *get_next_rvarname(ac_iter_t *); 269 static const char *get_next_loadfile(ac_iter_t *); 270 static ac_toktype_t parse_attr_list(ac_str_t *); 271 static ac_toktype_t parse_var_list(ac_str_t *); 272 273 static ac_iter_t tokval_iter = { 274 NULL, 275 NULL, 276 NULL, 277 common_bind, 278 common_reset, 279 get_next_tokval 280 }; 281 282 static ac_iter_t lvarname_iter = { 283 NULL, 284 NULL, 285 NULL, 286 varname_bind, 287 common_reset, 288 get_next_lvarname 289 }; 290 291 static ac_iter_t rvarname_iter = { 292 NULL, 293 NULL, 294 NULL, 295 varname_bind, 296 common_reset, 297 get_next_rvarname 298 }; 299 300 static ac_iter_t loadfile_iter = { 301 NULL, 302 NULL, 303 NULL, 304 loadfile_bind, 305 common_reset, 306 get_next_loadfile 307 }; 308 309 /* 310 * Note: We use toktype to index into this array, so for each toktype, 311 * there must be one element in the array, and in the same order 312 * as that toktype is defined in ac_toktype. 313 */ 314 static ac_tokinfo_t token_info[] = { 315 { TOKTYPE_CMD, &tokval_iter, CSUF_CMD, NULL }, 316 { TOKTYPE_ARG, &tokval_iter, CSUF_ARG, NULL }, 317 { TOKTYPE_ATTRNAME, &tokval_iter, CSUF_ATTRNAME, NULL }, 318 { TOKTYPE_ATTRVAL, NULL, NULL, NULL }, 319 { TOKTYPE_LVARNAME, &lvarname_iter, CSUF_LVARNAME, NULL }, 320 { TOKTYPE_RVARNAME, &rvarname_iter, CSUF_RVARNAME, NULL }, 321 { TOKTYPE_VARVAL, NULL, NULL, NULL }, 322 { TOKTYPE_LOADFILE, &loadfile_iter, CSUF_ARG, NULL }, 323 { TOKTYPE_ATTRLIST, NULL, NULL, parse_attr_list }, 324 { TOKTYPE_VARLIST, NULL, NULL, parse_var_list }, 325 { TOKTYPE_NULL, NULL, NULL, NULL } 326 }; 327 328 static ac_tokval_t event_attrnames[] = { 329 { "rate", NULL}, 330 { NULL, NULL} 331 }; 332 333 static ac_tvlist_t event_attrs = { 334 event_attrnames, 335 TOKTYPE_ATTRLIST 336 }; 337 338 static ac_tokval_t file_attrnames[] = { 339 { "path", NULL }, 340 { "reuse", NULL }, 341 { "prealloc", NULL }, 342 { "paralloc", NULL }, 343 { NULL, NULL } 344 }; 345 346 static ac_tvlist_t file_attrs = { 347 file_attrnames, 348 TOKTYPE_ATTRLIST 349 }; 350 351 static ac_tokval_t fileset_attrnames[] = { 352 { "size", NULL }, 353 { "path", NULL }, 354 { "dirwidth", NULL }, 355 { "prealloc", NULL }, 356 { "filesizegamma", NULL }, 357 { "dirgamma", NULL }, 358 { "cached", NULL }, 359 { "entries", NULL }, 360 { NULL, NULL } 361 }; 362 363 static ac_tvlist_t fileset_attrs = { 364 fileset_attrnames, 365 TOKTYPE_ATTRLIST 366 }; 367 368 static ac_tokval_t process_attrnames[] = { 369 { "nice", NULL }, 370 { "instances", NULL }, 371 { NULL, NULL } 372 }; 373 374 static ac_tvlist_t process_attrs = { 375 process_attrnames, 376 TOKTYPE_ATTRLIST 377 }; 378 379 static ac_tokval_t create_argnames[] = { 380 { "file", NULL }, 381 { "fileset", NULL }, 382 { "process", NULL }, 383 { NULL, NULL } 384 }; 385 386 static ac_tvlist_t create_args = { 387 create_argnames, 388 TOKTYPE_ARG 389 }; 390 391 static ac_tokval_t define_argnames[] = { 392 { "file", &file_attrs }, 393 { "fileset", &fileset_attrs }, 394 { "process", &process_attrs }, 395 { NULL, NULL } 396 }; 397 398 static ac_tvlist_t define_args = { 399 define_argnames, 400 TOKTYPE_ARG 401 }; 402 403 static ac_tvlist_t load_args = { 404 NULL, 405 TOKTYPE_LOADFILE 406 }; 407 408 static ac_tvlist_t set_args = { 409 NULL, 410 TOKTYPE_VARLIST 411 }; 412 413 static ac_tokval_t shutdown_argnames[] = { 414 { "process", NULL }, 415 { NULL, NULL } 416 }; 417 418 static ac_tvlist_t shutdown_args = { 419 shutdown_argnames, 420 TOKTYPE_ARG 421 }; 422 423 static ac_tokval_t stats_argnames[] = { 424 { "clear", NULL }, 425 { "directory", NULL }, 426 { "command", NULL }, 427 { "dump", NULL }, 428 { "xmldump", NULL }, 429 { NULL, NULL } 430 }; 431 432 static ac_tvlist_t stats_args = { 433 stats_argnames, 434 TOKTYPE_ARG 435 }; 436 437 static ac_tokval_t fb_cmdnames[] = { 438 { "create", &create_args }, 439 { "define", &define_args }, 440 { "debug", NULL }, 441 { "echo", NULL }, 442 { "eventgen", &event_attrs }, 443 { "foreach", NULL }, 444 { "help", NULL }, 445 { "list", NULL }, 446 { "load", &load_args }, 447 { "log", NULL }, 448 { "quit", NULL }, 449 { "run", NULL }, 450 { "set", &set_args }, 451 { "shutdown", &shutdown_args }, 452 { "sleep", NULL }, 453 { "stats", &stats_args }, 454 { "system", NULL }, 455 { "usage", NULL }, 456 { "vars", NULL }, 457 { "version", NULL }, 458 { NULL, NULL }, 459 }; 460 461 static ac_tvlist_t fb_cmds = { 462 fb_cmdnames, 463 TOKTYPE_CMD 464 }; 465 466 static ac_fname_cache_t loadnames = { NULL, 0, 0 }; 467 468 static int search_loadfiles(ac_fname_cache_t *); 469 static void parse_user_input(const char *, int, ac_inputline_t *); 470 static int compare_string(ac_str_t *, const char *, boolean_t, const char **); 471 static ac_match_result_t match_string(WordCompletion *, const char *, int, 472 ac_str_t *, ac_iter_t *, const char *); 473 474 /* 475 * Bind the iterator to the passed list 476 */ 477 static void 478 common_bind(ac_iter_t *iterp, void *listp, void *nlistpp) 479 { 480 iterp->listp = listp; 481 iterp->nlistpp = nlistpp; 482 } 483 484 /* 485 * Reset index pointer to point to list head 486 */ 487 static void 488 common_reset(ac_iter_t *iterp) 489 { 490 iterp->curp = iterp->listp; 491 } 492 493 /* 494 * Walk through an array of ac_tokval_t structures and return string 495 * of each item. 496 */ 497 static const char * 498 get_next_tokval(ac_iter_t *iterp) 499 { 500 ac_tokval_t *listp = iterp->listp; /* list head */ 501 ac_tokval_t *curp = iterp->curp; /* index pointer */ 502 /* user passed variable for returning value list for next token */ 503 ac_tvlist_t **nlistpp = iterp->nlistpp; 504 const char *p; 505 506 if (listp == NULL || curp == NULL) 507 return (NULL); 508 509 /* get the current item's string */ 510 p = curp->str; 511 512 /* 513 * save the current item's address into a user passed variable 514 */ 515 if (nlistpp != NULL) 516 *nlistpp = curp->nlistp; 517 518 /* advance the index pointer */ 519 iterp->curp = ++curp; 520 521 return (p); 522 } 523 524 /* 525 * Bind the iterator to filebench_shm->shm_var_list 526 */ 527 /* ARGSUSED */ 528 static void 529 varname_bind(ac_iter_t *iterp, void *listp, void * nlistpp) 530 { 531 iterp->listp = filebench_shm->shm_var_list; 532 iterp->nlistpp = nlistpp; 533 } 534 535 /* 536 * Walk through a linked list of var_t type structures and return name 537 * of each variable with a preceding '$' sign 538 */ 539 static const char * 540 get_next_lvarname(ac_iter_t *iterp) 541 { 542 static char buf[VARNAME_MAXLEN]; 543 544 var_t *listp = iterp->listp; /* list head */ 545 var_t *curp = iterp->curp; /* index pointer */ 546 /* User passed variable for returning value list for next token */ 547 ac_tvlist_t **nlistpp = iterp->nlistpp; 548 const char *p; 549 550 if (listp == NULL || curp == NULL) 551 return (NULL); 552 553 /* Get current variable's name, copy it to buf, with a '$' prefix */ 554 p = curp->var_name; 555 (void) snprintf(buf, sizeof (buf), "$%s", p); 556 557 /* No information for the next input string */ 558 if (nlistpp != NULL) 559 *nlistpp = NULL; 560 561 /* Advance the index pointer */ 562 iterp->curp = curp->var_next; 563 564 return (buf); 565 } 566 567 /* 568 * Walk through a linked list of var_t type structures and return name 569 * of each variable 570 */ 571 static const char * 572 get_next_rvarname(ac_iter_t *iterp) 573 { 574 var_t *listp = iterp->listp; /* list head */ 575 var_t *curp = iterp->curp; /* index pointer */ 576 /* User passed variable for returning value list for next item */ 577 ac_tvlist_t **nlistpp = iterp->nlistpp; 578 const char *p; 579 580 if (listp == NULL || curp == NULL) 581 return (NULL); 582 583 /* Get current variable's name */ 584 p = curp->var_name; 585 586 /* No information for the next input string */ 587 if (nlistpp != NULL) 588 *nlistpp = NULL; 589 590 /* Advance the index pointer */ 591 iterp->curp = curp->var_next; 592 593 return (p); 594 } 595 596 /* 597 * Bind the iterator to loadnames.fnc_buf, which is an ac_fname_t array 598 * and contains up-to-date workload file names. The function calls 599 * search_loadfiles() to update the cache before the binding. 600 */ 601 /* ARGSUSED */ 602 static void 603 loadfile_bind(ac_iter_t *iterp, void *listp, void * nlistpp) 604 { 605 /* Check loadfile name cache, update it if needed */ 606 (void) search_loadfiles(&loadnames); 607 608 iterp->listp = loadnames.fnc_buf; 609 iterp->nlistpp = nlistpp; 610 } 611 612 /* 613 * Walk through a string(ac_fname_t, more exactly) array and return each 614 * string, until a NULL iterm is encountered. 615 */ 616 static const char * 617 get_next_loadfile(ac_iter_t *iterp) 618 { 619 ac_fname_t *listp = iterp->listp; /* list head */ 620 ac_fname_t *curp = iterp->curp; /* index pointer */ 621 /* User passed variable for returning value list for next item */ 622 ac_tvlist_t **nlistpp = iterp->nlistpp; 623 const char *p; 624 625 if (listp == NULL || curp == NULL) 626 return (NULL); 627 628 /* 629 * Get current file name. If an NULL item is encountered, it means 630 * this is the end of the list. In that case, we need to set p to 631 * NULL to indicate to the caller that the end of the list is reached. 632 */ 633 p = (char *)curp; 634 if (*p == NULL) 635 p = NULL; 636 637 /* No information for the next input string */ 638 if (nlistpp != NULL) 639 *nlistpp = NULL; 640 641 /* Advance the index pointer */ 642 iterp->curp = ++curp; 643 644 return (p); 645 } 646 647 /* 648 * Search for available workload files in workload direcotry and 649 * update workload name cache. 650 */ 651 static int 652 search_loadfiles(ac_fname_cache_t *fnamecache) 653 { 654 DIR *dirp; 655 struct dirent *fp; 656 struct stat dstat; 657 time_t mtime; 658 ac_fname_t *buf; 659 int bufsize = MALLOC_STEP; 660 int len, i; 661 662 if (stat(FILEBENCHDIR"/workloads", &dstat) != 0) 663 return (-1); 664 mtime = dstat.st_mtime; 665 666 /* Return if there is no change since last time */ 667 if (mtime == fnamecache->fnc_mtime) 668 return (0); 669 670 /* Get loadfile names and cache it */ 671 if ((buf = malloc(sizeof (ac_fname_t) * bufsize)) == NULL) 672 return (-1); 673 if ((dirp = opendir(FILEBENCHDIR"/workloads")) == NULL) 674 return (-1); 675 i = 0; 676 while ((fp = readdir(dirp)) != NULL) { 677 len = strlen(fp->d_name); 678 if (len <= 2 || (fp->d_name)[len - 2] != '.' || 679 (fp->d_name)[len - 1] != 'f') 680 continue; 681 682 if (i == bufsize) { 683 bufsize += MALLOC_STEP; 684 if ((buf = realloc(buf, sizeof (ac_fname_t) * 685 bufsize)) == NULL) 686 return (-1); 687 } 688 689 (void) snprintf(buf[i], FILENAME_MAXLEN, "%s", fp->d_name); 690 if (len -2 <= FILENAME_MAXLEN - 1) { 691 /* Remove .f suffix in file name */ 692 buf[i][len -2] = NULL; 693 } 694 i++; 695 } 696 /* Added a NULL iterm as the array's terminator */ 697 buf[i][0] = NULL; 698 699 if (fnamecache->fnc_bufsize != 0) 700 free(fnamecache->fnc_buf); 701 fnamecache->fnc_buf = buf; 702 fnamecache->fnc_bufsize = bufsize; 703 fnamecache->fnc_mtime = mtime; 704 705 return (0); 706 } 707 708 /* 709 * Parse user input line into a list of blank separated strings, and 710 * save the result in the passed ac_inputline_t structure. line and word_end 711 * parameters are passed from libtecla library. line points to user input 712 * buffer, and word_end is the index of the last character of user input. 713 */ 714 /* ARGSUSED */ 715 static void 716 parse_user_input(const char *line, int word_end, ac_inputline_t *input) 717 { 718 const char *p = line; 719 int i; 720 721 /* Reset all fileds */ 722 for (i = 0; i < STR_NUM; i++) { 723 input->strs[i].startp = NULL; 724 input->strs[i].endp = NULL; 725 input->strs[i].strtype = STRTYPE_INVALID; 726 } 727 728 /* 729 * Parse user input. We don't use word_end to do boundary checking, 730 * instead we take advantage of the fact that the passed line 731 * parameter is always terminated by '\0'. 732 */ 733 for (i = 0; i < STR_NUM; i++) { 734 /* Skip leading blank spaces */ 735 while (*p == ' ') 736 p++; 737 738 if (*p == NULL) { 739 /* 740 * User input nothing for the string being input 741 * before he pressed TAB. We use STR_NULL flag 742 * to indicate this so that match_str() will list 743 * all available candidates. 744 */ 745 input->strs[i].startp = p; 746 input->strs[i].strtype = STRTYPE_NULL; 747 return; 748 } 749 750 /* Recoard the start and end of the string */ 751 input->strs[i].startp = p; 752 while ((*p != ' ') && (*p != NULL)) 753 p++; 754 input->strs[i].endp = p - 1; 755 756 if (*p == NULL) { 757 input->strs[i].strtype = STRTYPE_INCOMPLETE; 758 return; 759 } else { 760 /* The string is followed by a blank space */ 761 input->strs[i].strtype = STRTYPE_COMPLETE; 762 } 763 } 764 } 765 766 /* 767 * Parse an input string which is an attribue list, get the current word 768 * user wants to complete, and return its token type. 769 * 770 * An atribute list has the following format: 771 * 772 * name1=val,name2=$var,... 773 * 774 * The function modifies the passed acstr string on success to point to 775 * the word being completed. 776 */ 777 static ac_toktype_t 778 parse_attr_list(ac_str_t *acstr) 779 { 780 const char *p; 781 782 if (acstr->strtype == STRTYPE_COMPLETE) { 783 /* 784 * User has input a complete string for attribute list 785 * return TOKTYPE_NULL to abort the matching. 786 */ 787 return (TOKTYPE_ATTRLIST); 788 } else if (acstr->strtype == STRTYPE_NULL) { 789 /* 790 * User haven't input anything for the attribute list, 791 * he must be trying to list all attribute names. 792 */ 793 return (TOKTYPE_ATTRNAME); 794 } 795 796 /* 797 * The string may contain multiple comma separated "name=value" 798 * items. Try to find the last one and move startp to point to it. 799 */ 800 for (p = acstr->endp; p >= acstr->startp && *p != ATTR_LIST_SEP; p--) {} 801 802 if (p == acstr->endp) { 803 /* 804 * The last character of the string is ',', which means 805 * user is trying to list all attribute names. 806 */ 807 acstr->startp = p + 1; 808 acstr->strtype = STRTYPE_NULL; 809 return (TOKTYPE_ATTRNAME); 810 } else if (p > acstr->startp) { 811 /* 812 * Found ',' between starp and endp, move startp pointer 813 * to point to the last item. 814 */ 815 acstr->startp = p + 1; 816 } 817 818 /* 819 * Now startp points to the last "name=value" item. Search in 820 * the characters user has input for this item: 821 * 822 * a) if there isn't '=' character, user is inputting attribute name 823 * b) if there is a '=' character and it is followed by a '$', 824 * user is inputting variable name 825 * c) if there is a '=' character and it isn't followed by a '$', 826 * user is inputting a literal string as attribute value. 827 */ 828 for (p = acstr->startp; p <= acstr->endp; p++) { 829 if (*p == ATTR_ASSIGN_OP) { 830 /* Found "=" operator in the string */ 831 if (*(p + 1) == VAR_PREFIX) { 832 acstr->startp = p + 2; 833 if (*acstr->startp != NULL) 834 acstr->strtype = STRTYPE_INCOMPLETE; 835 else 836 acstr->strtype = STRTYPE_NULL; 837 return (TOKTYPE_RVARNAME); 838 } else { 839 return (TOKTYPE_ATTRVAL); 840 } 841 } 842 } 843 844 /* Didn't find '=' operator, the string must be an attribute name */ 845 return (TOKTYPE_ATTRNAME); 846 } 847 848 /* 849 * Parse an input string which is a variable list, get the current word 850 * user wants to complete, and return its token type. 851 * 852 * A varaible list has the following format: 853 * 854 * $varname=value 855 * 856 * The function modifies the passed acstr string on success to point to 857 * the word being completed. 858 */ 859 static ac_toktype_t 860 parse_var_list(ac_str_t *acstr) 861 { 862 const char *p; 863 864 if (acstr->strtype == STRTYPE_COMPLETE) { 865 /* 866 * User has input a complete string for var list 867 * return TOKTYPE_NULL to abort the matching. 868 */ 869 return (TOKTYPE_NULL); 870 } else if (acstr->strtype == STRTYPE_NULL) { 871 /* 872 * User haven't input anything for the attribute list, 873 * he must be trying to list all available var names. 874 */ 875 return (TOKTYPE_LVARNAME); 876 } 877 878 /* 879 * Search in what user has input: 880 * 881 * a) if there isn't a '=' character, user is inputting var name 882 * b) if there is a '=' character, user is inputting var value 883 */ 884 for (p = acstr->startp; p <= acstr->endp; p++) { 885 if (*p == VAR_ASSIGN_OP) 886 return (TOKTYPE_VARVAL); 887 } 888 889 /* Didn't find '=' operator, user must be inputting an var name */ 890 return (TOKTYPE_LVARNAME); 891 } 892 893 /* 894 * Compare two strings acstr and str. acstr is a string of ac_str_t type, 895 * str is a normal string. If issub is B_TRUE, the function checks if 896 * acstr is a sub-string of str, starting from index 0; otherwise it checks 897 * if acstr and str are exactly the same. 898 * 899 * The function returns 0 on success and -1 on failure. When it succeeds, 900 * it also set restp to point to the rest part of the normal string. 901 */ 902 static int 903 compare_string(ac_str_t *acstr, const char *str, boolean_t issub, 904 const char **restp) 905 { 906 const char *p, *q; 907 908 for (p = acstr->startp, q = str; (p <= acstr->endp) && (*q != '\0'); 909 p++, q++) { 910 if (*p != *q) 911 return (-1); 912 } 913 914 if (p == acstr->endp + 1) { 915 if (*q == '\0' || issub == B_TRUE) { 916 if (restp != NULL) 917 *restp = q; 918 return (0); 919 } 920 } 921 922 return (-1); 923 } 924 925 /* 926 * Use the passed iterp iterator to access a list of string values to 927 * look for those matches with acstr, an user input string to be completed. 928 * 929 * cpl, line, work_end, and cont_suffix are parameters needed by 930 * cpl_add_completion(), which adds matched entries to libtecla. 931 * 932 * Since user input line may have multiple strings, the function is 933 * expected to be called multiple times to match those strings one 934 * by one until the last one is reached. 935 * 936 * The multi-step matching process also means the function should provide 937 * a way to indicate to the caller whether to continue or abort the 938 * whole matching process. The function does that with the following 939 * return values: 940 * 941 * MATCH_DONE - the matching for the whole user input is done. This 942 * can mean either some items are found or none is found. 943 * In either case, the caller shouldn't continue to 944 * match the rest strings, either because there is 945 * no strings left, or because the matching for the 946 * current string failed so there is no need to check 947 * further. 948 * MATCH_CONT - the matching for the current string succeeds, but 949 * user needs to continue to match the rest strings. 950 */ 951 static ac_match_result_t 952 match_string(WordCompletion *cpl, const char *line, int word_end, 953 ac_str_t *acstr, ac_iter_t *iterp, const char *cont_suffix) 954 { 955 const char *str, *restp; 956 957 iterp->reset(iterp); 958 959 if (acstr->strtype == STRTYPE_COMPLETE) { 960 while ((str = iterp->get_nextstr(iterp)) != NULL) { 961 if (!compare_string(acstr, str, B_FALSE, NULL)) { 962 /* Continue to check rest strings */ 963 return (MATCH_CONT); 964 } 965 } 966 } else if (acstr->strtype == STRTYPE_NULL) { 967 /* User input nothing. List all available strings */ 968 while ((str = iterp->get_nextstr(iterp)) != NULL) { 969 (void) cpl_add_completion(cpl, line, 970 acstr->startp - line, word_end, str, 971 NULL, cont_suffix); 972 } 973 } else if (acstr->strtype == STRTYPE_INCOMPLETE) { 974 while ((str = iterp->get_nextstr(iterp)) != NULL) { 975 if (!compare_string(acstr, str, B_TRUE, &restp)) { 976 /* It matches! Add it. */ 977 (void) cpl_add_completion(cpl, line, 978 acstr->startp - line, word_end, restp, 979 NULL, cont_suffix); 980 } 981 } 982 } 983 984 return (MATCH_DONE); 985 } 986 987 /* 988 * This is the interface between filebench and libtecla for auto- 989 * completion. It is called by libtecla whenever user initiates a 990 * auto-completion request(ie., pressing TAB key). 991 * 992 * The function calls parse_user_input() to parse user input into 993 * multiple strings, then it calls match_string() to match each 994 * string in user input in sequence until either the last string 995 * is reached and completed or the the matching fails. 996 */ 997 /* ARGSUSED */ 998 CPL_MATCH_FN(command_complete) 999 { 1000 ac_inputline_t inputline; 1001 ac_tvlist_t *clistp = &fb_cmds, *nlistp; 1002 ac_toktype_t toktype; 1003 ac_iter_t *iterp; 1004 char *cont_suffix; 1005 ac_get_curtok_func_t get_curtok; 1006 int i, ret; 1007 1008 /* Parse user input and save the result in inputline variable. */ 1009 parse_user_input(line, word_end, &inputline); 1010 1011 /* 1012 * Match each string in user input against the proper token's 1013 * value list, and continue the loop until either the last string 1014 * is reached and completed or the matching aborts. 1015 */ 1016 for (i = 0; i < STR_NUM && 1017 inputline.strs[i].strtype != STRTYPE_INVALID && clistp != NULL; 1018 i++) { 1019 toktype = clistp->toktype; 1020 1021 /* 1022 * If the current stirng can contain multiple tokens, modify 1023 * the stirng to point to the word being input and return 1024 * its token type. 1025 */ 1026 get_curtok = token_info[toktype].get_curtok; 1027 if (get_curtok != NULL) 1028 toktype = (*get_curtok)(&inputline.strs[i]); 1029 1030 iterp = token_info[toktype].iter; 1031 cont_suffix = token_info[toktype].cont_suffix; 1032 /* Return if there is no completion info for the token */ 1033 if (iterp == NULL) 1034 break; 1035 1036 iterp->bind(iterp, clistp->vals, &nlistp); 1037 /* Match user string against the token's list */ 1038 ret = match_string(cpl, line, word_end, &inputline.strs[i], 1039 iterp, cont_suffix); 1040 if (ret == MATCH_DONE) 1041 return (0); 1042 clistp = nlistp; 1043 } 1044 1045 return (0); 1046 }