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