2 * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 readline(const char *prompt)
27 char *ch, *line = NULL;
34 linelen = getline(&line, &linesize, stdin);
38 if ((ch = strchr(line, '\n')) != NULL)
44 add_history(const char *line)
55 #else /* HAVE_READLINE */
63 #include <readline/readline.h>
64 #include <readline/history.h>
75 static struct compl_state compl_state;
76 static char compl_prfx[PATH_MAX];
79 compl_state_reset(void)
83 for (i = 0; i < compl_state.len; ++i)
84 free(compl_state.entries[i]);
85 free(compl_state.entries);
87 memset(&compl_state, 0, sizeof(compl_state));
91 compl_add_entry(const struct np_stat *st)
93 const char *sufx = "";
97 if (compl_state.len == compl_state.size) {
98 size_t newsz = compl_state.size * 1.5;
104 /* one for the NULL entry at the end */
105 t = recallocarray(compl_state.entries, compl_state.size,
106 newsz + 1, sizeof(char *));
109 compl_state.entries = t;
110 compl_state.size = newsz;
113 if (st->qid.type & QTDIR)
116 if (asprintf(&dup, "%s%s%s", compl_prfx, st->name, sufx) == -1)
118 compl_state.entries[compl_state.len++] = dup;
123 cleanword(char *buf, int brkspc)
128 while (brkspc && isspace((unsigned char)*buf))
129 memmove(buf, buf + 1, strlen(buf));
132 for (cmd = buf; *cmd != '\0'; ++cmd) {
143 if (*cmd == '\'' || *cmd == '"') {
149 if (brkspc && isspace((unsigned char)*cmd))
154 memmove(cmd, cmd + 1, strlen(cmd));
166 for (i = 0; i < nitems(cmds); ++i) {
167 if (!strcmp(cmds[i].name, buf))
168 return cmds[i].cmdtype;
175 tell_argno(const char *cmd, int *cmdtype)
177 char cmd0[64]; /* plenty of space */
178 const char *start = cmd;
182 *cmdtype = CMD_UNKNOWN;
184 /* find which argument needs to be completed */
186 while (isspace((unsigned char)*cmd))
192 for (; *cmd; ++cmd) {
205 if (*cmd == '\'' || *cmd == '\"') {
211 if (isspace((unsigned char)*cmd))
214 if (isspace((unsigned char)*cmd))
217 if (argno == 1 && strlcpy(cmd0, start, sizeof(cmd0)) <
219 *cmdtype = tellcmd(cmd0);
226 ftp_cmdname_generator(const char *text, int state)
228 static size_t i, len;
236 while (i < nitems(cmds)) {
238 if (strncmp(text, cmd->name, len) == 0)
239 return strdup(cmd->name);
246 ftp_bool_generator(const char *text, int state)
248 static const char *toks[] = { "on", "off" };
249 static size_t i, len;
257 while ((tok = toks[i++]) != NULL) {
258 if (strncmp(text, tok, len) == 0)
265 ftp_dirent_generator(const char *text, int state)
267 static size_t i, len;
275 while (i < compl_state.len) {
276 entry = compl_state.entries[i++];
277 if (strncmp(text, entry, len) == 0)
278 return strdup(entry);
284 ftp_remote_files(const char *text, int start, int end)
290 strlcpy(t, text, sizeof(t));
293 if (!strcmp(t, "..")) {
295 if ((cs = calloc(2, sizeof(*cs))) == NULL)
297 cs[0] = strdup("../");
302 if (!strncmp(s, "./", 2)) {
308 if ((e = strrchr(s, '/')) != NULL)
312 if (!strcmp(dir, "."))
313 strlcpy(compl_prfx, "", sizeof(compl_prfx));
315 strlcpy(compl_prfx, dir, sizeof(compl_prfx));
318 if (dir_listing(dir, compl_add_entry, 0) == -1)
320 return rl_completion_matches(text, ftp_dirent_generator);
324 ftp_completion(const char *text, int start, int end)
329 /* don't fall back on the default completion system by default */
330 rl_attempted_completion_over = 1;
332 if ((line = rl_copy_text(0, start)) == NULL)
335 argno = tell_argno(line, &cmdtype);
338 return rl_completion_matches(text, ftp_cmdname_generator);
346 return rl_completion_matches(text, ftp_bool_generator);
361 return ftp_remote_files(text, start, end);
367 return ftp_remote_files(text, start, end);
369 rl_attempted_completion_over = 0;
376 rl_attempted_completion_over = 0;
383 return ftp_remote_files(text, start, end);
385 rl_attempted_completion_over = 0;
393 rl_attempted_completion_over = 0;
396 return ftp_remote_files(text, start, end);
401 return ftp_remote_files(text, start, end);
408 ftp_quoted(char *line, int index)
410 if (index > 0 && line[index - 1] == '\\')
411 return !ftp_quoted(line, index - 1);
418 rl_attempted_completion_function = ftp_completion;
419 rl_completer_word_break_characters = "\t ";
420 rl_completer_quote_characters = "\"'";
421 rl_char_is_quoted_p = ftp_quoted;