xref: /onnv-gate/usr/src/cmd/filebench/common/auto_comp.c (revision 6391:f317d2de8920)
15184Sek110237 /*
25184Sek110237  * CDDL HEADER START
35184Sek110237  *
45184Sek110237  * The contents of this file are subject to the terms of the
55184Sek110237  * Common Development and Distribution License (the "License").
65184Sek110237  * You may not use this file except in compliance with the License.
75184Sek110237  *
85184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95184Sek110237  * or http://www.opensolaris.org/os/licensing.
105184Sek110237  * See the License for the specific language governing permissions
115184Sek110237  * and limitations under the License.
125184Sek110237  *
135184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
145184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
165184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
175184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
185184Sek110237  *
195184Sek110237  * CDDL HEADER END
205184Sek110237  */
215184Sek110237 /*
22*6391Saw148015  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235184Sek110237  * Use is subject to license terms.
245184Sek110237  */
255184Sek110237 
265184Sek110237 #pragma ident	"%Z%%M%	%I%	%E% SMI"
275184Sek110237 
285184Sek110237 #include <sys/types.h>
295184Sek110237 #include <dirent.h>
305184Sek110237 #include <strings.h>
315184Sek110237 #include "filebench.h"
325184Sek110237 #include "auto_comp.h"
335184Sek110237 
345184Sek110237 #define	VARNAME_MAXLEN	128
355184Sek110237 #define	FILENAME_MAXLEN	128
365184Sek110237 #define	MALLOC_STEP	64
375184Sek110237 
385184Sek110237 #define	CSUF_CMD	" "
395184Sek110237 #define	CSUF_ARG	" "
405184Sek110237 #define	CSUF_LVARNAME	"="
415184Sek110237 #define	CSUF_RVARNAME	","
425184Sek110237 #define	CSUF_ATTRNAME	"="
435184Sek110237 
445184Sek110237 #define	ATTR_LIST_SEP	','
455184Sek110237 #define	ATTR_ASSIGN_OP	'='
465184Sek110237 #define	VAR_ASSIGN_OP	'='
475184Sek110237 #define	VAR_PREFIX	'$'
485184Sek110237 
495184Sek110237 #ifndef HAVE_BOOLEAN_T
505184Sek110237 typedef enum { B_FALSE, B_TRUE } boolean_t;
515184Sek110237 #endif
525184Sek110237 
535184Sek110237 typedef char ac_fname_t[FILENAME_MAXLEN];
545184Sek110237 
555184Sek110237 typedef struct ac_fname_cache {
565184Sek110237 	ac_fname_t	*fnc_buf;
575184Sek110237 	int		fnc_bufsize;
585184Sek110237 	time_t		fnc_mtime;
595184Sek110237 } ac_fname_cache_t;
605184Sek110237 
615184Sek110237 typedef enum ac_match_result {
625184Sek110237 	MATCH_DONE,
635184Sek110237 	MATCH_CONT
645184Sek110237 } ac_match_result_t;
655184Sek110237 
665184Sek110237 /*
675184Sek110237  * We parse an user input line into multiple blank separated strings.
685184Sek110237  * The last string is always the one user wants to complete, the other
695184Sek110237  * preceding strings set up the context on how to complete the last one.
705184Sek110237  *
715184Sek110237  * ac_str_t repsents one such a string, which can be of the following
725184Sek110237  * types:
735184Sek110237  *
745184Sek110237  *	STRTYPE_COMPLETE   - the string is one of the preceding strings.
755184Sek110237  *	STRTYPE_INCOMPLETE - the string is the one being completed, user
765184Sek110237  *			     has inputted at least one character for it.
775184Sek110237  *	STRTYPE_NULL       - the string is the one being completed, user
785184Sek110237  *			     has inputted nothing for it.
795184Sek110237  *
805184Sek110237  * ac_str_t structure has the following members:
815184Sek110237  *
825184Sek110237  * 	startp	- the start position of the string in the user input buffer
835184Sek110237  * 	endp	- the end position of the string in the user input buffer
845184Sek110237  *	strtype	- the type of the string. It can be of the following values:
855184Sek110237  *		  STRTYPE_COMPLETE, STRTYPE_INCOMPLETE, STRTYPE_NULL,
865184Sek110237  *		  and STRTYPE_INVALID.
875184Sek110237  */
885184Sek110237 
895184Sek110237 typedef enum ac_strtype {
905184Sek110237 	STRTYPE_COMPLETE,
915184Sek110237 	STRTYPE_INCOMPLETE,
925184Sek110237 	STRTYPE_NULL,
935184Sek110237 	STRTYPE_INVALID
945184Sek110237 } ac_strtype_t;
955184Sek110237 
965184Sek110237 typedef struct ac_str {
975184Sek110237 	const char *startp;
985184Sek110237 	const char *endp;
995184Sek110237 	ac_strtype_t strtype;
1005184Sek110237 } ac_str_t;
1015184Sek110237 
1025184Sek110237 #define	STR_NUM	3
1035184Sek110237 
1045184Sek110237 typedef struct ac_inputline {
1055184Sek110237 	ac_str_t strs[STR_NUM];
1065184Sek110237 } ac_inputline_t;
1075184Sek110237 
1085184Sek110237 /*
1095184Sek110237  * ac_iter represents a general interface to access a list of values for
1105184Sek110237  * matching user input string. The structure has the following methods:
1115184Sek110237  *
1125184Sek110237  *	bind  - bind the iterator to a list, and save a pointer to user
1135184Sek110237  *		passed space in nlistpp.
1145184Sek110237  *	reset - reset internal index pointer to point to list head.
1155184Sek110237  *	get_nextstr - this is the method that does the real work. It
1165184Sek110237  *		walks through the list and returns string associated with
1175184Sek110237  *		the current item. It can also return some other thing the
1185184Sek110237  *		caller is interested via the user passed space pointed by
1195184Sek110237  *		nlistpp. In our case, that is a pointer to a list which
1205184Sek110237  *		contains all possible values for the next string in user
1215184Sek110237  *		input.
1225184Sek110237  *
1235184Sek110237  * It has the following data members:
1245184Sek110237  *
1255184Sek110237  *	listp   - a pointer to the list to be iterated through
1265184Sek110237  *	curp    - index pointer to maintain position when iterating
1275184Sek110237  *	nlistpp - a pointer to user passed space for returning a list of
1285184Sek110237  *		  values for the next string in user input
1295184Sek110237  */
1305184Sek110237 
1315184Sek110237 typedef struct ac_iter {
1325184Sek110237 	void	*listp;
1335184Sek110237 	void	*curp;
1345184Sek110237 	void	*nlistpp;
1355184Sek110237 	void	(*bind)(struct ac_iter *, void *, void *);
1365184Sek110237 	void	(*reset)(struct ac_iter *);
1375184Sek110237 	const char   *(*get_nextstr)(struct ac_iter *);
1385184Sek110237 } ac_iter_t;
1395184Sek110237 
1405184Sek110237 /*
1415184Sek110237  * We consider a filebench command is composed of a sequence of tokens
1425184Sek110237  * (ie., command name, argument name, attribute name, etc.). Many of
1435184Sek110237  * these tokens have limited string values. These values, as well as
1445184Sek110237  * their dependencies, are used to complete user input string.
1455184Sek110237  *
1465184Sek110237  * There are the following tokens:
1475184Sek110237  *
1485184Sek110237  *	TOKTYPE_CMD	 - command name
1495184Sek110237  *	TOKTYPE_ARG	 - argument name
1505184Sek110237  *	TOKTYPE_ATTRNAME - attribute name
1515184Sek110237  *	TOKTYPE_ATTRVAL  - attribute value
1525184Sek110237  *	TOKTYPE_LVARNAME - variable name, used on left side of assign
1535184Sek110237  *			   operator
1545184Sek110237  *	TOKTYPE_RVARNAME - variable name, used on right side of assign
1555184Sek110237  *			   operator
1565184Sek110237  *	TOKTYPE_VARVAL	 - variable value
1575184Sek110237  *	TOKTYPE_LOADFILE - load file name
1585184Sek110237  *	TOKTYPE_ATTRLIST - pseudo token type for attribute list
1595184Sek110237  *	TOKTYPE_VARLIST  - pseudo token type for variable list
1605184Sek110237  *	TOKTYPE_NULL	 - pseudo token type for aborting auto-completion
1615184Sek110237  *
1625184Sek110237  * The reason why there are two different token types for variable name
1635184Sek110237  * is because, depending on its position, there are different requirements
1645184Sek110237  * on how to do completion and display matching results. See more details
1655184Sek110237  * in lvarname_iter and rvarname_iter definition.
1665184Sek110237  *
1675184Sek110237  * Attribute list and variable list are not really a single token. Instead
1685184Sek110237  * they contain multiple tokens, and thus have different requirements on
1695184Sek110237  * how to complete them. TOKTYPE_ATTRLIST and TOKTYPE_VARLIST are
1705184Sek110237  * introduced to to solve this issue. See more details below on
1715184Sek110237  * get_curtok() function in ac_tokinfo_t structure.
1725184Sek110237  *
1735184Sek110237  * ac_tokval_t represents a string value a token can have. The structure
1745184Sek110237  * also contains a pointer to a ac_tvlist_t structure, which represents
1755184Sek110237  * all possible values for the next token in the same command.
1765184Sek110237  *
1775184Sek110237  *	str    - the token's string value
1785184Sek110237  *	nlistp - a list which contains string values for the token
1795184Sek110237  *		 that follows
1805184Sek110237  *
1815184Sek110237  * ac_tvlist_t represents all possible values for a token. These values
1825184Sek110237  * are stored in an ac_tokval_t array. The structure also has a member
1835184Sek110237  * toktype, which is used to index an ac_tokinfo_t array to get the
1845184Sek110237  * information on how to access and use the associated value list.
1855184Sek110237  *
1865184Sek110237  *	vals	- a list of string values for this token
1875184Sek110237  *	toktype	- the token's type
1885184Sek110237  *
1895184Sek110237  * ac_tokinfo_t contains information on how to access and use the
1905184Sek110237  * string values of a specific token. Among them, the most important
1915184Sek110237  * thing is an iterator to access the value list. The reason to use
1925184Sek110237  * iterator is to encapsulate list implementation details. That is
1935184Sek110237  * necessary because some tokens have dynamic values(for example,
1945184Sek110237  * argument of load command), which cannot be predefined using
1955184Sek110237  * ac_tokval_t array.
1965184Sek110237  *
1975184Sek110237  * ac_tokinfo_t structure has the following members:
1985184Sek110237  *
1995184Sek110237  *	toktype	    - token type
2005184Sek110237  *	iter	    - iterator to access the token's value list
2015184Sek110237  *	cont_suffix - continuation suffix for this token. See note 1
2025184Sek110237  *		      below on what is continuation suffix.
2035184Sek110237  *	get_curtok  - a function to parse a multi-token user string.
2045184Sek110237  *		      It parse that string and returns the word being
2055184Sek110237  *		      completed and its token type. See note 2 below.
2065184Sek110237  *
2075184Sek110237  * Notes:
2085184Sek110237  *
2095184Sek110237  * 1) Continuation suffix is a convenient feature provided by libtecla.
2105184Sek110237  *    A continuation suffix is a string which is automatically appended
2115184Sek110237  *    to a fully completed string. For example, if a command name is
2125184Sek110237  *    fully completed, a blank space will be appended to it. This is
2135184Sek110237  *    very convenient because it not only saves typing, but also gives
2145184Sek110237  *    user an indication to continue.
2155184Sek110237  *
2165184Sek110237  * 2) get_curtok() function is a trick to support user input strings
2175184Sek110237  *    which have multiple tokens. Take attribute list as an example,
2185184Sek110237  *    although we defined a token type TOKTYPE_ATTRLIST for it, it is
2195184Sek110237  *    not a token actually, instead it contains multiple tokens like
2205184Sek110237  *    attribute name, attribute value, etc., and attribute value can
2215184Sek110237  *    be either a literal string or a variable name prefixed with a
2225184Sek110237  *    '$' sign. For this reason, get_curtok() function is needed to
2235184Sek110237  *    parse that string to get the word being completed and its token
2245184Sek110237  *    type so that we can match the word against with the proper value
2255184Sek110237  *    list.
2265184Sek110237  */
2275184Sek110237 
2285184Sek110237 typedef enum ac_toktype {
2295184Sek110237 	TOKTYPE_CMD,
2305184Sek110237 	TOKTYPE_ARG,
2315184Sek110237 	TOKTYPE_ATTRNAME,
2325184Sek110237 	TOKTYPE_ATTRVAL,
2335184Sek110237 	TOKTYPE_LVARNAME,
2345184Sek110237 	TOKTYPE_RVARNAME,
2355184Sek110237 	TOKTYPE_VARVAL,
2365184Sek110237 	TOKTYPE_LOADFILE,
2375184Sek110237 	TOKTYPE_ATTRLIST,
2385184Sek110237 	TOKTYPE_VARLIST,
2395184Sek110237 	TOKTYPE_NULL
2405184Sek110237 } ac_toktype_t;
2415184Sek110237 
2425184Sek110237 typedef ac_toktype_t (*ac_get_curtok_func_t)(ac_str_t *);
2435184Sek110237 
2445184Sek110237 typedef struct ac_tokinfo {
2455184Sek110237 	ac_toktype_t	toktype;
2465184Sek110237 	ac_iter_t	*iter;
2475184Sek110237 	char		*cont_suffix;
2485184Sek110237 	ac_get_curtok_func_t	get_curtok;
2495184Sek110237 } ac_tokinfo_t;
2505184Sek110237 
2515184Sek110237 typedef struct ac_tokval {
2525184Sek110237 	char		 *str;
2535184Sek110237 	struct ac_tvlist *nlistp;
2545184Sek110237 } ac_tokval_t;
2555184Sek110237 
2565184Sek110237 typedef struct ac_tvlist {
2575184Sek110237 	ac_tokval_t	*vals;
2585184Sek110237 	ac_toktype_t	toktype;
2595184Sek110237 } ac_tvlist_t;
2605184Sek110237 
2615184Sek110237 /*
2625184Sek110237  * Variables and prototypes
2635184Sek110237  */
2645184Sek110237 
2655184Sek110237 static void common_bind(ac_iter_t *, void *, void *);
2665184Sek110237 static void common_reset(ac_iter_t *);
2675184Sek110237 static void varname_bind(ac_iter_t *, void *, void *);
2685184Sek110237 static void loadfile_bind(ac_iter_t *, void *, void *);
2695184Sek110237 static const char *get_next_tokval(ac_iter_t *);
2705184Sek110237 static const char *get_next_lvarname(ac_iter_t *);
2715184Sek110237 static const char *get_next_rvarname(ac_iter_t *);
2725184Sek110237 static const char *get_next_loadfile(ac_iter_t *);
2735184Sek110237 static ac_toktype_t parse_attr_list(ac_str_t *);
2745184Sek110237 static ac_toktype_t parse_var_list(ac_str_t *);
2755184Sek110237 
2765184Sek110237 static ac_iter_t tokval_iter = {
2775184Sek110237 	NULL,
2785184Sek110237 	NULL,
2795184Sek110237 	NULL,
2805184Sek110237 	common_bind,
2815184Sek110237 	common_reset,
2825184Sek110237 	get_next_tokval
2835184Sek110237 };
2845184Sek110237 
2855184Sek110237 static ac_iter_t lvarname_iter = {
2865184Sek110237 	NULL,
2875184Sek110237 	NULL,
2885184Sek110237 	NULL,
2895184Sek110237 	varname_bind,
2905184Sek110237 	common_reset,
2915184Sek110237 	get_next_lvarname
2925184Sek110237 };
2935184Sek110237 
2945184Sek110237 static ac_iter_t rvarname_iter = {
2955184Sek110237 	NULL,
2965184Sek110237 	NULL,
2975184Sek110237 	NULL,
2985184Sek110237 	varname_bind,
2995184Sek110237 	common_reset,
3005184Sek110237 	get_next_rvarname
3015184Sek110237 };
3025184Sek110237 
3035184Sek110237 static ac_iter_t loadfile_iter = {
3045184Sek110237 	NULL,
3055184Sek110237 	NULL,
3065184Sek110237 	NULL,
3075184Sek110237 	loadfile_bind,
3085184Sek110237 	common_reset,
3095184Sek110237 	get_next_loadfile
3105184Sek110237 };
3115184Sek110237 
3125184Sek110237 /*
3135184Sek110237  * Note: We use toktype to index into this array, so for each toktype,
3145184Sek110237  *	 there must be one element in the array, and in the same order
3155184Sek110237  *	 as that toktype is defined in ac_toktype.
3165184Sek110237  */
3175184Sek110237 static ac_tokinfo_t token_info[] = {
3185184Sek110237 	{ TOKTYPE_CMD,	    &tokval_iter,   CSUF_CMD,	   NULL },
3195184Sek110237 	{ TOKTYPE_ARG,	    &tokval_iter,   CSUF_ARG,	   NULL },
3205184Sek110237 	{ TOKTYPE_ATTRNAME, &tokval_iter,   CSUF_ATTRNAME, NULL },
3215184Sek110237 	{ TOKTYPE_ATTRVAL,  NULL,	    NULL,	   NULL },
3225184Sek110237 	{ TOKTYPE_LVARNAME, &lvarname_iter, CSUF_LVARNAME, NULL },
3235184Sek110237 	{ TOKTYPE_RVARNAME, &rvarname_iter, CSUF_RVARNAME, NULL },
3245184Sek110237 	{ TOKTYPE_VARVAL,   NULL,	    NULL,	   NULL },
3255184Sek110237 	{ TOKTYPE_LOADFILE, &loadfile_iter, CSUF_ARG,	   NULL },
3265184Sek110237 	{ TOKTYPE_ATTRLIST, NULL,	    NULL,	   parse_attr_list },
3275184Sek110237 	{ TOKTYPE_VARLIST,  NULL,	    NULL,	   parse_var_list },
3285184Sek110237 	{ TOKTYPE_NULL,	    NULL,	    NULL,	   NULL }
3295184Sek110237 };
3305184Sek110237 
3315184Sek110237 static ac_tokval_t event_attrnames[] = {
3325184Sek110237 	{ "rate",	NULL},
3335184Sek110237 	{ NULL, 	NULL}
3345184Sek110237 };
3355184Sek110237 
3365184Sek110237 static ac_tvlist_t event_attrs = {
3375184Sek110237 	event_attrnames,
3385184Sek110237 	TOKTYPE_ATTRLIST
3395184Sek110237 };
3405184Sek110237 
3415184Sek110237 static ac_tokval_t file_attrnames[] = {
3425184Sek110237 	{ "path",	NULL },
3435184Sek110237 	{ "reuse",	NULL },
3445184Sek110237 	{ "prealloc",	NULL },
3455184Sek110237 	{ "paralloc",	NULL },
3465184Sek110237 	{ NULL, 	NULL }
3475184Sek110237 };
3485184Sek110237 
3495184Sek110237 static ac_tvlist_t file_attrs = {
3505184Sek110237 	file_attrnames,
3515184Sek110237 	TOKTYPE_ATTRLIST
3525184Sek110237 };
3535184Sek110237 
3545184Sek110237 static ac_tokval_t fileset_attrnames[] = {
3555184Sek110237 	{ "size",	NULL },
3565184Sek110237 	{ "path",	NULL },
3575184Sek110237 	{ "dirwidth",	NULL },
3585184Sek110237 	{ "prealloc",	NULL },
3595184Sek110237 	{ "filesizegamma",	NULL },
3605184Sek110237 	{ "dirgamma",	NULL },
3615184Sek110237 	{ "cached",	NULL },
3625184Sek110237 	{ "entries",	NULL },
3635184Sek110237 	{ NULL, 	NULL }
3645184Sek110237 };
3655184Sek110237 
3665184Sek110237 static ac_tvlist_t fileset_attrs = {
3675184Sek110237 	fileset_attrnames,
3685184Sek110237 	TOKTYPE_ATTRLIST
3695184Sek110237 };
3705184Sek110237 
3715184Sek110237 static ac_tokval_t process_attrnames[] = {
3725184Sek110237 	{ "nice",	NULL },
3735184Sek110237 	{ "instances",	NULL },
3745184Sek110237 	{ NULL,		NULL }
3755184Sek110237 };
3765184Sek110237 
3775184Sek110237 static ac_tvlist_t process_attrs = {
3785184Sek110237 	process_attrnames,
3795184Sek110237 	TOKTYPE_ATTRLIST
3805184Sek110237 };
3815184Sek110237 
3825184Sek110237 static ac_tokval_t create_argnames[] = {
3835184Sek110237 	{ "file",	NULL },
3845184Sek110237 	{ "fileset",	NULL },
3855184Sek110237 	{ "process",	NULL },
3865184Sek110237 	{ NULL, 	NULL }
3875184Sek110237 };
3885184Sek110237 
3895184Sek110237 static ac_tvlist_t create_args = {
3905184Sek110237 	create_argnames,
3915184Sek110237 	TOKTYPE_ARG
3925184Sek110237 };
3935184Sek110237 
3945184Sek110237 static ac_tokval_t define_argnames[] = {
3955184Sek110237 	{ "file", 	&file_attrs },
3965184Sek110237 	{ "fileset",	&fileset_attrs },
3975184Sek110237 	{ "process",	&process_attrs },
3985184Sek110237 	{ NULL, 	NULL }
3995184Sek110237 };
4005184Sek110237 
4015184Sek110237 static ac_tvlist_t define_args = {
4025184Sek110237 	define_argnames,
4035184Sek110237 	TOKTYPE_ARG
4045184Sek110237 };
4055184Sek110237 
4065184Sek110237 static ac_tvlist_t load_args = {
4075184Sek110237 	NULL,
4085184Sek110237 	TOKTYPE_LOADFILE
4095184Sek110237 };
4105184Sek110237 
4115184Sek110237 static ac_tvlist_t set_args = {
4125184Sek110237 	NULL,
4135184Sek110237 	TOKTYPE_VARLIST
4145184Sek110237 };
4155184Sek110237 
4165184Sek110237 static ac_tokval_t shutdown_argnames[] = {
4175184Sek110237 	{ "process",	NULL },
4185184Sek110237 	{ NULL,		NULL }
4195184Sek110237 };
4205184Sek110237 
4215184Sek110237 static ac_tvlist_t shutdown_args = {
4225184Sek110237 	shutdown_argnames,
4235184Sek110237 	TOKTYPE_ARG
4245184Sek110237 };
4255184Sek110237 
4265184Sek110237 static ac_tokval_t stats_argnames[] = {
4275184Sek110237 	{ "clear", 	NULL },
4285184Sek110237 	{ "directory", 	NULL },
4295184Sek110237 	{ "command",	NULL },
4305184Sek110237 	{ "dump",	NULL },
4315184Sek110237 	{ "xmldump",	NULL },
4325184Sek110237 	{ NULL, 	NULL }
4335184Sek110237 };
4345184Sek110237 
4355184Sek110237 static ac_tvlist_t stats_args = {
4365184Sek110237 	stats_argnames,
4375184Sek110237 	TOKTYPE_ARG
4385184Sek110237 };
4395184Sek110237 
4405184Sek110237 static ac_tokval_t fb_cmdnames[] = {
4415184Sek110237 	{ "create",	&create_args },
4425184Sek110237 	{ "define",	&define_args },
4435184Sek110237 	{ "debug",	NULL },
4445184Sek110237 	{ "echo",	NULL },
4455184Sek110237 	{ "eventgen",	&event_attrs },
4465184Sek110237 	{ "foreach",	NULL },
4475184Sek110237 	{ "help",	NULL },
4485184Sek110237 	{ "list",	NULL },
4495184Sek110237 	{ "load",	&load_args },
4505184Sek110237 	{ "log",	NULL },
4515184Sek110237 	{ "quit",	NULL },
4525184Sek110237 	{ "run",	NULL },
4535184Sek110237 	{ "set",	&set_args },
4545184Sek110237 	{ "shutdown",	&shutdown_args },
4555184Sek110237 	{ "sleep",	NULL },
4565184Sek110237 	{ "stats",	&stats_args },
4575184Sek110237 	{ "system",	NULL },
4585184Sek110237 	{ "usage",	NULL },
4595184Sek110237 	{ "vars",	NULL },
4605184Sek110237 	{ NULL,		NULL },
4615184Sek110237 };
4625184Sek110237 
4635184Sek110237 static ac_tvlist_t fb_cmds = {
4645184Sek110237 	fb_cmdnames,
4655184Sek110237 	TOKTYPE_CMD
4665184Sek110237 };
4675184Sek110237 
4685184Sek110237 static ac_fname_cache_t loadnames = { NULL, 0, 0 };
4695184Sek110237 
4705184Sek110237 static int search_loadfiles(ac_fname_cache_t *);
4715184Sek110237 static void parse_user_input(const char *, int, ac_inputline_t *);
4725184Sek110237 static int compare_string(ac_str_t *, const char *, boolean_t, const char **);
4735184Sek110237 static ac_match_result_t match_string(WordCompletion *, const char *, int,
4745184Sek110237     ac_str_t *, ac_iter_t *, const char *);
4755184Sek110237 
4765184Sek110237 /*
4775184Sek110237  * Bind the iterator to the passed list
4785184Sek110237  */
4795184Sek110237 static void
4805184Sek110237 common_bind(ac_iter_t *iterp, void *listp, void *nlistpp)
4815184Sek110237 {
4825184Sek110237 	iterp->listp = listp;
4835184Sek110237 	iterp->nlistpp = nlistpp;
4845184Sek110237 }
4855184Sek110237 
4865184Sek110237 /*
4875184Sek110237  * Reset index pointer to point to list head
4885184Sek110237  */
4895184Sek110237 static void
4905184Sek110237 common_reset(ac_iter_t *iterp)
4915184Sek110237 {
4925184Sek110237 	iterp->curp = iterp->listp;
4935184Sek110237 }
4945184Sek110237 
4955184Sek110237 /*
4965184Sek110237  * Walk through an array of ac_tokval_t structures and return string
4975184Sek110237  * of each item.
4985184Sek110237  */
4995184Sek110237 static const char *
5005184Sek110237 get_next_tokval(ac_iter_t *iterp)
5015184Sek110237 {
5025184Sek110237 	ac_tokval_t *listp = iterp->listp;  /* list head */
5035184Sek110237 	ac_tokval_t *curp = iterp->curp;  /* index pointer */
5045184Sek110237 	/* user passed variable for returning value list for next token */
5055184Sek110237 	ac_tvlist_t **nlistpp = iterp->nlistpp;
5065184Sek110237 	const char *p;
5075184Sek110237 
5085184Sek110237 	if (listp == NULL || curp == NULL)
5095184Sek110237 		return (NULL);
5105184Sek110237 
5115184Sek110237 	/* get the current item's string */
5125184Sek110237 	p = curp->str;
5135184Sek110237 
5145184Sek110237 	/*
5155184Sek110237 	 * save the current item's address into a user passed variable
5165184Sek110237 	 */
5175184Sek110237 	if (nlistpp != NULL)
5185184Sek110237 		*nlistpp = curp->nlistp;
5195184Sek110237 
5205184Sek110237 	/* advance the index pointer */
5215184Sek110237 	iterp->curp = ++curp;
5225184Sek110237 
5235184Sek110237 	return (p);
5245184Sek110237 }
5255184Sek110237 
5265184Sek110237 /*
527*6391Saw148015  * Bind the iterator to filebench_shm->shm_var_list
5285184Sek110237  */
529*6391Saw148015 /* ARGSUSED */
5305184Sek110237 static void
5315184Sek110237 varname_bind(ac_iter_t *iterp, void *listp, void * nlistpp)
5325184Sek110237 {
533*6391Saw148015 	iterp->listp = filebench_shm->shm_var_list;
5345184Sek110237 	iterp->nlistpp = nlistpp;
5355184Sek110237 }
5365184Sek110237 
5375184Sek110237 /*
5385184Sek110237  * Walk through a linked list of var_t type structures and return name
5395184Sek110237  * of each variable with a preceding '$' sign
5405184Sek110237  */
5415184Sek110237 static const char *
5425184Sek110237 get_next_lvarname(ac_iter_t *iterp)
5435184Sek110237 {
5445184Sek110237 	static char buf[VARNAME_MAXLEN];
5455184Sek110237 
5465184Sek110237 	var_t *listp = iterp->listp;  /* list head */
5475184Sek110237 	var_t *curp = iterp->curp;  /* index pointer */
5485184Sek110237 	/* User passed variable for returning value list for next token */
5495184Sek110237 	ac_tvlist_t **nlistpp = iterp->nlistpp;
5505184Sek110237 	const char *p;
5515184Sek110237 
5525184Sek110237 	if (listp == NULL || curp == NULL)
5535184Sek110237 		return (NULL);
5545184Sek110237 
5555184Sek110237 	/* Get current variable's name, copy it to buf, with a '$' prefix */
5565184Sek110237 	p = curp->var_name;
5575184Sek110237 	(void) snprintf(buf, sizeof (buf), "$%s", p);
5585184Sek110237 
5595184Sek110237 	/* No information for the next input string */
5605184Sek110237 	if (nlistpp != NULL)
5615184Sek110237 		*nlistpp = NULL;
5625184Sek110237 
5635184Sek110237 	/* Advance the index pointer */
5645184Sek110237 	iterp->curp = curp->var_next;
5655184Sek110237 
5665184Sek110237 	return (buf);
5675184Sek110237 }
5685184Sek110237 
5695184Sek110237 /*
5705184Sek110237  * Walk through a linked list of var_t type structures and return name
5715184Sek110237  * of each variable
5725184Sek110237  */
5735184Sek110237 static const char *
5745184Sek110237 get_next_rvarname(ac_iter_t *iterp)
5755184Sek110237 {
5765184Sek110237 	var_t *listp = iterp->listp;  /* list head */
5775184Sek110237 	var_t *curp = iterp->curp;  /* index pointer */
5785184Sek110237 	/* User passed variable for returning value list for next item */
5795184Sek110237 	ac_tvlist_t **nlistpp = iterp->nlistpp;
5805184Sek110237 	const char *p;
5815184Sek110237 
5825184Sek110237 	if (listp == NULL || curp == NULL)
5835184Sek110237 		return (NULL);
5845184Sek110237 
5855184Sek110237 	/* Get current variable's name */
5865184Sek110237 	p = curp->var_name;
5875184Sek110237 
5885184Sek110237 	/* No information for the next input string */
5895184Sek110237 	if (nlistpp != NULL)
5905184Sek110237 		*nlistpp = NULL;
5915184Sek110237 
5925184Sek110237 	/* Advance the index pointer */
5935184Sek110237 	iterp->curp = curp->var_next;
5945184Sek110237 
5955184Sek110237 	return (p);
5965184Sek110237 }
5975184Sek110237 
5985184Sek110237 /*
5995184Sek110237  * Bind the iterator to loadnames.fnc_buf, which is an ac_fname_t array
6005184Sek110237  * and contains up-to-date workload file names. The function calls
6015184Sek110237  * search_loadfiles() to update the cache before the binding.
6025184Sek110237  */
603*6391Saw148015 /* ARGSUSED */
6045184Sek110237 static void
6055184Sek110237 loadfile_bind(ac_iter_t *iterp, void *listp, void * nlistpp)
6065184Sek110237 {
6075184Sek110237 	/* Check loadfile name cache, update it if needed */
6085184Sek110237 	(void) search_loadfiles(&loadnames);
6095184Sek110237 
6105184Sek110237 	iterp->listp = loadnames.fnc_buf;
6115184Sek110237 	iterp->nlistpp = nlistpp;
6125184Sek110237 }
6135184Sek110237 
6145184Sek110237 /*
6155184Sek110237  * Walk through a string(ac_fname_t, more exactly) array and return each
6165184Sek110237  * string, until a NULL iterm is encountered.
6175184Sek110237  */
6185184Sek110237 static const char *
6195184Sek110237 get_next_loadfile(ac_iter_t *iterp)
6205184Sek110237 {
6215184Sek110237 	ac_fname_t *listp = iterp->listp; /* list head */
6225184Sek110237 	ac_fname_t *curp = iterp->curp; /* index pointer */
6235184Sek110237 	/* User passed variable for returning value list for next item */
6245184Sek110237 	ac_tvlist_t **nlistpp = iterp->nlistpp;
6255184Sek110237 	const char *p;
6265184Sek110237 
6275184Sek110237 	if (listp == NULL || curp == NULL)
6285184Sek110237 		return (NULL);
6295184Sek110237 
6305184Sek110237 	/*
6315184Sek110237 	 * Get current file name. If an NULL item is encountered, it means
6325184Sek110237 	 * this is the end of the list. In that case, we need to set p to
6335184Sek110237 	 * NULL to indicate to the caller that the end of the list is reached.
6345184Sek110237 	 */
6355184Sek110237 	p = (char *)curp;
6365184Sek110237 	if (*p == NULL)
6375184Sek110237 		p = NULL;
6385184Sek110237 
6395184Sek110237 	/* No information for the next input string */
6405184Sek110237 	if (nlistpp != NULL)
6415184Sek110237 		*nlistpp = NULL;
6425184Sek110237 
6435184Sek110237 	/* Advance the index pointer */
6445184Sek110237 	iterp->curp = ++curp;
6455184Sek110237 
6465184Sek110237 	return (p);
6475184Sek110237 }
6485184Sek110237 
6495184Sek110237 /*
6505184Sek110237  * Search for available workload files in workload direcotry and
6515184Sek110237  * update workload name cache.
6525184Sek110237  */
6535184Sek110237 static int
6545184Sek110237 search_loadfiles(ac_fname_cache_t *fnamecache)
6555184Sek110237 {
6565184Sek110237 	DIR *dirp;
6575184Sek110237 	struct dirent *fp;
6585184Sek110237 	struct stat dstat;
6595184Sek110237 	time_t mtime;
6605184Sek110237 	ac_fname_t *buf;
6615184Sek110237 	int bufsize = MALLOC_STEP;
6625184Sek110237 	int len, i;
6635184Sek110237 
6645184Sek110237 	if (stat(FILEBENCHDIR"/workloads", &dstat) != 0)
6655184Sek110237 		return (-1);
6665184Sek110237 	mtime = dstat.st_mtime;
6675184Sek110237 
6685184Sek110237 	/* Return if there is no change since last time */
6695184Sek110237 	if (mtime == fnamecache->fnc_mtime)
6705184Sek110237 		return (0);
6715184Sek110237 
6725184Sek110237 	/* Get loadfile names and cache it */
6735184Sek110237 	if ((buf = malloc(sizeof (ac_fname_t) * bufsize)) == NULL)
6745184Sek110237 		return (-1);
6755184Sek110237 	if ((dirp = opendir(FILEBENCHDIR"/workloads")) == NULL)
6765184Sek110237 		return (-1);
6775184Sek110237 	i = 0;
6785184Sek110237 	while ((fp = readdir(dirp)) != NULL) {
6795184Sek110237 		len = strlen(fp->d_name);
6805184Sek110237 		if (len <= 2 || (fp->d_name)[len - 2] != '.' ||
6815184Sek110237 		    (fp->d_name)[len - 1] != 'f')
6825184Sek110237 			continue;
6835184Sek110237 
6845184Sek110237 		if (i == bufsize) {
6855184Sek110237 			bufsize += MALLOC_STEP;
6865184Sek110237 			if ((buf = realloc(buf, sizeof (ac_fname_t) *
6875184Sek110237 			    bufsize)) == NULL)
6885184Sek110237 				return (-1);
6895184Sek110237 		}
6905184Sek110237 
6915184Sek110237 		(void) snprintf(buf[i], FILENAME_MAXLEN, "%s", fp->d_name);
6925184Sek110237 		if (len -2 <= FILENAME_MAXLEN - 1) {
6935184Sek110237 			/* Remove .f suffix in file name */
6945184Sek110237 			buf[i][len -2] = NULL;
6955184Sek110237 		}
6965184Sek110237 		i++;
6975184Sek110237 	}
6985184Sek110237 	/* Added a NULL iterm as the array's terminator */
6995184Sek110237 	buf[i][0] = NULL;
7005184Sek110237 
7015184Sek110237 	if (fnamecache->fnc_bufsize != 0)
7025184Sek110237 		free(fnamecache->fnc_buf);
7035184Sek110237 	fnamecache->fnc_buf = buf;
7045184Sek110237 	fnamecache->fnc_bufsize = bufsize;
7055184Sek110237 	fnamecache->fnc_mtime = mtime;
7065184Sek110237 
7075184Sek110237 	return (0);
7085184Sek110237 }
7095184Sek110237 
7105184Sek110237 /*
7115184Sek110237  * Parse user input line into a list of blank separated strings, and
7125184Sek110237  * save the result in the passed ac_inputline_t structure. line and word_end
7135184Sek110237  * parameters are passed from libtecla library. line points to user input
7145184Sek110237  * buffer, and word_end is the index of the last character of user input.
7155184Sek110237  */
716*6391Saw148015 /* ARGSUSED */
7175184Sek110237 static void
7185184Sek110237 parse_user_input(const char *line, int word_end, ac_inputline_t *input)
7195184Sek110237 {
7205184Sek110237 	const char *p = line;
7215184Sek110237 	int i;
7225184Sek110237 
7235184Sek110237 	/* Reset all fileds */
7245184Sek110237 	for (i = 0; i < STR_NUM; i++) {
7255184Sek110237 		input->strs[i].startp = NULL;
7265184Sek110237 		input->strs[i].endp = NULL;
7275184Sek110237 		input->strs[i].strtype = STRTYPE_INVALID;
7285184Sek110237 	}
7295184Sek110237 
7305184Sek110237 	/*
7315184Sek110237 	 * Parse user input. We don't use word_end to do boundary checking,
7325184Sek110237 	 * instead we take advantage of the fact that the passed line
7335184Sek110237 	 * parameter is always terminated by '\0'.
7345184Sek110237 	 */
7355184Sek110237 	for (i = 0; i < STR_NUM; i++) {
7365184Sek110237 		/* Skip leading blank spaces */
7375184Sek110237 		while (*p == ' ')
7385184Sek110237 			p++;
7395184Sek110237 
7405184Sek110237 		if (*p == NULL) {
7415184Sek110237 			/*
7425184Sek110237 			 * User input nothing for the string being input
7435184Sek110237 			 * before he pressed TAB. We use STR_NULL flag
7445184Sek110237 			 * to indicate this so that match_str() will list
7455184Sek110237 			 * all available candidates.
7465184Sek110237 			 */
7475184Sek110237 			input->strs[i].startp = p;
7485184Sek110237 			input->strs[i].strtype = STRTYPE_NULL;
7495184Sek110237 			return;
7505184Sek110237 		}
7515184Sek110237 
7525184Sek110237 		/* Recoard the start and end of the string */
7535184Sek110237 		input->strs[i].startp = p;
7545184Sek110237 		while ((*p != ' ') && (*p != NULL))
7555184Sek110237 			p++;
7565184Sek110237 		input->strs[i].endp = p - 1;
7575184Sek110237 
7585184Sek110237 		if (*p == NULL) {
7595184Sek110237 			input->strs[i].strtype = STRTYPE_INCOMPLETE;
7605184Sek110237 			return;
7615184Sek110237 		} else {
7625184Sek110237 			/* The string is followed by a blank space */
7635184Sek110237 			input->strs[i].strtype = STRTYPE_COMPLETE;
7645184Sek110237 		}
7655184Sek110237 	}
7665184Sek110237 }
7675184Sek110237 
7685184Sek110237 /*
7695184Sek110237  * Parse an input string which is an attribue list, get the current word
7705184Sek110237  * user wants to complete, and return its token type.
7715184Sek110237  *
7725184Sek110237  * An atribute list has the following format:
7735184Sek110237  *
7745184Sek110237  * 	name1=val,name2=$var,...
7755184Sek110237  *
7765184Sek110237  * The function modifies the passed acstr string on success to point to
7775184Sek110237  * the word being completed.
7785184Sek110237  */
7795184Sek110237 static ac_toktype_t
7805184Sek110237 parse_attr_list(ac_str_t *acstr)
7815184Sek110237 {
7825184Sek110237 	const char *p;
7835184Sek110237 
7845184Sek110237 	if (acstr->strtype == STRTYPE_COMPLETE) {
7855184Sek110237 		/*
7865184Sek110237 		 * User has input a complete string for attribute list
7875184Sek110237 		 * return TOKTYPE_NULL to abort the matching.
7885184Sek110237 		 */
7895184Sek110237 		return (TOKTYPE_ATTRLIST);
7905184Sek110237 	} else if (acstr->strtype == STRTYPE_NULL) {
7915184Sek110237 		/*
7925184Sek110237 		 * User haven't input anything for the attribute list,
7935184Sek110237 		 * he must be trying to list all attribute names.
7945184Sek110237 		 */
7955184Sek110237 		return (TOKTYPE_ATTRNAME);
7965184Sek110237 	}
7975184Sek110237 
7985184Sek110237 	/*
7995184Sek110237 	 * The string may contain multiple comma separated "name=value"
8005184Sek110237 	 * items. Try to find the last one and move startp to point to it.
8015184Sek110237 	 */
8025184Sek110237 	for (p = acstr->endp; p >= acstr->startp && *p != ATTR_LIST_SEP; p--) {}
8035184Sek110237 
8045184Sek110237 	if (p == acstr->endp) {
8055184Sek110237 		/*
8065184Sek110237 		 * The last character of the string is ',', which means
8075184Sek110237 		 * user is trying to list all attribute names.
8085184Sek110237 		 */
8095184Sek110237 		acstr->startp = p + 1;
8105184Sek110237 		acstr->strtype = STRTYPE_NULL;
8115184Sek110237 		return (TOKTYPE_ATTRNAME);
8125184Sek110237 	} else if (p > acstr->startp) {
8135184Sek110237 		/*
8145184Sek110237 		 * Found ',' between starp and endp, move startp pointer
8155184Sek110237 		 * to point to the last item.
8165184Sek110237 		 */
8175184Sek110237 		acstr->startp = p + 1;
8185184Sek110237 	}
8195184Sek110237 
8205184Sek110237 	/*
8215184Sek110237 	 * Now startp points to the last "name=value" item. Search in
8225184Sek110237 	 * the characters user has input for this item:
8235184Sek110237 	 *
8245184Sek110237 	 *   a) if there isn't '=' character, user is inputting attribute name
8255184Sek110237 	 *   b) if there is a '=' character and it is followed by a '$',
8265184Sek110237 	 *	user is inputting variable name
8275184Sek110237 	 *   c) if there is a '=' character and it isn't followed by a '$',
8285184Sek110237 	 *	user is inputting a literal string as attribute value.
8295184Sek110237 	 */
8305184Sek110237 	for (p = acstr->startp; p <= acstr->endp; p++) {
8315184Sek110237 		if (*p == ATTR_ASSIGN_OP) {
8325184Sek110237 			/* Found "=" operator in the string */
8335184Sek110237 			if (*(p + 1) == VAR_PREFIX) {
8345184Sek110237 				acstr->startp = p + 2;
8355184Sek110237 				if (*acstr->startp != NULL)
8365184Sek110237 					acstr->strtype = STRTYPE_INCOMPLETE;
8375184Sek110237 				else
8385184Sek110237 					acstr->strtype = STRTYPE_NULL;
8395184Sek110237 				return (TOKTYPE_RVARNAME);
8405184Sek110237 			} else {
8415184Sek110237 				return (TOKTYPE_ATTRVAL);
8425184Sek110237 			}
8435184Sek110237 		}
8445184Sek110237 	}
8455184Sek110237 
8465184Sek110237 	/* Didn't find '=' operator, the string must be an attribute name */
8475184Sek110237 	return (TOKTYPE_ATTRNAME);
8485184Sek110237 }
8495184Sek110237 
8505184Sek110237 /*
8515184Sek110237  * Parse an input string which is a variable list, get the current word
8525184Sek110237  * user wants to complete, and return its token type.
8535184Sek110237  *
8545184Sek110237  * A varaible list has the following format:
8555184Sek110237  *
8565184Sek110237  *	$varname=value
8575184Sek110237  *
8585184Sek110237  * The function modifies the passed acstr string on success to point to
8595184Sek110237  * the word being completed.
8605184Sek110237  */
8615184Sek110237 static ac_toktype_t
8625184Sek110237 parse_var_list(ac_str_t *acstr)
8635184Sek110237 {
8645184Sek110237 	const char *p;
8655184Sek110237 
8665184Sek110237 	if (acstr->strtype == STRTYPE_COMPLETE) {
8675184Sek110237 		/*
8685184Sek110237 		 * User has input a complete string for var list
8695184Sek110237 		 * return TOKTYPE_NULL to abort the matching.
8705184Sek110237 		 */
8715184Sek110237 		return (TOKTYPE_NULL);
8725184Sek110237 	} else if (acstr->strtype == STRTYPE_NULL) {
8735184Sek110237 		/*
8745184Sek110237 		 * User haven't input anything for the attribute list,
8755184Sek110237 		 * he must be trying to list all available var names.
8765184Sek110237 		 */
8775184Sek110237 		return (TOKTYPE_LVARNAME);
8785184Sek110237 	}
8795184Sek110237 
8805184Sek110237 	/*
8815184Sek110237 	 * Search in what user has input:
8825184Sek110237 	 *
8835184Sek110237 	 *   a) if there isn't a '=' character, user is inputting var name
8845184Sek110237 	 *   b) if there is a '=' character, user is inputting var value
8855184Sek110237 	 */
8865184Sek110237 	for (p = acstr->startp; p <= acstr->endp; p++) {
8875184Sek110237 		if (*p == VAR_ASSIGN_OP)
8885184Sek110237 			return (TOKTYPE_VARVAL);
8895184Sek110237 	}
8905184Sek110237 
8915184Sek110237 	/* Didn't find '=' operator, user must be inputting an var name */
8925184Sek110237 	return (TOKTYPE_LVARNAME);
8935184Sek110237 }
8945184Sek110237 
8955184Sek110237 /*
8965184Sek110237  * Compare two strings acstr and str. acstr is a string of ac_str_t type,
8975184Sek110237  * str is a normal string. If issub is B_TRUE, the function checks if
8985184Sek110237  * acstr is a sub-string of str, starting from index 0; otherwise it checks
8995184Sek110237  * if acstr and str are exactly the same.
9005184Sek110237  *
9015184Sek110237  * The function returns 0 on success and -1 on failure. When it succeeds,
9025184Sek110237  * it also set restp to point to the rest part of the normal string.
9035184Sek110237  */
9045184Sek110237 static int
9055184Sek110237 compare_string(ac_str_t *acstr, const char *str, boolean_t issub,
9065184Sek110237     const char **restp)
9075184Sek110237 {
9085184Sek110237 	const char *p, *q;
9095184Sek110237 
9105184Sek110237 	for (p = acstr->startp, q = str; (p <= acstr->endp) && (*q != '\0');
9115184Sek110237 	    p++, q++) {
9125184Sek110237 		if (*p != *q)
9135184Sek110237 			return (-1);
9145184Sek110237 	}
9155184Sek110237 
9165184Sek110237 	if (p == acstr->endp + 1) {
9175184Sek110237 		if (*q == '\0' || issub == B_TRUE) {
9185184Sek110237 			if (restp != NULL)
9195184Sek110237 				*restp = q;
9205184Sek110237 			return (0);
9215184Sek110237 		}
9225184Sek110237 	}
9235184Sek110237 
9245184Sek110237 	return (-1);
9255184Sek110237 }
9265184Sek110237 
9275184Sek110237 /*
9285184Sek110237  * Use the passed iterp iterator to access a list of string values to
9295184Sek110237  * look for those matches with acstr, an user input string to be completed.
9305184Sek110237  *
9315184Sek110237  * cpl, line, work_end, and cont_suffix are parameters needed by
9325184Sek110237  * cpl_add_completion(), which adds matched entries to libtecla.
9335184Sek110237  *
9345184Sek110237  * Since user input line may have multiple strings, the function is
9355184Sek110237  * expected to be called multiple times to match those strings one
9365184Sek110237  * by one until the last one is reached.
9375184Sek110237  *
9385184Sek110237  * The multi-step matching process also means the function should provide
9395184Sek110237  * a way to indicate to the caller whether to continue or abort the
9405184Sek110237  * whole matching process. The function does that with the following
9415184Sek110237  * return values:
9425184Sek110237  *
9435184Sek110237  *    MATCH_DONE - the matching for the whole user input is done. This
9445184Sek110237  *		   can mean either some items are found or none is found.
9455184Sek110237  *		   In either case, the caller shouldn't continue to
9465184Sek110237  *		   match the rest strings, either because there is
9475184Sek110237  *		   no strings left, or because the matching for the
9485184Sek110237  *		   current string failed so there is no need to check
9495184Sek110237  *		   further.
9505184Sek110237  *    MATCH_CONT - the matching for the current string succeeds, but
9515184Sek110237  *		   user needs to continue to match the rest strings.
9525184Sek110237  */
9535184Sek110237 static ac_match_result_t
9545184Sek110237 match_string(WordCompletion *cpl, const char *line, int word_end,
9555184Sek110237     ac_str_t *acstr, ac_iter_t *iterp, const char *cont_suffix)
9565184Sek110237 {
9575184Sek110237 	const char *str, *restp;
9585184Sek110237 
9595184Sek110237 	iterp->reset(iterp);
9605184Sek110237 
9615184Sek110237 	if (acstr->strtype == STRTYPE_COMPLETE) {
9625184Sek110237 		while ((str = iterp->get_nextstr(iterp)) != NULL) {
9635184Sek110237 			if (!compare_string(acstr, str, B_FALSE, NULL)) {
9645184Sek110237 				/* Continue to check rest strings */
9655184Sek110237 				return (MATCH_CONT);
9665184Sek110237 			}
9675184Sek110237 		}
9685184Sek110237 	} else if (acstr->strtype == STRTYPE_NULL) {
9695184Sek110237 		/* User input nothing. List all available strings */
9705184Sek110237 		while ((str = iterp->get_nextstr(iterp)) != NULL) {
9715184Sek110237 			(void) cpl_add_completion(cpl, line,
9725184Sek110237 			    acstr->startp - line, word_end, str,
9735184Sek110237 			    NULL, cont_suffix);
9745184Sek110237 		}
9755184Sek110237 	} else if (acstr->strtype == STRTYPE_INCOMPLETE) {
9765184Sek110237 		while ((str = iterp->get_nextstr(iterp)) != NULL) {
9775184Sek110237 			if (!compare_string(acstr, str, B_TRUE, &restp)) {
9785184Sek110237 				/* It matches! Add it. */
9795184Sek110237 				(void) cpl_add_completion(cpl, line,
9805184Sek110237 				    acstr->startp - line, word_end, restp,
9815184Sek110237 				    NULL, cont_suffix);
9825184Sek110237 			}
9835184Sek110237 		}
9845184Sek110237 	}
9855184Sek110237 
9865184Sek110237 	return (MATCH_DONE);
9875184Sek110237 }
9885184Sek110237 
9895184Sek110237 /*
9905184Sek110237  * This is the interface between filebench and libtecla for auto-
9915184Sek110237  * completion. It is called by libtecla whenever user initiates a
9925184Sek110237  * auto-completion request(ie., pressing TAB key).
9935184Sek110237  *
9945184Sek110237  * The function calls parse_user_input() to parse user input into
9955184Sek110237  * multiple strings, then it calls match_string() to match each
9965184Sek110237  * string in user input in sequence until either the last string
9975184Sek110237  * is reached and completed or the the matching fails.
9985184Sek110237  */
999*6391Saw148015 /* ARGSUSED */
10005184Sek110237 CPL_MATCH_FN(command_complete)
10015184Sek110237 {
10025184Sek110237 	ac_inputline_t inputline;
10035184Sek110237 	ac_tvlist_t *clistp = &fb_cmds, *nlistp;
10045184Sek110237 	ac_toktype_t toktype;
10055184Sek110237 	ac_iter_t *iterp;
10065184Sek110237 	char *cont_suffix;
10075184Sek110237 	ac_get_curtok_func_t get_curtok;
10085184Sek110237 	int i, ret;
10095184Sek110237 
10105184Sek110237 	/* Parse user input and save the result in inputline variable. */
10115184Sek110237 	parse_user_input(line, word_end, &inputline);
10125184Sek110237 
10135184Sek110237 	/*
10145184Sek110237 	 * Match each string in user input against the proper token's
10155184Sek110237 	 * value list, and continue the loop until either the last string
10165184Sek110237 	 * is reached and completed or the matching aborts.
10175184Sek110237 	 */
10185184Sek110237 	for (i = 0; i < STR_NUM &&
10195184Sek110237 	    inputline.strs[i].strtype != STRTYPE_INVALID && clistp != NULL;
10205184Sek110237 	    i++) {
10215184Sek110237 		toktype = clistp->toktype;
10225184Sek110237 
10235184Sek110237 		/*
10245184Sek110237 		 * If the current stirng can contain multiple tokens, modify
10255184Sek110237 		 * the stirng to point to the word being input and return
10265184Sek110237 		 * its token type.
10275184Sek110237 		 */
10285184Sek110237 		get_curtok = token_info[toktype].get_curtok;
10295184Sek110237 		if (get_curtok != NULL)
10305184Sek110237 			toktype = (*get_curtok)(&inputline.strs[i]);
10315184Sek110237 
10325184Sek110237 		iterp = token_info[toktype].iter;
10335184Sek110237 		cont_suffix = token_info[toktype].cont_suffix;
10345184Sek110237 		/* Return if there is no completion info for the token */
10355184Sek110237 		if (iterp == NULL)
10365184Sek110237 			break;
10375184Sek110237 
10385184Sek110237 		iterp->bind(iterp, clistp->vals, &nlistp);
10395184Sek110237 		/* Match user string against the token's list */
10405184Sek110237 		ret = match_string(cpl, line, word_end, &inputline.strs[i],
10415184Sek110237 		    iterp, cont_suffix);
10425184Sek110237 		if (ret == MATCH_DONE)
10435184Sek110237 			return (0);
10445184Sek110237 		clistp = nlistp;
10455184Sek110237 	}
10465184Sek110237 
10475184Sek110237 	return (0);
10485184Sek110237 }
1049