xref: /onnv-gate/usr/src/cmd/filebench/common/auto_comp.c (revision 5184:da60d2b4a9e2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <dirent.h>
30 #include <strings.h>
31 #include "filebench.h"
32 #include "auto_comp.h"
33 
34 #define	VARNAME_MAXLEN	128
35 #define	FILENAME_MAXLEN	128
36 #define	MALLOC_STEP	64
37 
38 #define	CSUF_CMD	" "
39 #define	CSUF_ARG	" "
40 #define	CSUF_LVARNAME	"="
41 #define	CSUF_RVARNAME	","
42 #define	CSUF_ATTRNAME	"="
43 
44 #define	ATTR_LIST_SEP	','
45 #define	ATTR_ASSIGN_OP	'='
46 #define	VAR_ASSIGN_OP	'='
47 #define	VAR_PREFIX	'$'
48 
49 #ifndef HAVE_BOOLEAN_T
50 typedef enum { B_FALSE, B_TRUE } boolean_t;
51 #endif
52 
53 typedef char ac_fname_t[FILENAME_MAXLEN];
54 
55 typedef struct ac_fname_cache {
56 	ac_fname_t	*fnc_buf;
57 	int		fnc_bufsize;
58 	time_t		fnc_mtime;
59 } ac_fname_cache_t;
60 
61 typedef enum ac_match_result {
62 	MATCH_DONE,
63 	MATCH_CONT
64 } ac_match_result_t;
65 
66 /*
67  * We parse an user input line into multiple blank separated strings.
68  * The last string is always the one user wants to complete, the other
69  * preceding strings set up the context on how to complete the last one.
70  *
71  * ac_str_t repsents one such a string, which can be of the following
72  * types:
73  *
74  *	STRTYPE_COMPLETE   - the string is one of the preceding strings.
75  *	STRTYPE_INCOMPLETE - the string is the one being completed, user
76  *			     has inputted at least one character for it.
77  *	STRTYPE_NULL       - the string is the one being completed, user
78  *			     has inputted nothing for it.
79  *
80  * ac_str_t structure has the following members:
81  *
82  * 	startp	- the start position of the string in the user input buffer
83  * 	endp	- the end position of the string in the user input buffer
84  *	strtype	- the type of the string. It can be of the following values:
85  *		  STRTYPE_COMPLETE, STRTYPE_INCOMPLETE, STRTYPE_NULL,
86  *		  and STRTYPE_INVALID.
87  */
88 
89 typedef enum ac_strtype {
90 	STRTYPE_COMPLETE,
91 	STRTYPE_INCOMPLETE,
92 	STRTYPE_NULL,
93 	STRTYPE_INVALID
94 } ac_strtype_t;
95 
96 typedef struct ac_str {
97 	const char *startp;
98 	const char *endp;
99 	ac_strtype_t strtype;
100 } ac_str_t;
101 
102 #define	STR_NUM	3
103 
104 typedef struct ac_inputline {
105 	ac_str_t strs[STR_NUM];
106 } ac_inputline_t;
107 
108 /*
109  * ac_iter represents a general interface to access a list of values for
110  * matching user input string. The structure has the following methods:
111  *
112  *	bind  - bind the iterator to a list, and save a pointer to user
113  *		passed space in nlistpp.
114  *	reset - reset internal index pointer to point to list head.
115  *	get_nextstr - this is the method that does the real work. It
116  *		walks through the list and returns string associated with
117  *		the current item. It can also return some other thing the
118  *		caller is interested via the user passed space pointed by
119  *		nlistpp. In our case, that is a pointer to a list which
120  *		contains all possible values for the next string in user
121  *		input.
122  *
123  * It has the following data members:
124  *
125  *	listp   - a pointer to the list to be iterated through
126  *	curp    - index pointer to maintain position when iterating
127  *	nlistpp - a pointer to user passed space for returning a list of
128  *		  values for the next string in user input
129  */
130 
131 typedef struct ac_iter {
132 	void	*listp;
133 	void	*curp;
134 	void	*nlistpp;
135 	void	(*bind)(struct ac_iter *, void *, void *);
136 	void	(*reset)(struct ac_iter *);
137 	const char   *(*get_nextstr)(struct ac_iter *);
138 } ac_iter_t;
139 
140 /*
141  * We consider a filebench command is composed of a sequence of tokens
142  * (ie., command name, argument name, attribute name, etc.). Many of
143  * these tokens have limited string values. These values, as well as
144  * their dependencies, are used to complete user input string.
145  *
146  * There are the following tokens:
147  *
148  *	TOKTYPE_CMD	 - command name
149  *	TOKTYPE_ARG	 - argument name
150  *	TOKTYPE_ATTRNAME - attribute name
151  *	TOKTYPE_ATTRVAL  - attribute value
152  *	TOKTYPE_LVARNAME - variable name, used on left side of assign
153  *			   operator
154  *	TOKTYPE_RVARNAME - variable name, used on right side of assign
155  *			   operator
156  *	TOKTYPE_VARVAL	 - variable value
157  *	TOKTYPE_LOADFILE - load file name
158  *	TOKTYPE_ATTRLIST - pseudo token type for attribute list
159  *	TOKTYPE_VARLIST  - pseudo token type for variable list
160  *	TOKTYPE_NULL	 - pseudo token type for aborting auto-completion
161  *
162  * The reason why there are two different token types for variable name
163  * is because, depending on its position, there are different requirements
164  * on how to do completion and display matching results. See more details
165  * in lvarname_iter and rvarname_iter definition.
166  *
167  * Attribute list and variable list are not really a single token. Instead
168  * they contain multiple tokens, and thus have different requirements on
169  * how to complete them. TOKTYPE_ATTRLIST and TOKTYPE_VARLIST are
170  * introduced to to solve this issue. See more details below on
171  * get_curtok() function in ac_tokinfo_t structure.
172  *
173  * ac_tokval_t represents a string value a token can have. The structure
174  * also contains a pointer to a ac_tvlist_t structure, which represents
175  * all possible values for the next token in the same command.
176  *
177  *	str    - the token's string value
178  *	nlistp - a list which contains string values for the token
179  *		 that follows
180  *
181  * ac_tvlist_t represents all possible values for a token. These values
182  * are stored in an ac_tokval_t array. The structure also has a member
183  * toktype, which is used to index an ac_tokinfo_t array to get the
184  * information on how to access and use the associated value list.
185  *
186  *	vals	- a list of string values for this token
187  *	toktype	- the token's type
188  *
189  * ac_tokinfo_t contains information on how to access and use the
190  * string values of a specific token. Among them, the most important
191  * thing is an iterator to access the value list. The reason to use
192  * iterator is to encapsulate list implementation details. That is
193  * necessary because some tokens have dynamic values(for example,
194  * argument of load command), which cannot be predefined using
195  * ac_tokval_t array.
196  *
197  * ac_tokinfo_t structure has the following members:
198  *
199  *	toktype	    - token type
200  *	iter	    - iterator to access the token's value list
201  *	cont_suffix - continuation suffix for this token. See note 1
202  *		      below on what is continuation suffix.
203  *	get_curtok  - a function to parse a multi-token user string.
204  *		      It parse that string and returns the word being
205  *		      completed and its token type. See note 2 below.
206  *
207  * Notes:
208  *
209  * 1) Continuation suffix is a convenient feature provided by libtecla.
210  *    A continuation suffix is a string which is automatically appended
211  *    to a fully completed string. For example, if a command name is
212  *    fully completed, a blank space will be appended to it. This is
213  *    very convenient because it not only saves typing, but also gives
214  *    user an indication to continue.
215  *
216  * 2) get_curtok() function is a trick to support user input strings
217  *    which have multiple tokens. Take attribute list as an example,
218  *    although we defined a token type TOKTYPE_ATTRLIST for it, it is
219  *    not a token actually, instead it contains multiple tokens like
220  *    attribute name, attribute value, etc., and attribute value can
221  *    be either a literal string or a variable name prefixed with a
222  *    '$' sign. For this reason, get_curtok() function is needed to
223  *    parse that string to get the word being completed and its token
224  *    type so that we can match the word against with the proper value
225  *    list.
226  */
227 
228 typedef enum ac_toktype {
229 	TOKTYPE_CMD,
230 	TOKTYPE_ARG,
231 	TOKTYPE_ATTRNAME,
232 	TOKTYPE_ATTRVAL,
233 	TOKTYPE_LVARNAME,
234 	TOKTYPE_RVARNAME,
235 	TOKTYPE_VARVAL,
236 	TOKTYPE_LOADFILE,
237 	TOKTYPE_ATTRLIST,
238 	TOKTYPE_VARLIST,
239 	TOKTYPE_NULL
240 } ac_toktype_t;
241 
242 typedef ac_toktype_t (*ac_get_curtok_func_t)(ac_str_t *);
243 
244 typedef struct ac_tokinfo {
245 	ac_toktype_t	toktype;
246 	ac_iter_t	*iter;
247 	char		*cont_suffix;
248 	ac_get_curtok_func_t	get_curtok;
249 } ac_tokinfo_t;
250 
251 typedef struct ac_tokval {
252 	char		 *str;
253 	struct ac_tvlist *nlistp;
254 } ac_tokval_t;
255 
256 typedef struct ac_tvlist {
257 	ac_tokval_t	*vals;
258 	ac_toktype_t	toktype;
259 } ac_tvlist_t;
260 
261 /*
262  * Variables and prototypes
263  */
264 
265 static void common_bind(ac_iter_t *, void *, void *);
266 static void common_reset(ac_iter_t *);
267 static void varname_bind(ac_iter_t *, void *, void *);
268 static void loadfile_bind(ac_iter_t *, void *, void *);
269 static const char *get_next_tokval(ac_iter_t *);
270 static const char *get_next_lvarname(ac_iter_t *);
271 static const char *get_next_rvarname(ac_iter_t *);
272 static const char *get_next_loadfile(ac_iter_t *);
273 static ac_toktype_t parse_attr_list(ac_str_t *);
274 static ac_toktype_t parse_var_list(ac_str_t *);
275 
276 static ac_iter_t tokval_iter = {
277 	NULL,
278 	NULL,
279 	NULL,
280 	common_bind,
281 	common_reset,
282 	get_next_tokval
283 };
284 
285 static ac_iter_t lvarname_iter = {
286 	NULL,
287 	NULL,
288 	NULL,
289 	varname_bind,
290 	common_reset,
291 	get_next_lvarname
292 };
293 
294 static ac_iter_t rvarname_iter = {
295 	NULL,
296 	NULL,
297 	NULL,
298 	varname_bind,
299 	common_reset,
300 	get_next_rvarname
301 };
302 
303 static ac_iter_t loadfile_iter = {
304 	NULL,
305 	NULL,
306 	NULL,
307 	loadfile_bind,
308 	common_reset,
309 	get_next_loadfile
310 };
311 
312 /*
313  * Note: We use toktype to index into this array, so for each toktype,
314  *	 there must be one element in the array, and in the same order
315  *	 as that toktype is defined in ac_toktype.
316  */
317 static ac_tokinfo_t token_info[] = {
318 	{ TOKTYPE_CMD,	    &tokval_iter,   CSUF_CMD,	   NULL },
319 	{ TOKTYPE_ARG,	    &tokval_iter,   CSUF_ARG,	   NULL },
320 	{ TOKTYPE_ATTRNAME, &tokval_iter,   CSUF_ATTRNAME, NULL },
321 	{ TOKTYPE_ATTRVAL,  NULL,	    NULL,	   NULL },
322 	{ TOKTYPE_LVARNAME, &lvarname_iter, CSUF_LVARNAME, NULL },
323 	{ TOKTYPE_RVARNAME, &rvarname_iter, CSUF_RVARNAME, NULL },
324 	{ TOKTYPE_VARVAL,   NULL,	    NULL,	   NULL },
325 	{ TOKTYPE_LOADFILE, &loadfile_iter, CSUF_ARG,	   NULL },
326 	{ TOKTYPE_ATTRLIST, NULL,	    NULL,	   parse_attr_list },
327 	{ TOKTYPE_VARLIST,  NULL,	    NULL,	   parse_var_list },
328 	{ TOKTYPE_NULL,	    NULL,	    NULL,	   NULL }
329 };
330 
331 static ac_tokval_t event_attrnames[] = {
332 	{ "rate",	NULL},
333 	{ NULL, 	NULL}
334 };
335 
336 static ac_tvlist_t event_attrs = {
337 	event_attrnames,
338 	TOKTYPE_ATTRLIST
339 };
340 
341 static ac_tokval_t file_attrnames[] = {
342 	{ "path",	NULL },
343 	{ "reuse",	NULL },
344 	{ "prealloc",	NULL },
345 	{ "paralloc",	NULL },
346 	{ NULL, 	NULL }
347 };
348 
349 static ac_tvlist_t file_attrs = {
350 	file_attrnames,
351 	TOKTYPE_ATTRLIST
352 };
353 
354 static ac_tokval_t fileset_attrnames[] = {
355 	{ "size",	NULL },
356 	{ "path",	NULL },
357 	{ "dirwidth",	NULL },
358 	{ "prealloc",	NULL },
359 	{ "filesizegamma",	NULL },
360 	{ "dirgamma",	NULL },
361 	{ "cached",	NULL },
362 	{ "entries",	NULL },
363 	{ NULL, 	NULL }
364 };
365 
366 static ac_tvlist_t fileset_attrs = {
367 	fileset_attrnames,
368 	TOKTYPE_ATTRLIST
369 };
370 
371 static ac_tokval_t process_attrnames[] = {
372 	{ "nice",	NULL },
373 	{ "instances",	NULL },
374 	{ NULL,		NULL }
375 };
376 
377 static ac_tvlist_t process_attrs = {
378 	process_attrnames,
379 	TOKTYPE_ATTRLIST
380 };
381 
382 static ac_tokval_t create_argnames[] = {
383 	{ "file",	NULL },
384 	{ "fileset",	NULL },
385 	{ "process",	NULL },
386 	{ NULL, 	NULL }
387 };
388 
389 static ac_tvlist_t create_args = {
390 	create_argnames,
391 	TOKTYPE_ARG
392 };
393 
394 static ac_tokval_t define_argnames[] = {
395 	{ "file", 	&file_attrs },
396 	{ "fileset",	&fileset_attrs },
397 	{ "process",	&process_attrs },
398 	{ NULL, 	NULL }
399 };
400 
401 static ac_tvlist_t define_args = {
402 	define_argnames,
403 	TOKTYPE_ARG
404 };
405 
406 static ac_tvlist_t load_args = {
407 	NULL,
408 	TOKTYPE_LOADFILE
409 };
410 
411 static ac_tvlist_t set_args = {
412 	NULL,
413 	TOKTYPE_VARLIST
414 };
415 
416 static ac_tokval_t shutdown_argnames[] = {
417 	{ "process",	NULL },
418 	{ NULL,		NULL }
419 };
420 
421 static ac_tvlist_t shutdown_args = {
422 	shutdown_argnames,
423 	TOKTYPE_ARG
424 };
425 
426 static ac_tokval_t stats_argnames[] = {
427 	{ "clear", 	NULL },
428 	{ "directory", 	NULL },
429 	{ "command",	NULL },
430 	{ "dump",	NULL },
431 	{ "xmldump",	NULL },
432 	{ NULL, 	NULL }
433 };
434 
435 static ac_tvlist_t stats_args = {
436 	stats_argnames,
437 	TOKTYPE_ARG
438 };
439 
440 static ac_tokval_t fb_cmdnames[] = {
441 	{ "create",	&create_args },
442 	{ "define",	&define_args },
443 	{ "debug",	NULL },
444 	{ "echo",	NULL },
445 	{ "eventgen",	&event_attrs },
446 	{ "foreach",	NULL },
447 	{ "help",	NULL },
448 	{ "list",	NULL },
449 	{ "load",	&load_args },
450 	{ "log",	NULL },
451 	{ "quit",	NULL },
452 	{ "run",	NULL },
453 	{ "set",	&set_args },
454 	{ "shutdown",	&shutdown_args },
455 	{ "sleep",	NULL },
456 	{ "stats",	&stats_args },
457 	{ "system",	NULL },
458 	{ "usage",	NULL },
459 	{ "vars",	NULL },
460 	{ NULL,		NULL },
461 };
462 
463 static ac_tvlist_t fb_cmds = {
464 	fb_cmdnames,
465 	TOKTYPE_CMD
466 };
467 
468 static ac_fname_cache_t loadnames = { NULL, 0, 0 };
469 
470 static int search_loadfiles(ac_fname_cache_t *);
471 static void parse_user_input(const char *, int, ac_inputline_t *);
472 static int compare_string(ac_str_t *, const char *, boolean_t, const char **);
473 static ac_match_result_t match_string(WordCompletion *, const char *, int,
474     ac_str_t *, ac_iter_t *, const char *);
475 
476 /*
477  * Bind the iterator to the passed list
478  */
479 static void
480 common_bind(ac_iter_t *iterp, void *listp, void *nlistpp)
481 {
482 	iterp->listp = listp;
483 	iterp->nlistpp = nlistpp;
484 }
485 
486 /*
487  * Reset index pointer to point to list head
488  */
489 static void
490 common_reset(ac_iter_t *iterp)
491 {
492 	iterp->curp = iterp->listp;
493 }
494 
495 /*
496  * Walk through an array of ac_tokval_t structures and return string
497  * of each item.
498  */
499 static const char *
500 get_next_tokval(ac_iter_t *iterp)
501 {
502 	ac_tokval_t *listp = iterp->listp;  /* list head */
503 	ac_tokval_t *curp = iterp->curp;  /* index pointer */
504 	/* user passed variable for returning value list for next token */
505 	ac_tvlist_t **nlistpp = iterp->nlistpp;
506 	const char *p;
507 
508 	if (listp == NULL || curp == NULL)
509 		return (NULL);
510 
511 	/* get the current item's string */
512 	p = curp->str;
513 
514 	/*
515 	 * save the current item's address into a user passed variable
516 	 */
517 	if (nlistpp != NULL)
518 		*nlistpp = curp->nlistp;
519 
520 	/* advance the index pointer */
521 	iterp->curp = ++curp;
522 
523 	return (p);
524 }
525 
526 /*
527  * Bind the iterator to filebench_shm->var_list
528  */
529 static void
530 varname_bind(ac_iter_t *iterp, void *listp, void * nlistpp)
531 {
532 	iterp->listp = filebench_shm->var_list;
533 	iterp->nlistpp = nlistpp;
534 }
535 
536 /*
537  * Walk through a linked list of var_t type structures and return name
538  * of each variable with a preceding '$' sign
539  */
540 static const char *
541 get_next_lvarname(ac_iter_t *iterp)
542 {
543 	static char buf[VARNAME_MAXLEN];
544 
545 	var_t *listp = iterp->listp;  /* list head */
546 	var_t *curp = iterp->curp;  /* index pointer */
547 	/* User passed variable for returning value list for next token */
548 	ac_tvlist_t **nlistpp = iterp->nlistpp;
549 	const char *p;
550 
551 	if (listp == NULL || curp == NULL)
552 		return (NULL);
553 
554 	/* Get current variable's name, copy it to buf, with a '$' prefix */
555 	p = curp->var_name;
556 	(void) snprintf(buf, sizeof (buf), "$%s", p);
557 
558 	/* No information for the next input string */
559 	if (nlistpp != NULL)
560 		*nlistpp = NULL;
561 
562 	/* Advance the index pointer */
563 	iterp->curp = curp->var_next;
564 
565 	return (buf);
566 }
567 
568 /*
569  * Walk through a linked list of var_t type structures and return name
570  * of each variable
571  */
572 static const char *
573 get_next_rvarname(ac_iter_t *iterp)
574 {
575 	var_t *listp = iterp->listp;  /* list head */
576 	var_t *curp = iterp->curp;  /* index pointer */
577 	/* User passed variable for returning value list for next item */
578 	ac_tvlist_t **nlistpp = iterp->nlistpp;
579 	const char *p;
580 
581 	if (listp == NULL || curp == NULL)
582 		return (NULL);
583 
584 	/* Get current variable's name */
585 	p = curp->var_name;
586 
587 	/* No information for the next input string */
588 	if (nlistpp != NULL)
589 		*nlistpp = NULL;
590 
591 	/* Advance the index pointer */
592 	iterp->curp = curp->var_next;
593 
594 	return (p);
595 }
596 
597 /*
598  * Bind the iterator to loadnames.fnc_buf, which is an ac_fname_t array
599  * and contains up-to-date workload file names. The function calls
600  * search_loadfiles() to update the cache before the binding.
601  */
602 static void
603 loadfile_bind(ac_iter_t *iterp, void *listp, void * nlistpp)
604 {
605 	/* Check loadfile name cache, update it if needed */
606 	(void) search_loadfiles(&loadnames);
607 
608 	iterp->listp = loadnames.fnc_buf;
609 	iterp->nlistpp = nlistpp;
610 }
611 
612 /*
613  * Walk through a string(ac_fname_t, more exactly) array and return each
614  * string, until a NULL iterm is encountered.
615  */
616 static const char *
617 get_next_loadfile(ac_iter_t *iterp)
618 {
619 	ac_fname_t *listp = iterp->listp; /* list head */
620 	ac_fname_t *curp = iterp->curp; /* index pointer */
621 	/* User passed variable for returning value list for next item */
622 	ac_tvlist_t **nlistpp = iterp->nlistpp;
623 	const char *p;
624 
625 	if (listp == NULL || curp == NULL)
626 		return (NULL);
627 
628 	/*
629 	 * Get current file name. If an NULL item is encountered, it means
630 	 * this is the end of the list. In that case, we need to set p to
631 	 * NULL to indicate to the caller that the end of the list is reached.
632 	 */
633 	p = (char *)curp;
634 	if (*p == NULL)
635 		p = NULL;
636 
637 	/* No information for the next input string */
638 	if (nlistpp != NULL)
639 		*nlistpp = NULL;
640 
641 	/* Advance the index pointer */
642 	iterp->curp = ++curp;
643 
644 	return (p);
645 }
646 
647 /*
648  * Search for available workload files in workload direcotry and
649  * update workload name cache.
650  */
651 static int
652 search_loadfiles(ac_fname_cache_t *fnamecache)
653 {
654 	DIR *dirp;
655 	struct dirent *fp;
656 	struct stat dstat;
657 	time_t mtime;
658 	ac_fname_t *buf;
659 	int bufsize = MALLOC_STEP;
660 	int len, i;
661 
662 	if (stat(FILEBENCHDIR"/workloads", &dstat) != 0)
663 		return (-1);
664 	mtime = dstat.st_mtime;
665 
666 	/* Return if there is no change since last time */
667 	if (mtime == fnamecache->fnc_mtime)
668 		return (0);
669 
670 	/* Get loadfile names and cache it */
671 	if ((buf = malloc(sizeof (ac_fname_t) * bufsize)) == NULL)
672 		return (-1);
673 	if ((dirp = opendir(FILEBENCHDIR"/workloads")) == NULL)
674 		return (-1);
675 	i = 0;
676 	while ((fp = readdir(dirp)) != NULL) {
677 		len = strlen(fp->d_name);
678 		if (len <= 2 || (fp->d_name)[len - 2] != '.' ||
679 		    (fp->d_name)[len - 1] != 'f')
680 			continue;
681 
682 		if (i == bufsize) {
683 			bufsize += MALLOC_STEP;
684 			if ((buf = realloc(buf, sizeof (ac_fname_t) *
685 			    bufsize)) == NULL)
686 				return (-1);
687 		}
688 
689 		(void) snprintf(buf[i], FILENAME_MAXLEN, "%s", fp->d_name);
690 		if (len -2 <= FILENAME_MAXLEN - 1) {
691 			/* Remove .f suffix in file name */
692 			buf[i][len -2] = NULL;
693 		}
694 		i++;
695 	}
696 	/* Added a NULL iterm as the array's terminator */
697 	buf[i][0] = NULL;
698 
699 	if (fnamecache->fnc_bufsize != 0)
700 		free(fnamecache->fnc_buf);
701 	fnamecache->fnc_buf = buf;
702 	fnamecache->fnc_bufsize = bufsize;
703 	fnamecache->fnc_mtime = mtime;
704 
705 	return (0);
706 }
707 
708 /*
709  * Parse user input line into a list of blank separated strings, and
710  * save the result in the passed ac_inputline_t structure. line and word_end
711  * parameters are passed from libtecla library. line points to user input
712  * buffer, and word_end is the index of the last character of user input.
713  */
714 static void
715 parse_user_input(const char *line, int word_end, ac_inputline_t *input)
716 {
717 	const char *p = line;
718 	int i;
719 
720 	/* Reset all fileds */
721 	for (i = 0; i < STR_NUM; i++) {
722 		input->strs[i].startp = NULL;
723 		input->strs[i].endp = NULL;
724 		input->strs[i].strtype = STRTYPE_INVALID;
725 	}
726 
727 	/*
728 	 * Parse user input. We don't use word_end to do boundary checking,
729 	 * instead we take advantage of the fact that the passed line
730 	 * parameter is always terminated by '\0'.
731 	 */
732 	for (i = 0; i < STR_NUM; i++) {
733 		/* Skip leading blank spaces */
734 		while (*p == ' ')
735 			p++;
736 
737 		if (*p == NULL) {
738 			/*
739 			 * User input nothing for the string being input
740 			 * before he pressed TAB. We use STR_NULL flag
741 			 * to indicate this so that match_str() will list
742 			 * all available candidates.
743 			 */
744 			input->strs[i].startp = p;
745 			input->strs[i].strtype = STRTYPE_NULL;
746 			return;
747 		}
748 
749 		/* Recoard the start and end of the string */
750 		input->strs[i].startp = p;
751 		while ((*p != ' ') && (*p != NULL))
752 			p++;
753 		input->strs[i].endp = p - 1;
754 
755 		if (*p == NULL) {
756 			input->strs[i].strtype = STRTYPE_INCOMPLETE;
757 			return;
758 		} else {
759 			/* The string is followed by a blank space */
760 			input->strs[i].strtype = STRTYPE_COMPLETE;
761 		}
762 	}
763 }
764 
765 /*
766  * Parse an input string which is an attribue list, get the current word
767  * user wants to complete, and return its token type.
768  *
769  * An atribute list has the following format:
770  *
771  * 	name1=val,name2=$var,...
772  *
773  * The function modifies the passed acstr string on success to point to
774  * the word being completed.
775  */
776 static ac_toktype_t
777 parse_attr_list(ac_str_t *acstr)
778 {
779 	const char *p;
780 
781 	if (acstr->strtype == STRTYPE_COMPLETE) {
782 		/*
783 		 * User has input a complete string for attribute list
784 		 * return TOKTYPE_NULL to abort the matching.
785 		 */
786 		return (TOKTYPE_ATTRLIST);
787 	} else if (acstr->strtype == STRTYPE_NULL) {
788 		/*
789 		 * User haven't input anything for the attribute list,
790 		 * he must be trying to list all attribute names.
791 		 */
792 		return (TOKTYPE_ATTRNAME);
793 	}
794 
795 	/*
796 	 * The string may contain multiple comma separated "name=value"
797 	 * items. Try to find the last one and move startp to point to it.
798 	 */
799 	for (p = acstr->endp; p >= acstr->startp && *p != ATTR_LIST_SEP; p--) {}
800 
801 	if (p == acstr->endp) {
802 		/*
803 		 * The last character of the string is ',', which means
804 		 * user is trying to list all attribute names.
805 		 */
806 		acstr->startp = p + 1;
807 		acstr->strtype = STRTYPE_NULL;
808 		return (TOKTYPE_ATTRNAME);
809 	} else if (p > acstr->startp) {
810 		/*
811 		 * Found ',' between starp and endp, move startp pointer
812 		 * to point to the last item.
813 		 */
814 		acstr->startp = p + 1;
815 	}
816 
817 	/*
818 	 * Now startp points to the last "name=value" item. Search in
819 	 * the characters user has input for this item:
820 	 *
821 	 *   a) if there isn't '=' character, user is inputting attribute name
822 	 *   b) if there is a '=' character and it is followed by a '$',
823 	 *	user is inputting variable name
824 	 *   c) if there is a '=' character and it isn't followed by a '$',
825 	 *	user is inputting a literal string as attribute value.
826 	 */
827 	for (p = acstr->startp; p <= acstr->endp; p++) {
828 		if (*p == ATTR_ASSIGN_OP) {
829 			/* Found "=" operator in the string */
830 			if (*(p + 1) == VAR_PREFIX) {
831 				acstr->startp = p + 2;
832 				if (*acstr->startp != NULL)
833 					acstr->strtype = STRTYPE_INCOMPLETE;
834 				else
835 					acstr->strtype = STRTYPE_NULL;
836 				return (TOKTYPE_RVARNAME);
837 			} else {
838 				return (TOKTYPE_ATTRVAL);
839 			}
840 		}
841 	}
842 
843 	/* Didn't find '=' operator, the string must be an attribute name */
844 	return (TOKTYPE_ATTRNAME);
845 }
846 
847 /*
848  * Parse an input string which is a variable list, get the current word
849  * user wants to complete, and return its token type.
850  *
851  * A varaible list has the following format:
852  *
853  *	$varname=value
854  *
855  * The function modifies the passed acstr string on success to point to
856  * the word being completed.
857  */
858 static ac_toktype_t
859 parse_var_list(ac_str_t *acstr)
860 {
861 	const char *p;
862 
863 	if (acstr->strtype == STRTYPE_COMPLETE) {
864 		/*
865 		 * User has input a complete string for var list
866 		 * return TOKTYPE_NULL to abort the matching.
867 		 */
868 		return (TOKTYPE_NULL);
869 	} else if (acstr->strtype == STRTYPE_NULL) {
870 		/*
871 		 * User haven't input anything for the attribute list,
872 		 * he must be trying to list all available var names.
873 		 */
874 		return (TOKTYPE_LVARNAME);
875 	}
876 
877 	/*
878 	 * Search in what user has input:
879 	 *
880 	 *   a) if there isn't a '=' character, user is inputting var name
881 	 *   b) if there is a '=' character, user is inputting var value
882 	 */
883 	for (p = acstr->startp; p <= acstr->endp; p++) {
884 		if (*p == VAR_ASSIGN_OP)
885 			return (TOKTYPE_VARVAL);
886 	}
887 
888 	/* Didn't find '=' operator, user must be inputting an var name */
889 	return (TOKTYPE_LVARNAME);
890 }
891 
892 /*
893  * Compare two strings acstr and str. acstr is a string of ac_str_t type,
894  * str is a normal string. If issub is B_TRUE, the function checks if
895  * acstr is a sub-string of str, starting from index 0; otherwise it checks
896  * if acstr and str are exactly the same.
897  *
898  * The function returns 0 on success and -1 on failure. When it succeeds,
899  * it also set restp to point to the rest part of the normal string.
900  */
901 static int
902 compare_string(ac_str_t *acstr, const char *str, boolean_t issub,
903     const char **restp)
904 {
905 	const char *p, *q;
906 
907 	for (p = acstr->startp, q = str; (p <= acstr->endp) && (*q != '\0');
908 	    p++, q++) {
909 		if (*p != *q)
910 			return (-1);
911 	}
912 
913 	if (p == acstr->endp + 1) {
914 		if (*q == '\0' || issub == B_TRUE) {
915 			if (restp != NULL)
916 				*restp = q;
917 			return (0);
918 		}
919 	}
920 
921 	return (-1);
922 }
923 
924 /*
925  * Use the passed iterp iterator to access a list of string values to
926  * look for those matches with acstr, an user input string to be completed.
927  *
928  * cpl, line, work_end, and cont_suffix are parameters needed by
929  * cpl_add_completion(), which adds matched entries to libtecla.
930  *
931  * Since user input line may have multiple strings, the function is
932  * expected to be called multiple times to match those strings one
933  * by one until the last one is reached.
934  *
935  * The multi-step matching process also means the function should provide
936  * a way to indicate to the caller whether to continue or abort the
937  * whole matching process. The function does that with the following
938  * return values:
939  *
940  *    MATCH_DONE - the matching for the whole user input is done. This
941  *		   can mean either some items are found or none is found.
942  *		   In either case, the caller shouldn't continue to
943  *		   match the rest strings, either because there is
944  *		   no strings left, or because the matching for the
945  *		   current string failed so there is no need to check
946  *		   further.
947  *    MATCH_CONT - the matching for the current string succeeds, but
948  *		   user needs to continue to match the rest strings.
949  */
950 static ac_match_result_t
951 match_string(WordCompletion *cpl, const char *line, int word_end,
952     ac_str_t *acstr, ac_iter_t *iterp, const char *cont_suffix)
953 {
954 	const char *str, *restp;
955 
956 	iterp->reset(iterp);
957 
958 	if (acstr->strtype == STRTYPE_COMPLETE) {
959 		while ((str = iterp->get_nextstr(iterp)) != NULL) {
960 			if (!compare_string(acstr, str, B_FALSE, NULL)) {
961 				/* Continue to check rest strings */
962 				return (MATCH_CONT);
963 			}
964 		}
965 	} else if (acstr->strtype == STRTYPE_NULL) {
966 		/* User input nothing. List all available strings */
967 		while ((str = iterp->get_nextstr(iterp)) != NULL) {
968 			(void) cpl_add_completion(cpl, line,
969 			    acstr->startp - line, word_end, str,
970 			    NULL, cont_suffix);
971 		}
972 	} else if (acstr->strtype == STRTYPE_INCOMPLETE) {
973 		while ((str = iterp->get_nextstr(iterp)) != NULL) {
974 			if (!compare_string(acstr, str, B_TRUE, &restp)) {
975 				/* It matches! Add it. */
976 				(void) cpl_add_completion(cpl, line,
977 				    acstr->startp - line, word_end, restp,
978 				    NULL, cont_suffix);
979 			}
980 		}
981 	}
982 
983 	return (MATCH_DONE);
984 }
985 
986 /*
987  * This is the interface between filebench and libtecla for auto-
988  * completion. It is called by libtecla whenever user initiates a
989  * auto-completion request(ie., pressing TAB key).
990  *
991  * The function calls parse_user_input() to parse user input into
992  * multiple strings, then it calls match_string() to match each
993  * string in user input in sequence until either the last string
994  * is reached and completed or the the matching fails.
995  */
996 CPL_MATCH_FN(command_complete)
997 {
998 	ac_inputline_t inputline;
999 	ac_tvlist_t *clistp = &fb_cmds, *nlistp;
1000 	ac_toktype_t toktype;
1001 	ac_iter_t *iterp;
1002 	char *cont_suffix;
1003 	ac_get_curtok_func_t get_curtok;
1004 	int i, ret;
1005 
1006 	/* Parse user input and save the result in inputline variable. */
1007 	parse_user_input(line, word_end, &inputline);
1008 
1009 	/*
1010 	 * Match each string in user input against the proper token's
1011 	 * value list, and continue the loop until either the last string
1012 	 * is reached and completed or the matching aborts.
1013 	 */
1014 	for (i = 0; i < STR_NUM &&
1015 	    inputline.strs[i].strtype != STRTYPE_INVALID && clistp != NULL;
1016 	    i++) {
1017 		toktype = clistp->toktype;
1018 
1019 		/*
1020 		 * If the current stirng can contain multiple tokens, modify
1021 		 * the stirng to point to the word being input and return
1022 		 * its token type.
1023 		 */
1024 		get_curtok = token_info[toktype].get_curtok;
1025 		if (get_curtok != NULL)
1026 			toktype = (*get_curtok)(&inputline.strs[i]);
1027 
1028 		iterp = token_info[toktype].iter;
1029 		cont_suffix = token_info[toktype].cont_suffix;
1030 		/* Return if there is no completion info for the token */
1031 		if (iterp == NULL)
1032 			break;
1033 
1034 		iterp->bind(iterp, clistp->vals, &nlistp);
1035 		/* Match user string against the token's list */
1036 		ret = match_string(cpl, line, word_end, &inputline.strs[i],
1037 		    iterp, cont_suffix);
1038 		if (ret == MATCH_DONE)
1039 			return (0);
1040 		clistp = nlistp;
1041 	}
1042 
1043 	return (0);
1044 }
1045