static char * username_completion_function (const char *text, int state, input_complete_t flags) { static struct passwd *entry; static size_t userlen; (void) flags; SHOW_C_CTX ("username_completion_function"); if (text[0] == '\\' && text[1] == '~') text++; if (state == 0) { /* Initialization stuff */ setpwent (); userlen = strlen (text + 1); } while ((entry = getpwent ()) != NULL) { /* Null usernames should result in all users as possible completions. */ if (userlen == 0) break; if (text[1] == entry->pw_name[0] && !strncmp (text + 1, entry->pw_name, userlen)) break; } if (entry != NULL) return g_strconcat ("~", entry->pw_name, PATH_SEP_STR, (char *) NULL); endpwent (); return NULL; }
static char * hostname_completion_function (const char *text, int state, input_complete_t flags) { static char **host_p; static int textstart, textlen; (void) flags; SHOW_C_CTX ("hostname_completion_function"); if (!state) { /* Initialization stuff */ const char *p; if (hosts != NULL) { for (host_p = hosts; *host_p; host_p++) g_free (*host_p); g_free (hosts); } hosts = g_new (char *, (hosts_alloclen = 30) + 1); *hosts = NULL; hosts_p = hosts; fetch_hosts ((p = getenv ("HOSTFILE")) ? p : "/etc/hosts"); host_p = hosts; textstart = (*text == '@') ? 1 : 0; textlen = strlen (text + textstart); } while (*host_p) { if (!textlen) break; /* Match all of them */ else if (!strncmp (text + textstart, *host_p, textlen)) break; host_p++; } if (!*host_p) { for (host_p = hosts; *host_p; host_p++) g_free (*host_p); g_free (hosts); hosts = NULL; return NULL; } else { char *temp = g_malloc (2 + strlen (*host_p)); if (textstart) *temp = '@'; strcpy (temp + textstart, *host_p); host_p++; return temp; } }
static char * variable_completion_function (const char *text, int state, input_complete_t flags) { static char **env_p; static unsigned int isbrace; static size_t varlen; const char *p = NULL; (void) flags; SHOW_C_CTX ("variable_completion_function"); if (state == 0) { /* Initialization stuff */ isbrace = (text[1] == '{') ? 1 : 0; varlen = strlen (text + 1 + isbrace); env_p = environ; } while (*env_p) { p = strchr (*env_p, '='); if (p && ((size_t) (p - *env_p) >= varlen) && !strncmp (text + 1 + isbrace, *env_p, varlen)) break; env_p++; } if (*env_p == NULL) return NULL; { GString *temp; temp = g_string_new_len (*env_p, p - *env_p); if (isbrace != 0) { g_string_prepend_c (temp, '{'); g_string_append_c (temp, '}'); } g_string_prepend_c (temp, '$'); env_p++; return g_string_free (temp, FALSE); } }
/** Check if directory completion is needed */ static gboolean check_is_cd (const char *text, int lc_start, input_complete_t flags) { char *p, *q; SHOW_C_CTX ("check_is_cd"); if ((flags & INPUT_COMPLETE_CD) == 0) return FALSE; /* Skip initial spaces */ p = (char *) text; q = (char *) text + lc_start; while (p < q && p[0] != '\0' && str_isspace (p)) str_next_char (&p); /* Check if the command is "cd" and the cursor is after it */ return (p[0] == 'c' && p[1] == 'd' && str_isspace (p + 2) && p + 2 < q); }
static char * variable_completion_function (const char *text, int state, input_complete_t flags) { static char **env_p; static int varlen, isbrace; const char *p = NULL; (void) flags; SHOW_C_CTX ("variable_completion_function"); if (state == 0) { /* Initialization stuff */ isbrace = (text[1] == '{'); varlen = strlen (text + 1 + isbrace); env_p = environ; } while (*env_p) { p = strchr (*env_p, '='); if (p && p - *env_p >= varlen && !strncmp (text + 1 + isbrace, *env_p, varlen)) break; env_p++; } if (*env_p == NULL) return NULL; { char *temp = g_malloc (2 + 2 * isbrace + p - *env_p); *temp = '$'; if (isbrace) temp[1] = '{'; memcpy (temp + 1 + isbrace, *env_p, p - *env_p); if (isbrace) strcpy (temp + 2 + (p - *env_p), "}"); else temp[1 + p - *env_p] = 0; env_p++; return temp; } }
static char * filename_completion_function (const char *text, int state, input_complete_t flags) { static DIR *directory = NULL; static char *filename = NULL; static char *dirname = NULL; static char *users_dirname = NULL; static size_t filename_len; int isdir = 1, isexec = 0; static vfs_path_t *dirname_vpath = NULL; struct dirent *entry = NULL; SHOW_C_CTX ("filename_completion_function"); if (text && (flags & INPUT_COMPLETE_SHELL_ESC)) { char *u_text; char *result; char *e_result; u_text = strutils_shell_unescape (text); result = filename_completion_function (u_text, state, flags & (~INPUT_COMPLETE_SHELL_ESC)); g_free (u_text); e_result = strutils_shell_escape (result); g_free (result); return e_result; } /* If we're starting the match process, initialize us a bit. */ if (state == 0) { const char *temp; g_free (dirname); g_free (filename); g_free (users_dirname); vfs_path_free (dirname_vpath); if ((*text != '\0') && (temp = strrchr (text, PATH_SEP)) != NULL) { filename = g_strdup (++temp); dirname = g_strndup (text, temp - text); } else { dirname = g_strdup ("."); filename = g_strdup (text); } /* We aren't done yet. We also support the "~user" syntax. */ /* Save the version of the directory that the user typed. */ users_dirname = dirname; dirname = tilde_expand (dirname); canonicalize_pathname (dirname); dirname_vpath = vfs_path_from_str (dirname); /* Here we should do something with variable expansion and `command`. Maybe a dream - UNIMPLEMENTED yet. */ directory = mc_opendir (dirname_vpath); filename_len = strlen (filename); } /* Now that we have some state, we can read the directory. */ while (directory && (entry = mc_readdir (directory))) { if (!str_is_valid_string (entry->d_name)) continue; /* Special case for no filename. All entries except "." and ".." match. */ if (filename_len == 0) { if (DIR_IS_DOT (entry->d_name) || DIR_IS_DOTDOT (entry->d_name)) continue; } else { /* Otherwise, if these match up to the length of filename, then it may be a match. */ if ((entry->d_name[0] != filename[0]) || ((NLENGTH (entry)) < filename_len) || strncmp (filename, entry->d_name, filename_len)) continue; } isdir = 1; isexec = 0; { struct stat tempstat; vfs_path_t *tmp_vpath; tmp_vpath = vfs_path_build_filename (dirname, entry->d_name, (char *) NULL); /* Unix version */ if (mc_stat (tmp_vpath, &tempstat) == 0) { uid_t my_uid = getuid (); gid_t my_gid = getgid (); if (!S_ISDIR (tempstat.st_mode)) { isdir = 0; if ((!my_uid && (tempstat.st_mode & 0111)) || (my_uid == tempstat.st_uid && (tempstat.st_mode & 0100)) || (my_gid == tempstat.st_gid && (tempstat.st_mode & 0010)) || (tempstat.st_mode & 0001)) isexec = 1; } } else { /* stat failed, strange. not a dir in any case */ isdir = 0; } vfs_path_free (tmp_vpath); } if ((flags & INPUT_COMPLETE_COMMANDS) && (isexec || isdir)) break; if ((flags & INPUT_COMPLETE_CD) && isdir) break; if (flags & (INPUT_COMPLETE_FILENAMES)) break; } if (entry == NULL) { if (directory) { mc_closedir (directory); directory = NULL; } g_free (dirname); dirname = NULL; vfs_path_free (dirname_vpath); dirname_vpath = NULL; g_free (filename); filename = NULL; g_free (users_dirname); users_dirname = NULL; return NULL; } { GString *temp; temp = g_string_sized_new (16); if (users_dirname != NULL && (users_dirname[0] != '.' || users_dirname[1] != '\0')) { g_string_append (temp, users_dirname); /* We need a '/' at the end. */ if (temp->str[temp->len - 1] != PATH_SEP) g_string_append_c (temp, PATH_SEP); } g_string_append (temp, entry->d_name); if (isdir) g_string_append_c (temp, PATH_SEP); return g_string_free (temp, FALSE); } }
static char * command_completion_function (const char *_text, int state, input_complete_t flags) { char *text; static const char *path_end; static gboolean isabsolute; static int phase; static size_t text_len; static const char *const *words; static char *path; static char *cur_path; static char *cur_word; static int init_state; static const char *const bash_reserved[] = { "if", "then", "else", "elif", "fi", "case", "esac", "for", "select", "while", "until", "do", "done", "in", "function", 0 }; static const char *const bash_builtins[] = { "alias", "bg", "bind", "break", "builtin", "cd", "command", "continue", "declare", "dirs", "echo", "enable", "eval", "exec", "exit", "export", "fc", "fg", "getopts", "hash", "help", "history", "jobs", "kill", "let", "local", "logout", "popd", "pushd", "pwd", "read", "readonly", "return", "set", "shift", "source", "suspend", "test", "times", "trap", "type", "typeset", "ulimit", "umask", "unalias", "unset", "wait", 0 }; char *p, *found; SHOW_C_CTX ("command_completion_function"); if (!(flags & INPUT_COMPLETE_COMMANDS)) return 0; text = strutils_shell_unescape (_text); flags &= ~INPUT_COMPLETE_SHELL_ESC; if (state == 0) { /* Initialize us a little bit */ isabsolute = strchr (text, PATH_SEP) != NULL; if (!isabsolute) { words = bash_reserved; phase = 0; text_len = strlen (text); if (path == NULL) { path = g_strdup (getenv ("PATH")); if (path != NULL) { p = path; path_end = strchr (p, '\0'); while ((p = strchr (p, PATH_ENV_SEP)) != NULL) { *p++ = '\0'; } } } } } if (isabsolute) { p = filename_completion_function (text, state, flags); if (p != NULL) { char *temp_p = p; p = strutils_shell_escape (p); g_free (temp_p); } g_free (text); return p; } found = NULL; switch (phase) { case 0: /* Reserved words */ while (*words) { if (strncmp (*words, text, text_len) == 0) { g_free (text); return g_strdup (*(words++)); } words++; } phase++; words = bash_builtins; case 1: /* Builtin commands */ while (*words) { if (strncmp (*words, text, text_len) == 0) { g_free (text); return g_strdup (*(words++)); } words++; } phase++; if (!path) break; cur_path = path; cur_word = NULL; case 2: /* And looking through the $PATH */ while (!found) { if (!cur_word) { char *expanded; if (cur_path >= path_end) break; expanded = tilde_expand (*cur_path ? cur_path : "."); cur_word = mc_build_filename (expanded, text, NULL); g_free (expanded); canonicalize_pathname (cur_word); cur_path = strchr (cur_path, 0) + 1; init_state = state; } found = filename_completion_function (cur_word, state - init_state, flags); if (!found) { g_free (cur_word); cur_word = NULL; } } } if (found == NULL) { g_free (path); path = NULL; } else { p = strrchr (found, PATH_SEP); if (p != NULL) { char *tmp = found; found = strutils_shell_escape (p + 1); g_free (tmp); } } g_free (text); return found; }