diff --git a/src/command.c b/src/command.c index 4f9e9805d..00fe5870c 100644 --- a/src/command.c +++ b/src/command.c @@ -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; diff --git a/src/complete.c b/src/complete.c index e12dce33a..ab19bdc30 100644 --- a/src/complete.c +++ b/src/complete.c @@ -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; diff --git a/src/widget.h b/src/widget.h index f162cf643..8c6f78170 100644 --- a/src/widget.h +++ b/src/widget.h @@ -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