/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include <sys/types.h> #include <dirent.h> #include <strings.h> #include "filebench.h" #include "auto_comp.h" #define VARNAME_MAXLEN 128 #define FILENAME_MAXLEN 128 #define MALLOC_STEP 64 #define CSUF_CMD " " #define CSUF_ARG " " #define CSUF_LVARNAME "=" #define CSUF_RVARNAME "," #define CSUF_ATTRNAME "=" #define ATTR_LIST_SEP ',' #define ATTR_ASSIGN_OP '=' #define VAR_ASSIGN_OP '=' #define VAR_PREFIX '$' typedef char ac_fname_t[FILENAME_MAXLEN]; typedef struct ac_fname_cache { ac_fname_t *fnc_buf; int fnc_bufsize; time_t fnc_mtime; } ac_fname_cache_t; typedef enum ac_match_result { MATCH_DONE, MATCH_CONT } ac_match_result_t; /* * We parse an user input line into multiple blank separated strings. * The last string is always the one user wants to complete, the other * preceding strings set up the context on how to complete the last one. * * ac_str_t repsents one such a string, which can be of the following * types: * * STRTYPE_COMPLETE - the string is one of the preceding strings. * STRTYPE_INCOMPLETE - the string is the one being completed, user * has inputted at least one character for it. * STRTYPE_NULL - the string is the one being completed, user * has inputted nothing for it. * * ac_str_t structure has the following members: * * startp - the start position of the string in the user input buffer * endp - the end position of the string in the user input buffer * strtype - the type of the string. It can be of the following values: * STRTYPE_COMPLETE, STRTYPE_INCOMPLETE, STRTYPE_NULL, * and STRTYPE_INVALID. */ typedef enum ac_strtype { STRTYPE_COMPLETE, STRTYPE_INCOMPLETE, STRTYPE_NULL, STRTYPE_INVALID } ac_strtype_t; typedef struct ac_str { const char *startp; const char *endp; ac_strtype_t strtype; } ac_str_t; #define STR_NUM 3 typedef struct ac_inputline { ac_str_t strs[STR_NUM]; } ac_inputline_t; /* * ac_iter represents a general interface to access a list of values for * matching user input string. The structure has the following methods: * * bind - bind the iterator to a list, and save a pointer to user * passed space in nlistpp. * reset - reset internal index pointer to point to list head. * get_nextstr - this is the method that does the real work. It * walks through the list and returns string associated with * the current item. It can also return some other thing the * caller is interested via the user passed space pointed by * nlistpp. In our case, that is a pointer to a list which * contains all possible values for the next string in user * input. * * It has the following data members: * * listp - a pointer to the list to be iterated through * curp - index pointer to maintain position when iterating * nlistpp - a pointer to user passed space for returning a list of * values for the next string in user input */ typedef struct ac_iter { void *listp; void *curp; void *nlistpp; void (*bind)(struct ac_iter *, void *, void *); void (*reset)(struct ac_iter *); const char *(*get_nextstr)(struct ac_iter *); } ac_iter_t; /* * We consider a filebench command is composed of a sequence of tokens * (ie., command name, argument name, attribute name, etc.). Many of * these tokens have limited string values. These values, as well as * their dependencies, are used to complete user input string. * * There are the following tokens: * * TOKTYPE_CMD - command name * TOKTYPE_ARG - argument name * TOKTYPE_ATTRNAME - attribute name * TOKTYPE_ATTRVAL - attribute value * TOKTYPE_LVARNAME - variable name, used on left side of assign * operator * TOKTYPE_RVARNAME - variable name, used on right side of assign * operator * TOKTYPE_VARVAL - variable value * TOKTYPE_LOADFILE - load file name * TOKTYPE_ATTRLIST - pseudo token type for attribute list * TOKTYPE_VARLIST - pseudo token type for variable list * TOKTYPE_NULL - pseudo token type for aborting auto-completion * * The reason why there are two different token types for variable name * is because, depending on its position, there are different requirements * on how to do completion and display matching results. See more details * in lvarname_iter and rvarname_iter definition. * * Attribute list and variable list are not really a single token. Instead * they contain multiple tokens, and thus have different requirements on * how to complete them. TOKTYPE_ATTRLIST and TOKTYPE_VARLIST are * introduced to to solve this issue. See more details below on * get_curtok() function in ac_tokinfo_t structure. * * ac_tokval_t represents a string value a token can have. The structure * also contains a pointer to a ac_tvlist_t structure, which represents * all possible values for the next token in the same command. * * str - the token's string value * nlistp - a list which contains string values for the token * that follows * * ac_tvlist_t represents all possible values for a token. These values * are stored in an ac_tokval_t array. The structure also has a member * toktype, which is used to index an ac_tokinfo_t array to get the * information on how to access and use the associated value list. * * vals - a list of string values for this token * toktype - the token's type * * ac_tokinfo_t contains information on how to access and use the * string values of a specific token. Among them, the most important * thing is an iterator to access the value list. The reason to use * iterator is to encapsulate list implementation details. That is * necessary because some tokens have dynamic values(for example, * argument of load command), which cannot be predefined using * ac_tokval_t array. * * ac_tokinfo_t structure has the following members: * * toktype - token type * iter - iterator to access the token's value list * cont_suffix - continuation suffix for this token. See note 1 * below on what is continuation suffix. * get_curtok - a function to parse a multi-token user string. * It parse that string and returns the word being * completed and its token type. See note 2 below. * * Notes: * * 1) Continuation suffix is a convenient feature provided by libtecla. * A continuation suffix is a string which is automatically appended * to a fully completed string. For example, if a command name is * fully completed, a blank space will be appended to it. This is * very convenient because it not only saves typing, but also gives * user an indication to continue. * * 2) get_curtok() function is a trick to support user input strings * which have multiple tokens. Take attribute list as an example, * although we defined a token type TOKTYPE_ATTRLIST for it, it is * not a token actually, instead it contains multiple tokens like * attribute name, attribute value, etc., and attribute value can * be either a literal string or a variable name prefixed with a * '$' sign. For this reason, get_curtok() function is needed to * parse that string to get the word being completed and its token * type so that we can match the word against with the proper value * list. */ typedef enum ac_toktype { TOKTYPE_CMD, TOKTYPE_ARG, TOKTYPE_ATTRNAME, TOKTYPE_ATTRVAL, TOKTYPE_LVARNAME, TOKTYPE_RVARNAME, TOKTYPE_VARVAL, TOKTYPE_LOADFILE, TOKTYPE_ATTRLIST, TOKTYPE_VARLIST, TOKTYPE_NULL } ac_toktype_t; typedef ac_toktype_t (*ac_get_curtok_func_t)(ac_str_t *); typedef struct ac_tokinfo { ac_toktype_t toktype; ac_iter_t *iter; char *cont_suffix; ac_get_curtok_func_t get_curtok; } ac_tokinfo_t; typedef struct ac_tokval { char *str; struct ac_tvlist *nlistp; } ac_tokval_t; typedef struct ac_tvlist { ac_tokval_t *vals; ac_toktype_t toktype; } ac_tvlist_t; /* * Variables and prototypes */ static void common_bind(ac_iter_t *, void *, void *); static void common_reset(ac_iter_t *); static void varname_bind(ac_iter_t *, void *, void *); static void loadfile_bind(ac_iter_t *, void *, void *); static const char *get_next_tokval(ac_iter_t *); static const char *get_next_lvarname(ac_iter_t *); static const char *get_next_rvarname(ac_iter_t *); static const char *get_next_loadfile(ac_iter_t *); static ac_toktype_t parse_attr_list(ac_str_t *); static ac_toktype_t parse_var_list(ac_str_t *); static ac_iter_t tokval_iter = { NULL, NULL, NULL, common_bind, common_reset, get_next_tokval }; static ac_iter_t lvarname_iter = { NULL, NULL, NULL, varname_bind, common_reset, get_next_lvarname }; static ac_iter_t rvarname_iter = { NULL, NULL, NULL, varname_bind, common_reset, get_next_rvarname }; static ac_iter_t loadfile_iter = { NULL, NULL, NULL, loadfile_bind, common_reset, get_next_loadfile }; /* * Note: We use toktype to index into this array, so for each toktype, * there must be one element in the array, and in the same order * as that toktype is defined in ac_toktype. */ static ac_tokinfo_t token_info[] = { { TOKTYPE_CMD, &tokval_iter, CSUF_CMD, NULL }, { TOKTYPE_ARG, &tokval_iter, CSUF_ARG, NULL }, { TOKTYPE_ATTRNAME, &tokval_iter, CSUF_ATTRNAME, NULL }, { TOKTYPE_ATTRVAL, NULL, NULL, NULL }, { TOKTYPE_LVARNAME, &lvarname_iter, CSUF_LVARNAME, NULL }, { TOKTYPE_RVARNAME, &rvarname_iter, CSUF_RVARNAME, NULL }, { TOKTYPE_VARVAL, NULL, NULL, NULL }, { TOKTYPE_LOADFILE, &loadfile_iter, CSUF_ARG, NULL }, { TOKTYPE_ATTRLIST, NULL, NULL, parse_attr_list }, { TOKTYPE_VARLIST, NULL, NULL, parse_var_list }, { TOKTYPE_NULL, NULL, NULL, NULL } }; static ac_tokval_t event_attrnames[] = { { "rate", NULL}, { NULL, NULL} }; static ac_tvlist_t event_attrs = { event_attrnames, TOKTYPE_ATTRLIST }; static ac_tokval_t file_attrnames[] = { { "path", NULL }, { "reuse", NULL }, { "prealloc", NULL }, { "paralloc", NULL }, { NULL, NULL } }; static ac_tvlist_t file_attrs = { file_attrnames, TOKTYPE_ATTRLIST }; static ac_tokval_t fileset_attrnames[] = { { "size", NULL }, { "path", NULL }, { "dirwidth", NULL }, { "prealloc", NULL }, { "filesizegamma", NULL }, { "dirgamma", NULL }, { "cached", NULL }, { "entries", NULL }, { NULL, NULL } }; static ac_tvlist_t fileset_attrs = { fileset_attrnames, TOKTYPE_ATTRLIST }; static ac_tokval_t process_attrnames[] = { { "nice", NULL }, { "instances", NULL }, { NULL, NULL } }; static ac_tvlist_t process_attrs = { process_attrnames, TOKTYPE_ATTRLIST }; static ac_tokval_t create_argnames[] = { { "file", NULL }, { "fileset", NULL }, { "process", NULL }, { NULL, NULL } }; static ac_tvlist_t create_args = { create_argnames, TOKTYPE_ARG }; static ac_tokval_t define_argnames[] = { { "file", &file_attrs }, { "fileset", &fileset_attrs }, { "process", &process_attrs }, { NULL, NULL } }; static ac_tvlist_t define_args = { define_argnames, TOKTYPE_ARG }; static ac_tvlist_t load_args = { NULL, TOKTYPE_LOADFILE }; static ac_tvlist_t set_args = { NULL, TOKTYPE_VARLIST }; static ac_tokval_t shutdown_argnames[] = { { "process", NULL }, { NULL, NULL } }; static ac_tvlist_t shutdown_args = { shutdown_argnames, TOKTYPE_ARG }; static ac_tokval_t stats_argnames[] = { { "clear", NULL }, { "directory", NULL }, { "command", NULL }, { "dump", NULL }, { "xmldump", NULL }, { NULL, NULL } }; static ac_tvlist_t stats_args = { stats_argnames, TOKTYPE_ARG }; static ac_tokval_t fb_cmdnames[] = { { "create", &create_args }, { "define", &define_args }, { "debug", NULL }, { "echo", NULL }, { "eventgen", &event_attrs }, { "foreach", NULL }, { "help", NULL }, { "list", NULL }, { "load", &load_args }, { "log", NULL }, { "quit", NULL }, { "run", NULL }, { "set", &set_args }, { "shutdown", &shutdown_args }, { "sleep", NULL }, { "stats", &stats_args }, { "system", NULL }, { "usage", NULL }, { "vars", NULL }, { "version", NULL }, { NULL, NULL }, }; static ac_tvlist_t fb_cmds = { fb_cmdnames, TOKTYPE_CMD }; static ac_fname_cache_t loadnames = { NULL, 0, 0 }; static int search_loadfiles(ac_fname_cache_t *); static void parse_user_input(const char *, int, ac_inputline_t *); static int compare_string(ac_str_t *, const char *, boolean_t, const char **); static ac_match_result_t match_string(WordCompletion *, const char *, int, ac_str_t *, ac_iter_t *, const char *); /* * Bind the iterator to the passed list */ static void common_bind(ac_iter_t *iterp, void *listp, void *nlistpp) { iterp->listp = listp; iterp->nlistpp = nlistpp; } /* * Reset index pointer to point to list head */ static void common_reset(ac_iter_t *iterp) { iterp->curp = iterp->listp; } /* * Walk through an array of ac_tokval_t structures and return string * of each item. */ static const char * get_next_tokval(ac_iter_t *iterp) { ac_tokval_t *listp = iterp->listp; /* list head */ ac_tokval_t *curp = iterp->curp; /* index pointer */ /* user passed variable for returning value list for next token */ ac_tvlist_t **nlistpp = iterp->nlistpp; const char *p; if (listp == NULL || curp == NULL) return (NULL); /* get the current item's string */ p = curp->str; /* * save the current item's address into a user passed variable */ if (nlistpp != NULL) *nlistpp = curp->nlistp; /* advance the index pointer */ iterp->curp = ++curp; return (p); } /* * Bind the iterator to filebench_shm->shm_var_list */ /* ARGSUSED */ static void varname_bind(ac_iter_t *iterp, void *listp, void * nlistpp) { iterp->listp = filebench_shm->shm_var_list; iterp->nlistpp = nlistpp; } /* * Walk through a linked list of var_t type structures and return name * of each variable with a preceding '$' sign */ static const char * get_next_lvarname(ac_iter_t *iterp) { static char buf[VARNAME_MAXLEN]; var_t *listp = iterp->listp; /* list head */ var_t *curp = iterp->curp; /* index pointer */ /* User passed variable for returning value list for next token */ ac_tvlist_t **nlistpp = iterp->nlistpp; const char *p; if (listp == NULL || curp == NULL) return (NULL); /* Get current variable's name, copy it to buf, with a '$' prefix */ p = curp->var_name; (void) snprintf(buf, sizeof (buf), "$%s", p); /* No information for the next input string */ if (nlistpp != NULL) *nlistpp = NULL; /* Advance the index pointer */ iterp->curp = curp->var_next; return (buf); } /* * Walk through a linked list of var_t type structures and return name * of each variable */ static const char * get_next_rvarname(ac_iter_t *iterp) { var_t *listp = iterp->listp; /* list head */ var_t *curp = iterp->curp; /* index pointer */ /* User passed variable for returning value list for next item */ ac_tvlist_t **nlistpp = iterp->nlistpp; const char *p; if (listp == NULL || curp == NULL) return (NULL); /* Get current variable's name */ p = curp->var_name; /* No information for the next input string */ if (nlistpp != NULL) *nlistpp = NULL; /* Advance the index pointer */ iterp->curp = curp->var_next; return (p); } /* * Bind the iterator to loadnames.fnc_buf, which is an ac_fname_t array * and contains up-to-date workload file names. The function calls * search_loadfiles() to update the cache before the binding. */ /* ARGSUSED */ static void loadfile_bind(ac_iter_t *iterp, void *listp, void * nlistpp) { /* Check loadfile name cache, update it if needed */ (void) search_loadfiles(&loadnames); iterp->listp = loadnames.fnc_buf; iterp->nlistpp = nlistpp; } /* * Walk through a string(ac_fname_t, more exactly) array and return each * string, until a NULL iterm is encountered. */ static const char * get_next_loadfile(ac_iter_t *iterp) { ac_fname_t *listp = iterp->listp; /* list head */ ac_fname_t *curp = iterp->curp; /* index pointer */ /* User passed variable for returning value list for next item */ ac_tvlist_t **nlistpp = iterp->nlistpp; const char *p; if (listp == NULL || curp == NULL) return (NULL); /* * Get current file name. If an NULL item is encountered, it means * this is the end of the list. In that case, we need to set p to * NULL to indicate to the caller that the end of the list is reached. */ p = (char *)curp; if (*p == NULL) p = NULL; /* No information for the next input string */ if (nlistpp != NULL) *nlistpp = NULL; /* Advance the index pointer */ iterp->curp = ++curp; return (p); } /* * Search for available workload files in workload direcotry and * update workload name cache. */ static int search_loadfiles(ac_fname_cache_t *fnamecache) { DIR *dirp; struct dirent *fp; struct stat dstat; time_t mtime; ac_fname_t *buf; int bufsize = MALLOC_STEP; int len, i; if (stat(FILEBENCHDIR"/workloads", &dstat) != 0) return (-1); mtime = dstat.st_mtime; /* Return if there is no change since last time */ if (mtime == fnamecache->fnc_mtime) return (0); /* Get loadfile names and cache it */ if ((buf = malloc(sizeof (ac_fname_t) * bufsize)) == NULL) return (-1); if ((dirp = opendir(FILEBENCHDIR"/workloads")) == NULL) return (-1); i = 0; while ((fp = readdir(dirp)) != NULL) { len = strlen(fp->d_name); if (len <= 2 || (fp->d_name)[len - 2] != '.' || (fp->d_name)[len - 1] != 'f') continue; if (i == bufsize) { bufsize += MALLOC_STEP; if ((buf = realloc(buf, sizeof (ac_fname_t) * bufsize)) == NULL) return (-1); } (void) snprintf(buf[i], FILENAME_MAXLEN, "%s", fp->d_name); if (len -2 <= FILENAME_MAXLEN - 1) { /* Remove .f suffix in file name */ buf[i][len -2] = NULL; } i++; } /* Added a NULL iterm as the array's terminator */ buf[i][0] = NULL; if (fnamecache->fnc_bufsize != 0) free(fnamecache->fnc_buf); fnamecache->fnc_buf = buf; fnamecache->fnc_bufsize = bufsize; fnamecache->fnc_mtime = mtime; return (0); } /* * Parse user input line into a list of blank separated strings, and * save the result in the passed ac_inputline_t structure. line and word_end * parameters are passed from libtecla library. line points to user input * buffer, and word_end is the index of the last character of user input. */ /* ARGSUSED */ static void parse_user_input(const char *line, int word_end, ac_inputline_t *input) { const char *p = line; int i; /* Reset all fileds */ for (i = 0; i < STR_NUM; i++) { input->strs[i].startp = NULL; input->strs[i].endp = NULL; input->strs[i].strtype = STRTYPE_INVALID; } /* * Parse user input. We don't use word_end to do boundary checking, * instead we take advantage of the fact that the passed line * parameter is always terminated by '\0'. */ for (i = 0; i < STR_NUM; i++) { /* Skip leading blank spaces */ while (*p == ' ') p++; if (*p == NULL) { /* * User input nothing for the string being input * before he pressed TAB. We use STR_NULL flag * to indicate this so that match_str() will list * all available candidates. */ input->strs[i].startp = p; input->strs[i].strtype = STRTYPE_NULL; return; } /* Recoard the start and end of the string */ input->strs[i].startp = p; while ((*p != ' ') && (*p != NULL)) p++; input->strs[i].endp = p - 1; if (*p == NULL) { input->strs[i].strtype = STRTYPE_INCOMPLETE; return; } else { /* The string is followed by a blank space */ input->strs[i].strtype = STRTYPE_COMPLETE; } } } /* * Parse an input string which is an attribue list, get the current word * user wants to complete, and return its token type. * * An atribute list has the following format: * * name1=val,name2=$var,... * * The function modifies the passed acstr string on success to point to * the word being completed. */ static ac_toktype_t parse_attr_list(ac_str_t *acstr) { const char *p; if (acstr->strtype == STRTYPE_COMPLETE) { /* * User has input a complete string for attribute list * return TOKTYPE_NULL to abort the matching. */ return (TOKTYPE_ATTRLIST); } else if (acstr->strtype == STRTYPE_NULL) { /* * User haven't input anything for the attribute list, * he must be trying to list all attribute names. */ return (TOKTYPE_ATTRNAME); } /* * The string may contain multiple comma separated "name=value" * items. Try to find the last one and move startp to point to it. */ for (p = acstr->endp; p >= acstr->startp && *p != ATTR_LIST_SEP; p--) {} if (p == acstr->endp) { /* * The last character of the string is ',', which means * user is trying to list all attribute names. */ acstr->startp = p + 1; acstr->strtype = STRTYPE_NULL; return (TOKTYPE_ATTRNAME); } else if (p > acstr->startp) { /* * Found ',' between starp and endp, move startp pointer * to point to the last item. */ acstr->startp = p + 1; } /* * Now startp points to the last "name=value" item. Search in * the characters user has input for this item: * * a) if there isn't '=' character, user is inputting attribute name * b) if there is a '=' character and it is followed by a '$', * user is inputting variable name * c) if there is a '=' character and it isn't followed by a '$', * user is inputting a literal string as attribute value. */ for (p = acstr->startp; p <= acstr->endp; p++) { if (*p == ATTR_ASSIGN_OP) { /* Found "=" operator in the string */ if (*(p + 1) == VAR_PREFIX) { acstr->startp = p + 2; if (*acstr->startp != NULL) acstr->strtype = STRTYPE_INCOMPLETE; else acstr->strtype = STRTYPE_NULL; return (TOKTYPE_RVARNAME); } else { return (TOKTYPE_ATTRVAL); } } } /* Didn't find '=' operator, the string must be an attribute name */ return (TOKTYPE_ATTRNAME); } /* * Parse an input string which is a variable list, get the current word * user wants to complete, and return its token type. * * A varaible list has the following format: * * $varname=value * * The function modifies the passed acstr string on success to point to * the word being completed. */ static ac_toktype_t parse_var_list(ac_str_t *acstr) { const char *p; if (acstr->strtype == STRTYPE_COMPLETE) { /* * User has input a complete string for var list * return TOKTYPE_NULL to abort the matching. */ return (TOKTYPE_NULL); } else if (acstr->strtype == STRTYPE_NULL) { /* * User haven't input anything for the attribute list, * he must be trying to list all available var names. */ return (TOKTYPE_LVARNAME); } /* * Search in what user has input: * * a) if there isn't a '=' character, user is inputting var name * b) if there is a '=' character, user is inputting var value */ for (p = acstr->startp; p <= acstr->endp; p++) { if (*p == VAR_ASSIGN_OP) return (TOKTYPE_VARVAL); } /* Didn't find '=' operator, user must be inputting an var name */ return (TOKTYPE_LVARNAME); } /* * Compare two strings acstr and str. acstr is a string of ac_str_t type, * str is a normal string. If issub is B_TRUE, the function checks if * acstr is a sub-string of str, starting from index 0; otherwise it checks * if acstr and str are exactly the same. * * The function returns 0 on success and -1 on failure. When it succeeds, * it also set restp to point to the rest part of the normal string. */ static int compare_string(ac_str_t *acstr, const char *str, boolean_t issub, const char **restp) { const char *p, *q; for (p = acstr->startp, q = str; (p <= acstr->endp) && (*q != '\0'); p++, q++) { if (*p != *q) return (-1); } if (p == acstr->endp + 1) { if (*q == '\0' || issub == B_TRUE) { if (restp != NULL) *restp = q; return (0); } } return (-1); } /* * Use the passed iterp iterator to access a list of string values to * look for those matches with acstr, an user input string to be completed. * * cpl, line, work_end, and cont_suffix are parameters needed by * cpl_add_completion(), which adds matched entries to libtecla. * * Since user input line may have multiple strings, the function is * expected to be called multiple times to match those strings one * by one until the last one is reached. * * The multi-step matching process also means the function should provide * a way to indicate to the caller whether to continue or abort the * whole matching process. The function does that with the following * return values: * * MATCH_DONE - the matching for the whole user input is done. This * can mean either some items are found or none is found. * In either case, the caller shouldn't continue to * match the rest strings, either because there is * no strings left, or because the matching for the * current string failed so there is no need to check * further. * MATCH_CONT - the matching for the current string succeeds, but * user needs to continue to match the rest strings. */ 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) { const char *str, *restp; iterp->reset(iterp); if (acstr->strtype == STRTYPE_COMPLETE) { while ((str = iterp->get_nextstr(iterp)) != NULL) { if (!compare_string(acstr, str, B_FALSE, NULL)) { /* Continue to check rest strings */ return (MATCH_CONT); } } } else if (acstr->strtype == STRTYPE_NULL) { /* User input nothing. List all available strings */ while ((str = iterp->get_nextstr(iterp)) != NULL) { (void) cpl_add_completion(cpl, line, acstr->startp - line, word_end, str, NULL, cont_suffix); } } else if (acstr->strtype == STRTYPE_INCOMPLETE) { while ((str = iterp->get_nextstr(iterp)) != NULL) { if (!compare_string(acstr, str, B_TRUE, &restp)) { /* It matches! Add it. */ (void) cpl_add_completion(cpl, line, acstr->startp - line, word_end, restp, NULL, cont_suffix); } } } return (MATCH_DONE); } /* * This is the interface between filebench and libtecla for auto- * completion. It is called by libtecla whenever user initiates a * auto-completion request(ie., pressing TAB key). * * The function calls parse_user_input() to parse user input into * multiple strings, then it calls match_string() to match each * string in user input in sequence until either the last string * is reached and completed or the the matching fails. */ /* ARGSUSED */ CPL_MATCH_FN(command_complete) { ac_inputline_t inputline; ac_tvlist_t *clistp = &fb_cmds, *nlistp; ac_toktype_t toktype; ac_iter_t *iterp; char *cont_suffix; ac_get_curtok_func_t get_curtok; int i, ret; /* Parse user input and save the result in inputline variable. */ parse_user_input(line, word_end, &inputline); /* * Match each string in user input against the proper token's * value list, and continue the loop until either the last string * is reached and completed or the matching aborts. */ for (i = 0; i < STR_NUM && inputline.strs[i].strtype != STRTYPE_INVALID && clistp != NULL; i++) { toktype = clistp->toktype; /* * If the current stirng can contain multiple tokens, modify * the stirng to point to the word being input and return * its token type. */ get_curtok = token_info[toktype].get_curtok; if (get_curtok != NULL) toktype = (*get_curtok)(&inputline.strs[i]); iterp = token_info[toktype].iter; cont_suffix = token_info[toktype].cont_suffix; /* Return if there is no completion info for the token */ if (iterp == NULL) break; iterp->bind(iterp, clistp->vals, &nlistp); /* Match user string against the token's list */ ret = match_string(cpl, line, word_end, &inputline.strs[i], iterp, cont_suffix); if (ret == MATCH_DONE) return (0); clistp = nlistp; } return (0); }