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