Page MenuHomeFreeBSD

sh(1): autocomplete commands

Authored by pstef on Mar 21 2021, 2:29 PM.
Referenced Files
Unknown Object (File)
Sat, Nov 25, 12:05 AM
Unknown Object (File)
Wed, Nov 22, 7:51 AM
Unknown Object (File)
Tue, Nov 21, 8:10 AM
Unknown Object (File)
Tue, Nov 21, 5:18 AM
Unknown Object (File)
Mon, Nov 13, 11:51 PM
Unknown Object (File)
Sun, Nov 12, 9:55 AM
Unknown Object (File)
Tue, Nov 7, 9:19 AM
Unknown Object (File)
Oct 19 2023, 3:39 AM



Without this patch, sh can autocomplete file names but not commands from
$PATH. Use libedit's facility to execute custom function for autocomplete,
but yield to the library's standard autocomplete function when cursor is
not at position 0.

Please note that for this to work as intended, libedit has to be synchronized
with upstream.

Diff Detail

rG FreeBSD src repository
Lint Not Applicable
Tests Not Applicable

Event Timeline

pstef requested review of this revision.Mar 21 2021, 2:29 PM
pstef created this revision.

Thanks for working on this, $PATH completion for commands is the feature I miss most when using /bin/sh.


The attribute is ignored in the declaration, it's only needed in the definition.


Does this function need a special case if text starts with /?


Could just declare char execpath[PATH_MAX] and avoid this malloc?


usr.bin/which.c has the following, so maybe we need to handle empty elements explicitly:

	if (strchr(filename, '/') != NULL)
		return (is_there(filename) ? 0 : -1);
	found = 0;
	while ((d = strsep(&path, ":")) != NULL) {
		if (*d == '\0')
			d = ".";
		if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
		    filename) >= (int)sizeof(candidate))
		if (is_there(candidate)) {
			found = 1;
			if (!allpaths)

missing closedir(dir)?


cdefs.h has a macro for this attribute, so it can be
sh_complete(EditLine *sel, int ch __unused)

Address mistakes pointed out by Alex.

Thanks, I tried to fix all the obvious mistakes. I will have to test and think about the rest of your comments.


This will have to be escaped since libedit will refuse to do it seeing that the completion function is non-standard.

Nice feature.


Code to retrieve all commands in PATH does not exist yet in sh, so it is appropriate to write it here.

Note that this does not include builtins or functions.


Handling empty elements is definitely required.


Using fstatat() will allow avoiding the string manipulation.


d_type could be used to reduce stat calls: if DT_REG the file can be accepted without stat call, if DT_UNKNOWN or DT_LNK a stat call is required, otherwise the file can be rejected without stat call.


The paranoid will want some overflow checks here.


The code in contrib/libedit/filecomplete.c will sort the list, but will not remove duplicates. Do we care?


To be precise, all names will have to be escaped. Otherwise, the single_match check in contrib/libedit/filecomplete.c may see a difference.


Is is possible that this overflows the allocated memory by one pointer?

Apply some of the fixes suggested by Jilles.

pstef added inline comments.

Do we want to autocomplete builtins and functions? If so, maybe in a later patch? :)


Do you mean the if (*d == '\0') d = "."; part? So that it will search for matching commands in $PWD?


Currently I don't see a good reason to sort the list, but I may change my mind.


It may be debatable. If only one command was found (so matches[2] is NULL) and we decide to escape only matches[0] and matches[1] then all the conditions for single_match == TRUE are met. Then libedit should replace user input with an escaped equivalent and skip showing matches.

For the multiple matches case we could escape all of them, so we would have the behavior described above and also every item on the list of suggestions would be escaped, like I've been seeing in some Linux distributions for a while (except that seems to be limited to enclosing the file name with single or double quotes; no backslashes).

When hitting twice tab in a crappy environnement the same path added multiple time in PATH, it proposes completion for a given file for every occurence.

When I hit tab twice, it proposes PATH and "file"

Add code to remove duplicates.

I believe that all reported issues (and more) have been addressed.


I don't think this needs to be a global variable (see comments below).


If you use qsort_s here and pass (void *)(intptr_t)curpos as the extra argument you can avoid the global variable and make curpos local to sh_matches.

this patch has been tested successfully here, thank you for having worked on that.
as for the review of the code I don't have any further comments to add than the one which has already been provided.

This revision is now accepted and ready to land.Mar 29 2021, 8:13 AM
This revision was automatically updated to reflect the committed changes.