56d0d5012f
Add auto-completion for path files. Signed-off-by: Alexander Aring <a.aring@phytec.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
244 lines
4.4 KiB
C
244 lines
4.4 KiB
C
/*
|
|
* complete.c - functions for TAB completion
|
|
*
|
|
* Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <complete.h>
|
|
#include <xfuncs.h>
|
|
#include <linux/list.h>
|
|
#include <malloc.h>
|
|
#include <fs.h>
|
|
#include <linux/stat.h>
|
|
#include <libgen.h>
|
|
#include <command.h>
|
|
#include <stringlist.h>
|
|
#include <environment.h>
|
|
|
|
static int file_complete(struct string_list *sl, char *instr)
|
|
{
|
|
char *path = strdup(instr);
|
|
struct stat s;
|
|
DIR *dir;
|
|
struct dirent *d;
|
|
char tmp[PATH_MAX];
|
|
char *base, *dirn;
|
|
|
|
base = basename(instr);
|
|
dirn = dirname(path);
|
|
|
|
dir = opendir(dirn);
|
|
if (!dir)
|
|
goto out;
|
|
|
|
while ((d = readdir(dir))) {
|
|
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
|
continue;
|
|
|
|
if (!strncmp(base, d->d_name, strlen(base))) {
|
|
strcpy(tmp, instr);
|
|
strcat(tmp, d->d_name + strlen(base));
|
|
if (!stat(tmp, &s) && S_ISDIR(s.st_mode))
|
|
strcat(tmp, "/");
|
|
else
|
|
strcat(tmp, " ");
|
|
string_list_add_sorted(sl, tmp);
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
out:
|
|
free(path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int path_command_complete(struct string_list *sl, char *instr)
|
|
{
|
|
struct stat s;
|
|
DIR *dir;
|
|
struct dirent *d;
|
|
char tmp[PATH_MAX];
|
|
char *path, *p, *n;
|
|
|
|
p = path = strdup(getenv("PATH"));
|
|
|
|
if (!path)
|
|
return -1;
|
|
|
|
while (p) {
|
|
n = strchr(p, ':');
|
|
if (n)
|
|
*n++ = '\0';
|
|
if (*p == '\0') {
|
|
p = n;
|
|
continue;
|
|
}
|
|
dir = opendir(p);
|
|
|
|
/* We need to check all PATH dirs, so if one failed,
|
|
* try next */
|
|
if (!dir) {
|
|
p = n;
|
|
continue;
|
|
}
|
|
|
|
while ((d = readdir(dir))) {
|
|
if (!strcmp(d->d_name, ".") ||
|
|
!strcmp(d->d_name, ".."))
|
|
continue;
|
|
|
|
if (!strncmp(instr, d->d_name, strlen(instr))) {
|
|
strcpy(tmp, d->d_name);
|
|
if (!stat(tmp, &s) &&
|
|
S_ISDIR(s.st_mode))
|
|
continue;
|
|
else
|
|
strcat(tmp, " ");
|
|
|
|
/* This function is called
|
|
* after command_complete,
|
|
* so we check if a double
|
|
* entry exist */
|
|
if (string_list_contains
|
|
(sl, tmp) == 0) {
|
|
string_list_add_sorted(sl, tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
p = n;
|
|
}
|
|
|
|
free(path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int command_complete(struct string_list *sl, char *instr)
|
|
{
|
|
struct command *cmdtp;
|
|
char cmd[128];
|
|
|
|
for_each_command(cmdtp) {
|
|
if (!strncmp(instr, cmdtp->name, strlen(instr))) {
|
|
strcpy(cmd, cmdtp->name);
|
|
cmd[strlen(cmdtp->name)] = ' ';
|
|
cmd[strlen(cmdtp->name) + 1] = 0;
|
|
string_list_add(sl, cmd);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tab_pressed = 0;
|
|
|
|
void complete_reset(void)
|
|
{
|
|
tab_pressed = 0;
|
|
}
|
|
|
|
int complete(char *instr, char **outstr)
|
|
{
|
|
struct string_list sl, *entry, *first_entry;
|
|
int pos;
|
|
char ch;
|
|
int changed;
|
|
static char out[256];
|
|
int outpos = 0;
|
|
int reprint = 0;
|
|
char *t;
|
|
|
|
string_list_init(&sl);
|
|
|
|
/* advance to the last command */
|
|
t = strrchr(instr, ';');
|
|
if (!t)
|
|
t = instr;
|
|
else
|
|
t++;
|
|
|
|
while (*t == ' ')
|
|
t++;
|
|
|
|
instr = t;
|
|
|
|
/* get the completion possibilities */
|
|
if ((t = strrchr(t, ' '))) {
|
|
t++;
|
|
file_complete(&sl, t);
|
|
instr = t;
|
|
} else {
|
|
command_complete(&sl, instr);
|
|
path_command_complete(&sl, instr);
|
|
}
|
|
|
|
pos = strlen(instr);
|
|
|
|
*outstr = "";
|
|
if (list_empty(&sl.list))
|
|
return reprint;
|
|
|
|
out[0] = 0;
|
|
|
|
first_entry = list_first_entry(&sl.list, struct string_list, list);
|
|
|
|
while (1) {
|
|
entry = first_entry;
|
|
ch = entry->str[pos];
|
|
if (!ch)
|
|
break;
|
|
|
|
changed = 0;
|
|
list_for_each_entry(entry, &sl.list, list) {
|
|
if (!entry->str[pos])
|
|
break;
|
|
if (ch != entry->str[pos]) {
|
|
changed = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changed)
|
|
break;
|
|
out[outpos++] = ch;
|
|
pos++;
|
|
}
|
|
|
|
if (!list_is_last(&first_entry->list, &sl.list) && !outpos && tab_pressed) {
|
|
printf("\n");
|
|
string_list_print_by_column(&sl);
|
|
reprint = 1;
|
|
}
|
|
|
|
out[outpos++] = 0;
|
|
*outstr = out;
|
|
|
|
if (*out == 0)
|
|
tab_pressed = 1;
|
|
else
|
|
tab_pressed = 0;
|
|
|
|
string_list_free(&sl);
|
|
|
|
return reprint;
|
|
}
|
|
|