2 * Copyright (c) 2011 Jiri Svoboda
3 * Copyright (c) 2011 Martin Sucha
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include "cmds/cmds.h"
43 static errno_t
compl_init(wchar_t *text
, size_t pos
, size_t *cstart
, void **state
);
44 static errno_t
compl_get_next(void *state
, char **compl);
45 static void compl_fini(void *state
);
47 /** Bdsh implementation of completion ops. */
48 tinput_compl_ops_t compl_ops
= {
50 .get_next
= compl_get_next
,
54 /** Completion state object.
56 * The state object contains 'iterators' for modules, builtins and
57 * executables in directories.
60 /** String prefix which we are trying to complete. */
62 /** Length of string prefix (number of characters) */
65 /** Pointer inside list of modules */
67 /** Pointer inside list of builtins */
70 /** Pointer inside list of directories */
71 const char *const *path
;
72 /** If not @c NULL, should be freed in the end. */
74 /** Current open directory */
80 * @c true if we are completing a command, @c false if we are
81 * completing an argument
88 * Set up iterators in completion object, based on current token.
90 static errno_t
compl_init(wchar_t *text
, size_t pos
, size_t *cstart
, void **state
)
98 token_t
*tokens
= calloc(WORD_MAX
, sizeof(token_t
));
106 static const char *dirlist_arg
[] = { ".", NULL
};
108 ssize_t current_token
;
109 size_t tokens_length
;
111 cs
= calloc(1, sizeof(compl_t
));
117 /* Convert text buffer to string */
118 stext
= wstr_to_astr(text
);
124 /* Tokenize the input string */
125 retval
= tok_init(&tok
, stext
, tokens
, WORD_MAX
);
130 retval
= tok_tokenize(&tok
, &tokens_length
);
135 /* Find the current token */
136 for (current_token
= 0; current_token
< (ssize_t
) tokens_length
;
138 token_t
*t
= &tokens
[current_token
];
139 size_t end
= t
->char_start
+ t
->char_length
;
142 * Check if the caret lies inside the token or immediately
145 if (t
->char_start
<= pos
&& pos
<= end
) {
150 if (tokens_length
== 0)
153 if ((current_token
>= 0) && (tokens
[current_token
].type
!= TOKTYPE_SPACE
))
154 *cstart
= tokens
[current_token
].char_start
;
159 * Extract the prefix being completed
160 * XXX: handle strings, etc.
162 pref_size
= str_lsize(stext
, pos
- *cstart
);
163 prefix
= malloc(pref_size
+ 1);
164 if (prefix
== NULL
) {
168 prefix
[pref_size
] = 0;
170 if (current_token
>= 0) {
171 str_ncpy(prefix
, pref_size
+ 1, stext
+
172 tokens
[current_token
].byte_start
, pref_size
);
176 * Determine if the token being completed is a command or argument.
177 * We look at the previous token. If there is none or it is a pipe
178 * ('|'), it is a command, otherwise it is an argument.
181 /* Skip any whitespace before current token */
182 ssize_t prev_token
= current_token
- 1;
183 if ((prev_token
>= 0) && (tokens
[prev_token
].type
== TOKTYPE_SPACE
))
187 * It is a command if it is the first token or if it immediately
188 * follows a pipe token.
190 if ((prev_token
< 0) || (tokens
[prev_token
].type
== TOKTYPE_SPACE
))
191 cs
->is_command
= true;
193 cs
->is_command
= false;
195 rpath_sep
= str_rchr(prefix
, '/');
196 if (rpath_sep
!= NULL
) {
197 /* Extract path. For path beginning with '/' keep the '/'. */
198 dirname
= str_ndup(prefix
, max(1, rpath_sep
- prefix
));
199 if (dirname
== NULL
) {
204 /* Extract name prefix */
205 cs
->prefix
= str_dup(rpath_sep
+ 1);
206 if (cs
->prefix
== NULL
) {
210 *cstart
+= rpath_sep
+ 1 - prefix
;
214 cs
->path_list
= malloc(sizeof(char *) * 2);
215 if (cs
->path_list
== NULL
) {
219 cs
->path_list
[0] = dirname
;
220 cs
->path_list
[1] = NULL
;
221 /* The second const ensures that we can't assign a const
222 * string to the non-const array. */
223 cs
->path
= (const char *const *) cs
->path_list
;
225 } else if (cs
->is_command
) {
226 /* Command without path */
227 cs
->module
= modules
;
228 cs
->builtin
= builtins
;
230 cs
->path
= &search_dir
[0];
232 /* Argument without path */
234 cs
->path
= &dirlist_arg
[0];
237 cs
->prefix_len
= str_length(cs
->prefix
);
249 if (cs
!= NULL
&& cs
->path_list
!= NULL
) {
251 while (cs
->path_list
[i
] != NULL
) {
252 free(cs
->path_list
[i
]);
258 if ((cs
!= NULL
) && (cs
->prefix
!= NULL
))
279 /** Determine if completion matches the required prefix.
281 * Required prefix is stored in @a cs->prefix.
283 * @param cs Completion state object
284 * @param compl Completion string
285 * @return @c true when @a compl matches, @c false otherwise
287 static bool compl_match_prefix(compl_t
*cs
, const char *compl)
289 return str_lcmp(compl, cs
->prefix
, cs
->prefix_len
) == 0;
292 /** Get next match. */
293 static errno_t
compl_get_next(void *state
, char **compl)
295 compl_t
*cs
= (compl_t
*) state
;
300 if (cs
->last_compl
!= NULL
) {
301 free(cs
->last_compl
);
302 cs
->last_compl
= NULL
;
306 if (cs
->module
!= NULL
) {
307 while (*compl == NULL
&& cs
->module
->name
!= NULL
) {
308 if (compl_match_prefix(cs
, cs
->module
->name
)) {
309 asprintf(compl, "%s ", cs
->module
->name
);
310 cs
->last_compl
= *compl;
319 if (cs
->builtin
!= NULL
) {
320 while (*compl == NULL
&& cs
->builtin
->name
!= NULL
) {
321 if (compl_match_prefix(cs
, cs
->builtin
->name
)) {
322 asprintf(compl, "%s ", cs
->builtin
->name
);
323 cs
->last_compl
= *compl;
331 /* Files and directories. We scan entries from a set of directories. */
332 if (cs
->path
!= NULL
) {
333 while (*compl == NULL
) {
334 /* Open next directory */
335 while (cs
->dir
== NULL
) {
336 if (*cs
->path
== NULL
)
339 cs
->dir
= opendir(*cs
->path
);
341 /* Skip directories that we fail to open. */
346 /* If it was the last one, we are done */
350 /* Read next directory entry */
351 dent
= readdir(cs
->dir
);
353 /* Error. Close directory, go to next one */
360 if (compl_match_prefix(cs
, dent
->d_name
)) {
361 /* Construct pathname */
363 asprintf(&ent_path
, "%s/%s", *cs
->path
, dent
->d_name
);
365 if (vfs_stat_path(ent_path
, &ent_stat
) != EOK
) {
373 /* If completing command, do not match directories. */
374 if (!ent_stat
.is_directory
|| !cs
->is_command
) {
375 asprintf(compl, "%s%c", dent
->d_name
,
376 ent_stat
.is_directory
? '/' : ' ');
377 cs
->last_compl
= *compl;
391 /** Finish completion operation. */
392 static void compl_fini(void *state
)
394 compl_t
*cs
= (compl_t
*) state
;
396 if (cs
->path_list
!= NULL
) {
398 while (cs
->path_list
[i
] != NULL
) {
399 free(cs
->path_list
[i
]);
405 if (cs
->last_compl
!= NULL
)
406 free(cs
->last_compl
);