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