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 }