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