completion: fixed completion of escaped commands in commandline

Changes:
 * added new flag: INPUT_COMPLETE_SHELL_ESC - forces
   filename completion return escaped names
 * cleaned up src/complete.c: removed global variables in
   favour of completion flags. Code changed respectively.

Signed-off-by: Sergei Trofimovich <slyfox@inbox.ru>
This commit is contained in:
Sergei Trofimovich 2009-01-31 21:46:32 +02:00
parent a5e61d285e
commit 60d8c1d615
3 changed files with 98 additions and 66 deletions

View File

@ -294,7 +294,8 @@ command_new (int y, int x, int cols)
{
WInput *cmd;
cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline", INPUT_COMPLETE_DEFAULT);
cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline",
INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC);
/* Add our hooks */
cmd->widget.callback = command_callback;

View File

@ -45,17 +45,34 @@
#include "util.h"
#include "key.h" /* XCTRL and ALT macros */
typedef char *CompletionFunction (char *, int);
typedef char *CompletionFunction (char * text, int state, INPUT_COMPLETE_FLAGS flags);
/* This flag is used in filename_completion_function */
static int ignore_filenames = 0;
//#define DO_COMPLETION_DEBUG
#ifdef DO_COMPLETION_DEBUG
/*
* Useful to print/debug completion flags
*/
static const char * show_c_flags(INPUT_COMPLETE_FLAGS flags)
{
static char s_cf[] = "FHCVUDS";
/* This flag is used by command_completion_function */
/* to hint the filename_completion_function */
static int look_for_executables = 0;
s_cf[0] = (flags & INPUT_COMPLETE_FILENAMES) ? 'F' : ' ';
s_cf[1] = (flags & INPUT_COMPLETE_HOSTNAMES) ? 'H' : ' ';
s_cf[2] = (flags & INPUT_COMPLETE_COMMANDS) ? 'C' : ' ';
s_cf[3] = (flags & INPUT_COMPLETE_VARIABLES) ? 'V' : ' ';
s_cf[4] = (flags & INPUT_COMPLETE_USERNAMES) ? 'U' : ' ';
s_cf[5] = (flags & INPUT_COMPLETE_CD) ? 'D' : ' ';
s_cf[6] = (flags & INPUT_COMPLETE_SHELL_ESC) ? 'S' : ' ';
return s_cf;
}
#define SHOW_C_CTX(func) fprintf(stderr, "%s: text='%s' flags=%s\n", func, text, show_c_flags(flags))
#else
#define SHOW_C_CTX(func)
#endif /* DO_CMPLETION_DEBUG */
static char *
filename_completion_function (char *text, int state)
filename_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static DIR *directory;
static char *filename = NULL;
@ -66,6 +83,8 @@ filename_completion_function (char *text, int state)
struct dirent *entry = NULL;
SHOW_C_CTX("filename_completion_function");
/* If we're starting the match process, initialize us a bit. */
if (!state){
const char *temp;
@ -139,18 +158,15 @@ filename_completion_function (char *text, int state)
}
g_free (tmp);
}
switch (look_for_executables)
{
case 2: if (!isexec)
continue;
break;
case 1: if (!isexec && !isdir)
continue;
break;
}
if (ignore_filenames && !isdir)
continue;
break;
if ((flags & INPUT_COMPLETE_COMMANDS)
&& (isexec || isdir))
break;
if ((flags & INPUT_COMPLETE_CD)
&& isdir)
break;
if (flags & (INPUT_COMPLETE_FILENAMES))
break;
continue;
}
if (!entry){
@ -184,18 +200,27 @@ filename_completion_function (char *text, int state)
}
if (isdir)
strcat (temp, PATH_SEP_STR);
return temp;
if (temp && (flags & INPUT_COMPLETE_SHELL_ESC))
{
SHELL_ESCAPED_STR e_temp = mhl_shell_escape_dup(temp);
mhl_mem_free (temp);
temp = e_temp.s;
}
return temp;
}
}
/* We assume here that text[0] == '~' , if you want to call it in another way,
you have to change the code */
static char *
username_completion_function (char *text, int state)
username_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static struct passwd *entry;
static int userlen;
SHOW_C_CTX("username_completion_function");
if (text[0] == '\\' && text[1] == '~') text++;
if (!state){ /* Initialization stuff */
setpwent ();
@ -231,12 +256,14 @@ extern char **environ;
/* We assume text [0] == '$' and want to have a look at text [1], if it is
equal to '{', so that we should append '}' at the end */
static char *
variable_completion_function (char *text, int state)
variable_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static char **env_p;
static int varlen, isbrace;
const char *p = NULL;
SHOW_C_CTX("variable_completion_function");
if (!state){ /* Initialization stuff */
isbrace = (text [1] == '{');
varlen = strlen (text + 1 + isbrace);
@ -346,11 +373,13 @@ static void fetch_hosts (const char *filename)
}
static char *
hostname_completion_function (char *text, int state)
hostname_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
static char **host_p;
static int textstart, textlen;
SHOW_C_CTX("hostname_completion_function");
if (!state){ /* Initialization stuff */
const char *p;
@ -400,9 +429,8 @@ hostname_completion_function (char *text, int state)
* table of shell built-ins.
*/
static char *
command_completion_function (char *text, int state)
command_completion_function (char *text, int state, INPUT_COMPLETE_FLAGS flags)
{
text = mhl_shell_unescape_buf(text);
static const char *path_end;
static int isabsolute;
static int phase;
@ -427,9 +455,15 @@ command_completion_function (char *text, int state)
};
char *p, *found;
SHOW_C_CTX("command_completion_function");
if (!(flags & INPUT_COMPLETE_COMMANDS))
return 0;
text = mhl_shell_unescape_buf(text);
if (!state) { /* Initialize us a little bit */
isabsolute = strchr (text, PATH_SEP) != 0;
look_for_executables = isabsolute ? 1 : 2;
if (!isabsolute) {
words = bash_reserved;
phase = 0;
@ -445,16 +479,8 @@ command_completion_function (char *text, int state)
}
if (isabsolute) {
p = filename_completion_function (text, state);
if (!p)
{
look_for_executables = 0;
return 0;
}
SHELL_ESCAPED_STR e_p = mhl_shell_escape_dup(p);
mhl_mem_free(p);
return e_p.s;
p = filename_completion_function (text, state, flags);
return p;
}
found = NULL;
@ -494,7 +520,7 @@ command_completion_function (char *text, int state)
}
found =
filename_completion_function (cur_word,
state - init_state);
state - init_state, flags);
if (!found) {
g_free (cur_word);
cur_word = NULL;
@ -503,17 +529,16 @@ command_completion_function (char *text, int state)
}
if (!found) {
look_for_executables = 0;
g_free (path);
path = NULL;
return NULL;
}
if ((p = strrchr (found, PATH_SEP)) != NULL) {
p++;
p = g_strdup (p);
g_free (found);
/* TODO: shell escape? */
return p;
SHELL_ESCAPED_STR e_p = mhl_shell_escape_dup(p);
mhl_mem_free(found);
return e_p.s;
}
return found;
@ -533,7 +558,7 @@ match_compare (const void *a, const void *b)
as the second.
In case no matches were found we return NULL. */
static char **
completion_matches (char *text, CompletionFunction entry_function)
completion_matches (char *text, CompletionFunction entry_function, INPUT_COMPLETE_FLAGS flags)
{
/* Number of slots in match_list. */
int match_list_size;
@ -549,7 +574,7 @@ completion_matches (char *text, CompletionFunction entry_function)
match_list[1] = NULL;
while ((string = (*entry_function) (text, matches)) != NULL){
while ((string = (*entry_function) (text, matches, flags)) != NULL){
if (matches + 1 == match_list_size)
match_list = (char **) g_realloc (match_list, ((match_list_size += 30) + 1) * sizeof (char *));
match_list[++matches] = string;
@ -613,10 +638,8 @@ check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
{
const char *p, *q;
if (flags & INPUT_COMPLETE_CD)
return 1;
if (!(flags & INPUT_COMPLETE_COMMANDS))
SHOW_C_CTX("check_is_cd");
if (!(flags & INPUT_COMPLETE_CD))
return 0;
/* Skip initial spaces */
@ -635,7 +658,7 @@ check_is_cd (const char *text, int start, INPUT_COMPLETE_FLAGS flags)
/* Returns an array of matches, or NULL if none. */
static char **
try_complete (char *text, int *start, int *end, int flags)
try_complete (char *text, int *start, int *end, INPUT_COMPLETE_FLAGS flags)
{
int in_command_position = 0;
char *word, c;
@ -644,8 +667,8 @@ try_complete (char *text, int *start, int *end, int flags)
char *p = NULL, *q = NULL, *r = NULL;
int is_cd = check_is_cd (text, *start, flags);
SHOW_C_CTX("try_complete");
ignore_filenames = 0;
c = text [*end];
text [*end] = 0;
word = g_strdup (text + *start);
@ -660,7 +683,7 @@ try_complete (char *text, int *start, int *end, int flags)
for (i = *start - 1; i > -1; i--) {
if (text[i] == ' ' || text[i] == '\t'){
if (i == 0 ) continue;
if (text[i-1] == '\\') {
if (text[i-1] != '\\') {
i--;
break;
}
@ -702,14 +725,16 @@ try_complete (char *text, int *start, int *end, int flags)
/* Command substitution? */
if (p > q && p > r){
matches = completion_matches (p + 1, command_completion_function);
SHOW_C_CTX("try_complete:cmd_backq_subst");
matches = completion_matches (p + 1, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
if (matches)
*start += p + 1 - word;
}
/* Variable name? */
else if (q > p && q > r){
matches = completion_matches (q, variable_completion_function);
SHOW_C_CTX("try_complete:var_subst");
matches = completion_matches (q, variable_completion_function, flags);
if (matches)
*start += q - word;
}
@ -717,7 +742,8 @@ try_complete (char *text, int *start, int *end, int flags)
/* Starts with '@', then look through the known hostnames for
completion first. */
else if (r > p && r > q){
matches = completion_matches (r, hostname_completion_function);
SHOW_C_CTX("try_complete:host_subst");
matches = completion_matches (r, hostname_completion_function, flags);
if (matches)
*start += r - word;
}
@ -725,20 +751,26 @@ try_complete (char *text, int *start, int *end, int flags)
/* Starts with `~' and there is no slash in the word, then
try completing this word as a username. */
if (!matches && *word == '~' && (flags & INPUT_COMPLETE_USERNAMES) && !strchr (word, PATH_SEP))
matches = completion_matches (word, username_completion_function);
{
SHOW_C_CTX("try_complete:user_subst");
matches = completion_matches (word, username_completion_function, flags);
}
/* And finally if this word is in a command position, then
complete over possible command names, including aliases, functions,
and command names. */
if (!matches && in_command_position)
matches = completion_matches (word, command_completion_function);
{
SHOW_C_CTX("try_complete:cmd_subst");
matches = completion_matches (word, command_completion_function, flags & (~INPUT_COMPLETE_FILENAMES));
}
else if (!matches && (flags & INPUT_COMPLETE_FILENAMES)){
if (is_cd)
ignore_filenames = 1;
matches = completion_matches (word, filename_completion_function);
ignore_filenames = 0;
if (is_cd)
flags &= ~(INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_COMMANDS);
SHOW_C_CTX("try_complete:filename_subst_1");
matches = completion_matches (word, filename_completion_function, flags);
if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
char *p, *q = text + *start;
@ -776,10 +808,9 @@ try_complete (char *text, int *start, int *end, int flags)
*s = 0;
if (*cdpath){
r = concat_dir_and_file (cdpath, word);
ignore_filenames = 1;
matches = completion_matches (r, filename_completion_function);
ignore_filenames = 0;
g_free (r);
SHOW_C_CTX("try_complete:filename_subst_2");
matches = completion_matches (r, filename_completion_function, flags);
g_free (r);
}
*s = c;
cdpath = s + 1;
@ -1037,7 +1068,6 @@ complete_engine (WInput *in, int what_to_do)
return 0;
}
//void complete (WInput *in, COMPLETION_STYLE style)
void complete (WInput *in)
{
int engine_flags;

View File

@ -12,6 +12,7 @@ typedef enum {
INPUT_COMPLETE_VARIABLES = 1<<3,
INPUT_COMPLETE_USERNAMES = 1<<4,
INPUT_COMPLETE_CD = 1<<5,
INPUT_COMPLETE_SHELL_ESC = 1<<6,
INPUT_COMPLETE_DEFAULT = INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_HOSTNAMES