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> 31*6613Sek110237 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 }, 4575184Sek110237 { NULL, NULL }, 4585184Sek110237 }; 4595184Sek110237 4605184Sek110237 static ac_tvlist_t fb_cmds = { 4615184Sek110237 fb_cmdnames, 4625184Sek110237 TOKTYPE_CMD 4635184Sek110237 }; 4645184Sek110237 4655184Sek110237 static ac_fname_cache_t loadnames = { NULL, 0, 0 }; 4665184Sek110237 4675184Sek110237 static int search_loadfiles(ac_fname_cache_t *); 4685184Sek110237 static void parse_user_input(const char *, int, ac_inputline_t *); 4695184Sek110237 static int compare_string(ac_str_t *, const char *, boolean_t, const char **); 4705184Sek110237 static ac_match_result_t match_string(WordCompletion *, const char *, int, 4715184Sek110237 ac_str_t *, ac_iter_t *, const char *); 4725184Sek110237 4735184Sek110237 /* 4745184Sek110237 * Bind the iterator to the passed list 4755184Sek110237 */ 4765184Sek110237 static void 4775184Sek110237 common_bind(ac_iter_t *iterp, void *listp, void *nlistpp) 4785184Sek110237 { 4795184Sek110237 iterp->listp = listp; 4805184Sek110237 iterp->nlistpp = nlistpp; 4815184Sek110237 } 4825184Sek110237 4835184Sek110237 /* 4845184Sek110237 * Reset index pointer to point to list head 4855184Sek110237 */ 4865184Sek110237 static void 4875184Sek110237 common_reset(ac_iter_t *iterp) 4885184Sek110237 { 4895184Sek110237 iterp->curp = iterp->listp; 4905184Sek110237 } 4915184Sek110237 4925184Sek110237 /* 4935184Sek110237 * Walk through an array of ac_tokval_t structures and return string 4945184Sek110237 * of each item. 4955184Sek110237 */ 4965184Sek110237 static const char * 4975184Sek110237 get_next_tokval(ac_iter_t *iterp) 4985184Sek110237 { 4995184Sek110237 ac_tokval_t *listp = iterp->listp; /* list head */ 5005184Sek110237 ac_tokval_t *curp = iterp->curp; /* index pointer */ 5015184Sek110237 /* user passed variable for returning value list for next token */ 5025184Sek110237 ac_tvlist_t **nlistpp = iterp->nlistpp; 5035184Sek110237 const char *p; 5045184Sek110237 5055184Sek110237 if (listp == NULL || curp == NULL) 5065184Sek110237 return (NULL); 5075184Sek110237 5085184Sek110237 /* get the current item's string */ 5095184Sek110237 p = curp->str; 5105184Sek110237 5115184Sek110237 /* 5125184Sek110237 * save the current item's address into a user passed variable 5135184Sek110237 */ 5145184Sek110237 if (nlistpp != NULL) 5155184Sek110237 *nlistpp = curp->nlistp; 5165184Sek110237 5175184Sek110237 /* advance the index pointer */ 5185184Sek110237 iterp->curp = ++curp; 5195184Sek110237 5205184Sek110237 return (p); 5215184Sek110237 } 5225184Sek110237 5235184Sek110237 /* 5246391Saw148015 * Bind the iterator to filebench_shm->shm_var_list 5255184Sek110237 */ 5266391Saw148015 /* ARGSUSED */ 5275184Sek110237 static void 5285184Sek110237 varname_bind(ac_iter_t *iterp, void *listp, void * nlistpp) 5295184Sek110237 { 5306391Saw148015 iterp->listp = filebench_shm->shm_var_list; 5315184Sek110237 iterp->nlistpp = nlistpp; 5325184Sek110237 } 5335184Sek110237 5345184Sek110237 /* 5355184Sek110237 * Walk through a linked list of var_t type structures and return name 5365184Sek110237 * of each variable with a preceding '$' sign 5375184Sek110237 */ 5385184Sek110237 static const char * 5395184Sek110237 get_next_lvarname(ac_iter_t *iterp) 5405184Sek110237 { 5415184Sek110237 static char buf[VARNAME_MAXLEN]; 5425184Sek110237 5435184Sek110237 var_t *listp = iterp->listp; /* list head */ 5445184Sek110237 var_t *curp = iterp->curp; /* index pointer */ 5455184Sek110237 /* User passed variable for returning value list for next token */ 5465184Sek110237 ac_tvlist_t **nlistpp = iterp->nlistpp; 5475184Sek110237 const char *p; 5485184Sek110237 5495184Sek110237 if (listp == NULL || curp == NULL) 5505184Sek110237 return (NULL); 5515184Sek110237 5525184Sek110237 /* Get current variable's name, copy it to buf, with a '$' prefix */ 5535184Sek110237 p = curp->var_name; 5545184Sek110237 (void) snprintf(buf, sizeof (buf), "$%s", p); 5555184Sek110237 5565184Sek110237 /* No information for the next input string */ 5575184Sek110237 if (nlistpp != NULL) 5585184Sek110237 *nlistpp = NULL; 5595184Sek110237 5605184Sek110237 /* Advance the index pointer */ 5615184Sek110237 iterp->curp = curp->var_next; 5625184Sek110237 5635184Sek110237 return (buf); 5645184Sek110237 } 5655184Sek110237 5665184Sek110237 /* 5675184Sek110237 * Walk through a linked list of var_t type structures and return name 5685184Sek110237 * of each variable 5695184Sek110237 */ 5705184Sek110237 static const char * 5715184Sek110237 get_next_rvarname(ac_iter_t *iterp) 5725184Sek110237 { 5735184Sek110237 var_t *listp = iterp->listp; /* list head */ 5745184Sek110237 var_t *curp = iterp->curp; /* index pointer */ 5755184Sek110237 /* User passed variable for returning value list for next item */ 5765184Sek110237 ac_tvlist_t **nlistpp = iterp->nlistpp; 5775184Sek110237 const char *p; 5785184Sek110237 5795184Sek110237 if (listp == NULL || curp == NULL) 5805184Sek110237 return (NULL); 5815184Sek110237 5825184Sek110237 /* Get current variable's name */ 5835184Sek110237 p = curp->var_name; 5845184Sek110237 5855184Sek110237 /* No information for the next input string */ 5865184Sek110237 if (nlistpp != NULL) 5875184Sek110237 *nlistpp = NULL; 5885184Sek110237 5895184Sek110237 /* Advance the index pointer */ 5905184Sek110237 iterp->curp = curp->var_next; 5915184Sek110237 5925184Sek110237 return (p); 5935184Sek110237 } 5945184Sek110237 5955184Sek110237 /* 5965184Sek110237 * Bind the iterator to loadnames.fnc_buf, which is an ac_fname_t array 5975184Sek110237 * and contains up-to-date workload file names. The function calls 5985184Sek110237 * search_loadfiles() to update the cache before the binding. 5995184Sek110237 */ 6006391Saw148015 /* ARGSUSED */ 6015184Sek110237 static void 6025184Sek110237 loadfile_bind(ac_iter_t *iterp, void *listp, void * nlistpp) 6035184Sek110237 { 6045184Sek110237 /* Check loadfile name cache, update it if needed */ 6055184Sek110237 (void) search_loadfiles(&loadnames); 6065184Sek110237 6075184Sek110237 iterp->listp = loadnames.fnc_buf; 6085184Sek110237 iterp->nlistpp = nlistpp; 6095184Sek110237 } 6105184Sek110237 6115184Sek110237 /* 6125184Sek110237 * Walk through a string(ac_fname_t, more exactly) array and return each 6135184Sek110237 * string, until a NULL iterm is encountered. 6145184Sek110237 */ 6155184Sek110237 static const char * 6165184Sek110237 get_next_loadfile(ac_iter_t *iterp) 6175184Sek110237 { 6185184Sek110237 ac_fname_t *listp = iterp->listp; /* list head */ 6195184Sek110237 ac_fname_t *curp = iterp->curp; /* index pointer */ 6205184Sek110237 /* User passed variable for returning value list for next item */ 6215184Sek110237 ac_tvlist_t **nlistpp = iterp->nlistpp; 6225184Sek110237 const char *p; 6235184Sek110237 6245184Sek110237 if (listp == NULL || curp == NULL) 6255184Sek110237 return (NULL); 6265184Sek110237 6275184Sek110237 /* 6285184Sek110237 * Get current file name. If an NULL item is encountered, it means 6295184Sek110237 * this is the end of the list. In that case, we need to set p to 6305184Sek110237 * NULL to indicate to the caller that the end of the list is reached. 6315184Sek110237 */ 6325184Sek110237 p = (char *)curp; 6335184Sek110237 if (*p == NULL) 6345184Sek110237 p = NULL; 6355184Sek110237 6365184Sek110237 /* No information for the next input string */ 6375184Sek110237 if (nlistpp != NULL) 6385184Sek110237 *nlistpp = NULL; 6395184Sek110237 6405184Sek110237 /* Advance the index pointer */ 6415184Sek110237 iterp->curp = ++curp; 6425184Sek110237 6435184Sek110237 return (p); 6445184Sek110237 } 6455184Sek110237 6465184Sek110237 /* 6475184Sek110237 * Search for available workload files in workload direcotry and 6485184Sek110237 * update workload name cache. 6495184Sek110237 */ 6505184Sek110237 static int 6515184Sek110237 search_loadfiles(ac_fname_cache_t *fnamecache) 6525184Sek110237 { 6535184Sek110237 DIR *dirp; 6545184Sek110237 struct dirent *fp; 6555184Sek110237 struct stat dstat; 6565184Sek110237 time_t mtime; 6575184Sek110237 ac_fname_t *buf; 6585184Sek110237 int bufsize = MALLOC_STEP; 6595184Sek110237 int len, i; 6605184Sek110237 6615184Sek110237 if (stat(FILEBENCHDIR"/workloads", &dstat) != 0) 6625184Sek110237 return (-1); 6635184Sek110237 mtime = dstat.st_mtime; 6645184Sek110237 6655184Sek110237 /* Return if there is no change since last time */ 6665184Sek110237 if (mtime == fnamecache->fnc_mtime) 6675184Sek110237 return (0); 6685184Sek110237 6695184Sek110237 /* Get loadfile names and cache it */ 6705184Sek110237 if ((buf = malloc(sizeof (ac_fname_t) * bufsize)) == NULL) 6715184Sek110237 return (-1); 6725184Sek110237 if ((dirp = opendir(FILEBENCHDIR"/workloads")) == NULL) 6735184Sek110237 return (-1); 6745184Sek110237 i = 0; 6755184Sek110237 while ((fp = readdir(dirp)) != NULL) { 6765184Sek110237 len = strlen(fp->d_name); 6775184Sek110237 if (len <= 2 || (fp->d_name)[len - 2] != '.' || 6785184Sek110237 (fp->d_name)[len - 1] != 'f') 6795184Sek110237 continue; 6805184Sek110237 6815184Sek110237 if (i == bufsize) { 6825184Sek110237 bufsize += MALLOC_STEP; 6835184Sek110237 if ((buf = realloc(buf, sizeof (ac_fname_t) * 6845184Sek110237 bufsize)) == NULL) 6855184Sek110237 return (-1); 6865184Sek110237 } 6875184Sek110237 6885184Sek110237 (void) snprintf(buf[i], FILENAME_MAXLEN, "%s", fp->d_name); 6895184Sek110237 if (len -2 <= FILENAME_MAXLEN - 1) { 6905184Sek110237 /* Remove .f suffix in file name */ 6915184Sek110237 buf[i][len -2] = NULL; 6925184Sek110237 } 6935184Sek110237 i++; 6945184Sek110237 } 6955184Sek110237 /* Added a NULL iterm as the array's terminator */ 6965184Sek110237 buf[i][0] = NULL; 6975184Sek110237 6985184Sek110237 if (fnamecache->fnc_bufsize != 0) 6995184Sek110237 free(fnamecache->fnc_buf); 7005184Sek110237 fnamecache->fnc_buf = buf; 7015184Sek110237 fnamecache->fnc_bufsize = bufsize; 7025184Sek110237 fnamecache->fnc_mtime = mtime; 7035184Sek110237 7045184Sek110237 return (0); 7055184Sek110237 } 7065184Sek110237 7075184Sek110237 /* 7085184Sek110237 * Parse user input line into a list of blank separated strings, and 7095184Sek110237 * save the result in the passed ac_inputline_t structure. line and word_end 7105184Sek110237 * parameters are passed from libtecla library. line points to user input 7115184Sek110237 * buffer, and word_end is the index of the last character of user input. 7125184Sek110237 */ 7136391Saw148015 /* ARGSUSED */ 7145184Sek110237 static void 7155184Sek110237 parse_user_input(const char *line, int word_end, ac_inputline_t *input) 7165184Sek110237 { 7175184Sek110237 const char *p = line; 7185184Sek110237 int i; 7195184Sek110237 7205184Sek110237 /* Reset all fileds */ 7215184Sek110237 for (i = 0; i < STR_NUM; i++) { 7225184Sek110237 input->strs[i].startp = NULL; 7235184Sek110237 input->strs[i].endp = NULL; 7245184Sek110237 input->strs[i].strtype = STRTYPE_INVALID; 7255184Sek110237 } 7265184Sek110237 7275184Sek110237 /* 7285184Sek110237 * Parse user input. We don't use word_end to do boundary checking, 7295184Sek110237 * instead we take advantage of the fact that the passed line 7305184Sek110237 * parameter is always terminated by '\0'. 7315184Sek110237 */ 7325184Sek110237 for (i = 0; i < STR_NUM; i++) { 7335184Sek110237 /* Skip leading blank spaces */ 7345184Sek110237 while (*p == ' ') 7355184Sek110237 p++; 7365184Sek110237 7375184Sek110237 if (*p == NULL) { 7385184Sek110237 /* 7395184Sek110237 * User input nothing for the string being input 7405184Sek110237 * before he pressed TAB. We use STR_NULL flag 7415184Sek110237 * to indicate this so that match_str() will list 7425184Sek110237 * all available candidates. 7435184Sek110237 */ 7445184Sek110237 input->strs[i].startp = p; 7455184Sek110237 input->strs[i].strtype = STRTYPE_NULL; 7465184Sek110237 return; 7475184Sek110237 } 7485184Sek110237 7495184Sek110237 /* Recoard the start and end of the string */ 7505184Sek110237 input->strs[i].startp = p; 7515184Sek110237 while ((*p != ' ') && (*p != NULL)) 7525184Sek110237 p++; 7535184Sek110237 input->strs[i].endp = p - 1; 7545184Sek110237 7555184Sek110237 if (*p == NULL) { 7565184Sek110237 input->strs[i].strtype = STRTYPE_INCOMPLETE; 7575184Sek110237 return; 7585184Sek110237 } else { 7595184Sek110237 /* The string is followed by a blank space */ 7605184Sek110237 input->strs[i].strtype = STRTYPE_COMPLETE; 7615184Sek110237 } 7625184Sek110237 } 7635184Sek110237 } 7645184Sek110237 7655184Sek110237 /* 7665184Sek110237 * Parse an input string which is an attribue list, get the current word 7675184Sek110237 * user wants to complete, and return its token type. 7685184Sek110237 * 7695184Sek110237 * An atribute list has the following format: 7705184Sek110237 * 7715184Sek110237 * name1=val,name2=$var,... 7725184Sek110237 * 7735184Sek110237 * The function modifies the passed acstr string on success to point to 7745184Sek110237 * the word being completed. 7755184Sek110237 */ 7765184Sek110237 static ac_toktype_t 7775184Sek110237 parse_attr_list(ac_str_t *acstr) 7785184Sek110237 { 7795184Sek110237 const char *p; 7805184Sek110237 7815184Sek110237 if (acstr->strtype == STRTYPE_COMPLETE) { 7825184Sek110237 /* 7835184Sek110237 * User has input a complete string for attribute list 7845184Sek110237 * return TOKTYPE_NULL to abort the matching. 7855184Sek110237 */ 7865184Sek110237 return (TOKTYPE_ATTRLIST); 7875184Sek110237 } else if (acstr->strtype == STRTYPE_NULL) { 7885184Sek110237 /* 7895184Sek110237 * User haven't input anything for the attribute list, 7905184Sek110237 * he must be trying to list all attribute names. 7915184Sek110237 */ 7925184Sek110237 return (TOKTYPE_ATTRNAME); 7935184Sek110237 } 7945184Sek110237 7955184Sek110237 /* 7965184Sek110237 * The string may contain multiple comma separated "name=value" 7975184Sek110237 * items. Try to find the last one and move startp to point to it. 7985184Sek110237 */ 7995184Sek110237 for (p = acstr->endp; p >= acstr->startp && *p != ATTR_LIST_SEP; p--) {} 8005184Sek110237 8015184Sek110237 if (p == acstr->endp) { 8025184Sek110237 /* 8035184Sek110237 * The last character of the string is ',', which means 8045184Sek110237 * user is trying to list all attribute names. 8055184Sek110237 */ 8065184Sek110237 acstr->startp = p + 1; 8075184Sek110237 acstr->strtype = STRTYPE_NULL; 8085184Sek110237 return (TOKTYPE_ATTRNAME); 8095184Sek110237 } else if (p > acstr->startp) { 8105184Sek110237 /* 8115184Sek110237 * Found ',' between starp and endp, move startp pointer 8125184Sek110237 * to point to the last item. 8135184Sek110237 */ 8145184Sek110237 acstr->startp = p + 1; 8155184Sek110237 } 8165184Sek110237 8175184Sek110237 /* 8185184Sek110237 * Now startp points to the last "name=value" item. Search in 8195184Sek110237 * the characters user has input for this item: 8205184Sek110237 * 8215184Sek110237 * a) if there isn't '=' character, user is inputting attribute name 8225184Sek110237 * b) if there is a '=' character and it is followed by a '$', 8235184Sek110237 * user is inputting variable name 8245184Sek110237 * c) if there is a '=' character and it isn't followed by a '$', 8255184Sek110237 * user is inputting a literal string as attribute value. 8265184Sek110237 */ 8275184Sek110237 for (p = acstr->startp; p <= acstr->endp; p++) { 8285184Sek110237 if (*p == ATTR_ASSIGN_OP) { 8295184Sek110237 /* Found "=" operator in the string */ 8305184Sek110237 if (*(p + 1) == VAR_PREFIX) { 8315184Sek110237 acstr->startp = p + 2; 8325184Sek110237 if (*acstr->startp != NULL) 8335184Sek110237 acstr->strtype = STRTYPE_INCOMPLETE; 8345184Sek110237 else 8355184Sek110237 acstr->strtype = STRTYPE_NULL; 8365184Sek110237 return (TOKTYPE_RVARNAME); 8375184Sek110237 } else { 8385184Sek110237 return (TOKTYPE_ATTRVAL); 8395184Sek110237 } 8405184Sek110237 } 8415184Sek110237 } 8425184Sek110237 8435184Sek110237 /* Didn't find '=' operator, the string must be an attribute name */ 8445184Sek110237 return (TOKTYPE_ATTRNAME); 8455184Sek110237 } 8465184Sek110237 8475184Sek110237 /* 8485184Sek110237 * Parse an input string which is a variable list, get the current word 8495184Sek110237 * user wants to complete, and return its token type. 8505184Sek110237 * 8515184Sek110237 * A varaible list has the following format: 8525184Sek110237 * 8535184Sek110237 * $varname=value 8545184Sek110237 * 8555184Sek110237 * The function modifies the passed acstr string on success to point to 8565184Sek110237 * the word being completed. 8575184Sek110237 */ 8585184Sek110237 static ac_toktype_t 8595184Sek110237 parse_var_list(ac_str_t *acstr) 8605184Sek110237 { 8615184Sek110237 const char *p; 8625184Sek110237 8635184Sek110237 if (acstr->strtype == STRTYPE_COMPLETE) { 8645184Sek110237 /* 8655184Sek110237 * User has input a complete string for var list 8665184Sek110237 * return TOKTYPE_NULL to abort the matching. 8675184Sek110237 */ 8685184Sek110237 return (TOKTYPE_NULL); 8695184Sek110237 } else if (acstr->strtype == STRTYPE_NULL) { 8705184Sek110237 /* 8715184Sek110237 * User haven't input anything for the attribute list, 8725184Sek110237 * he must be trying to list all available var names. 8735184Sek110237 */ 8745184Sek110237 return (TOKTYPE_LVARNAME); 8755184Sek110237 } 8765184Sek110237 8775184Sek110237 /* 8785184Sek110237 * Search in what user has input: 8795184Sek110237 * 8805184Sek110237 * a) if there isn't a '=' character, user is inputting var name 8815184Sek110237 * b) if there is a '=' character, user is inputting var value 8825184Sek110237 */ 8835184Sek110237 for (p = acstr->startp; p <= acstr->endp; p++) { 8845184Sek110237 if (*p == VAR_ASSIGN_OP) 8855184Sek110237 return (TOKTYPE_VARVAL); 8865184Sek110237 } 8875184Sek110237 8885184Sek110237 /* Didn't find '=' operator, user must be inputting an var name */ 8895184Sek110237 return (TOKTYPE_LVARNAME); 8905184Sek110237 } 8915184Sek110237 8925184Sek110237 /* 8935184Sek110237 * Compare two strings acstr and str. acstr is a string of ac_str_t type, 8945184Sek110237 * str is a normal string. If issub is B_TRUE, the function checks if 8955184Sek110237 * acstr is a sub-string of str, starting from index 0; otherwise it checks 8965184Sek110237 * if acstr and str are exactly the same. 8975184Sek110237 * 8985184Sek110237 * The function returns 0 on success and -1 on failure. When it succeeds, 8995184Sek110237 * it also set restp to point to the rest part of the normal string. 9005184Sek110237 */ 9015184Sek110237 static int 9025184Sek110237 compare_string(ac_str_t *acstr, const char *str, boolean_t issub, 9035184Sek110237 const char **restp) 9045184Sek110237 { 9055184Sek110237 const char *p, *q; 9065184Sek110237 9075184Sek110237 for (p = acstr->startp, q = str; (p <= acstr->endp) && (*q != '\0'); 9085184Sek110237 p++, q++) { 9095184Sek110237 if (*p != *q) 9105184Sek110237 return (-1); 9115184Sek110237 } 9125184Sek110237 9135184Sek110237 if (p == acstr->endp + 1) { 9145184Sek110237 if (*q == '\0' || issub == B_TRUE) { 9155184Sek110237 if (restp != NULL) 9165184Sek110237 *restp = q; 9175184Sek110237 return (0); 9185184Sek110237 } 9195184Sek110237 } 9205184Sek110237 9215184Sek110237 return (-1); 9225184Sek110237 } 9235184Sek110237 9245184Sek110237 /* 9255184Sek110237 * Use the passed iterp iterator to access a list of string values to 9265184Sek110237 * look for those matches with acstr, an user input string to be completed. 9275184Sek110237 * 9285184Sek110237 * cpl, line, work_end, and cont_suffix are parameters needed by 9295184Sek110237 * cpl_add_completion(), which adds matched entries to libtecla. 9305184Sek110237 * 9315184Sek110237 * Since user input line may have multiple strings, the function is 9325184Sek110237 * expected to be called multiple times to match those strings one 9335184Sek110237 * by one until the last one is reached. 9345184Sek110237 * 9355184Sek110237 * The multi-step matching process also means the function should provide 9365184Sek110237 * a way to indicate to the caller whether to continue or abort the 9375184Sek110237 * whole matching process. The function does that with the following 9385184Sek110237 * return values: 9395184Sek110237 * 9405184Sek110237 * MATCH_DONE - the matching for the whole user input is done. This 9415184Sek110237 * can mean either some items are found or none is found. 9425184Sek110237 * In either case, the caller shouldn't continue to 9435184Sek110237 * match the rest strings, either because there is 9445184Sek110237 * no strings left, or because the matching for the 9455184Sek110237 * current string failed so there is no need to check 9465184Sek110237 * further. 9475184Sek110237 * MATCH_CONT - the matching for the current string succeeds, but 9485184Sek110237 * user needs to continue to match the rest strings. 9495184Sek110237 */ 9505184Sek110237 static ac_match_result_t 9515184Sek110237 match_string(WordCompletion *cpl, const char *line, int word_end, 9525184Sek110237 ac_str_t *acstr, ac_iter_t *iterp, const char *cont_suffix) 9535184Sek110237 { 9545184Sek110237 const char *str, *restp; 9555184Sek110237 9565184Sek110237 iterp->reset(iterp); 9575184Sek110237 9585184Sek110237 if (acstr->strtype == STRTYPE_COMPLETE) { 9595184Sek110237 while ((str = iterp->get_nextstr(iterp)) != NULL) { 9605184Sek110237 if (!compare_string(acstr, str, B_FALSE, NULL)) { 9615184Sek110237 /* Continue to check rest strings */ 9625184Sek110237 return (MATCH_CONT); 9635184Sek110237 } 9645184Sek110237 } 9655184Sek110237 } else if (acstr->strtype == STRTYPE_NULL) { 9665184Sek110237 /* User input nothing. List all available strings */ 9675184Sek110237 while ((str = iterp->get_nextstr(iterp)) != NULL) { 9685184Sek110237 (void) cpl_add_completion(cpl, line, 9695184Sek110237 acstr->startp - line, word_end, str, 9705184Sek110237 NULL, cont_suffix); 9715184Sek110237 } 9725184Sek110237 } else if (acstr->strtype == STRTYPE_INCOMPLETE) { 9735184Sek110237 while ((str = iterp->get_nextstr(iterp)) != NULL) { 9745184Sek110237 if (!compare_string(acstr, str, B_TRUE, &restp)) { 9755184Sek110237 /* It matches! Add it. */ 9765184Sek110237 (void) cpl_add_completion(cpl, line, 9775184Sek110237 acstr->startp - line, word_end, restp, 9785184Sek110237 NULL, cont_suffix); 9795184Sek110237 } 9805184Sek110237 } 9815184Sek110237 } 9825184Sek110237 9835184Sek110237 return (MATCH_DONE); 9845184Sek110237 } 9855184Sek110237 9865184Sek110237 /* 9875184Sek110237 * This is the interface between filebench and libtecla for auto- 9885184Sek110237 * completion. It is called by libtecla whenever user initiates a 9895184Sek110237 * auto-completion request(ie., pressing TAB key). 9905184Sek110237 * 9915184Sek110237 * The function calls parse_user_input() to parse user input into 9925184Sek110237 * multiple strings, then it calls match_string() to match each 9935184Sek110237 * string in user input in sequence until either the last string 9945184Sek110237 * is reached and completed or the the matching fails. 9955184Sek110237 */ 9966391Saw148015 /* ARGSUSED */ 9975184Sek110237 CPL_MATCH_FN(command_complete) 9985184Sek110237 { 9995184Sek110237 ac_inputline_t inputline; 10005184Sek110237 ac_tvlist_t *clistp = &fb_cmds, *nlistp; 10015184Sek110237 ac_toktype_t toktype; 10025184Sek110237 ac_iter_t *iterp; 10035184Sek110237 char *cont_suffix; 10045184Sek110237 ac_get_curtok_func_t get_curtok; 10055184Sek110237 int i, ret; 10065184Sek110237 10075184Sek110237 /* Parse user input and save the result in inputline variable. */ 10085184Sek110237 parse_user_input(line, word_end, &inputline); 10095184Sek110237 10105184Sek110237 /* 10115184Sek110237 * Match each string in user input against the proper token's 10125184Sek110237 * value list, and continue the loop until either the last string 10135184Sek110237 * is reached and completed or the matching aborts. 10145184Sek110237 */ 10155184Sek110237 for (i = 0; i < STR_NUM && 10165184Sek110237 inputline.strs[i].strtype != STRTYPE_INVALID && clistp != NULL; 10175184Sek110237 i++) { 10185184Sek110237 toktype = clistp->toktype; 10195184Sek110237 10205184Sek110237 /* 10215184Sek110237 * If the current stirng can contain multiple tokens, modify 10225184Sek110237 * the stirng to point to the word being input and return 10235184Sek110237 * its token type. 10245184Sek110237 */ 10255184Sek110237 get_curtok = token_info[toktype].get_curtok; 10265184Sek110237 if (get_curtok != NULL) 10275184Sek110237 toktype = (*get_curtok)(&inputline.strs[i]); 10285184Sek110237 10295184Sek110237 iterp = token_info[toktype].iter; 10305184Sek110237 cont_suffix = token_info[toktype].cont_suffix; 10315184Sek110237 /* Return if there is no completion info for the token */ 10325184Sek110237 if (iterp == NULL) 10335184Sek110237 break; 10345184Sek110237 10355184Sek110237 iterp->bind(iterp, clistp->vals, &nlistp); 10365184Sek110237 /* Match user string against the token's list */ 10375184Sek110237 ret = match_string(cpl, line, word_end, &inputline.strs[i], 10385184Sek110237 iterp, cont_suffix); 10395184Sek110237 if (ret == MATCH_DONE) 10405184Sek110237 return (0); 10415184Sek110237 clistp = nlistp; 10425184Sek110237 } 10435184Sek110237 10445184Sek110237 return (0); 10455184Sek110237 } 1046