218 lines
4.5 KiB
C
218 lines
4.5 KiB
C
/*
|
|
* Copyright 2008-2013 Various Authors
|
|
* Copyright 2004-2005 Timo Hirvonen
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "tabexp_file.h"
|
|
#include "tabexp.h"
|
|
#include "load_dir.h"
|
|
#include "misc.h"
|
|
#include "xmalloc.h"
|
|
#include "xstrjoin.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include <dirent.h>
|
|
|
|
static char *get_home(const char *user)
|
|
{
|
|
struct passwd *passwd;
|
|
char *home;
|
|
int len;
|
|
|
|
if (user[0] == 0) {
|
|
passwd = getpwuid(getuid());
|
|
} else {
|
|
passwd = getpwnam(user);
|
|
}
|
|
if (passwd == NULL)
|
|
return NULL;
|
|
len = strlen(passwd->pw_dir);
|
|
home = xnew(char, len + 2);
|
|
memcpy(home, passwd->pw_dir, len);
|
|
home[len] = '/';
|
|
home[len + 1] = 0;
|
|
return home;
|
|
}
|
|
|
|
static char *get_full_dir_name(const char *dir)
|
|
{
|
|
char *full;
|
|
|
|
if (dir[0] == 0) {
|
|
full = xstrdup("./");
|
|
} else if (dir[0] == '~') {
|
|
char *first_slash, *tmp, *home;
|
|
|
|
first_slash = strchr(dir, '/');
|
|
tmp = xstrndup(dir, first_slash - dir);
|
|
home = get_home(tmp + 1);
|
|
free(tmp);
|
|
if (home == NULL)
|
|
return NULL;
|
|
full = xstrjoin(home, first_slash);
|
|
free(home);
|
|
} else {
|
|
full = xstrdup(dir);
|
|
}
|
|
return full;
|
|
}
|
|
|
|
static void load_dir(struct ptr_array *array,
|
|
const char *dirname, const char *start,
|
|
int (*filter)(const char *, const struct stat *))
|
|
{
|
|
int start_len = strlen(start);
|
|
struct directory dir;
|
|
char *full_dir_name;
|
|
const char *name;
|
|
|
|
full_dir_name = get_full_dir_name(dirname);
|
|
if (!full_dir_name)
|
|
return;
|
|
|
|
if (dir_open(&dir, full_dir_name))
|
|
goto out;
|
|
|
|
while ((name = dir_read(&dir))) {
|
|
char *str;
|
|
|
|
if (!start_len) {
|
|
if (name[0] == '.')
|
|
continue;
|
|
} else {
|
|
if (strncmp(name, start, start_len))
|
|
continue;
|
|
}
|
|
|
|
if (!filter(name, &dir.st))
|
|
continue;
|
|
|
|
if (S_ISDIR(dir.st.st_mode)) {
|
|
int len = strlen(name);
|
|
|
|
str = xnew(char, len + 2);
|
|
memcpy(str, name, len);
|
|
str[len++] = '/';
|
|
str[len] = 0;
|
|
} else {
|
|
str = xstrdup(name);
|
|
}
|
|
ptr_array_add(array, str);
|
|
}
|
|
dir_close(&dir);
|
|
out:
|
|
free(full_dir_name);
|
|
}
|
|
|
|
/*
|
|
* load all directory entries from directory 'dir' starting with 'start' and
|
|
* filtered with 'filter'
|
|
*/
|
|
static void tabexp_load_dir(const char *dirname, const char *start,
|
|
int (*filter)(const char *, const struct stat *))
|
|
{
|
|
PTR_ARRAY(array);
|
|
|
|
/* tabexp is reset */
|
|
load_dir(&array, dirname, start, filter);
|
|
|
|
if (array.count) {
|
|
ptr_array_sort(&array, strptrcmp);
|
|
|
|
tabexp.head = xstrdup(dirname);
|
|
tabexp.tails = array.ptrs;
|
|
tabexp.count = array.count;
|
|
}
|
|
}
|
|
|
|
static void tabexp_load_env_path(const char *env_path, const char *start,
|
|
int (*filter)(const char *, const struct stat *))
|
|
{
|
|
char *path = xstrdup(env_path);
|
|
PTR_ARRAY(array);
|
|
char cwd[1024];
|
|
char *p = path, *n;
|
|
|
|
/* tabexp is reset */
|
|
do {
|
|
n = strchr(p, ':');
|
|
if (n)
|
|
*n = '\0';
|
|
if (strcmp(p, "") == 0 && getcwd(cwd, sizeof(cwd)))
|
|
p = cwd;
|
|
load_dir(&array, p, start, filter);
|
|
p = n + 1;
|
|
} while (n);
|
|
|
|
if (array.count) {
|
|
ptr_array_sort(&array, strptrcoll);
|
|
ptr_array_unique(&array, strptrcmp);
|
|
|
|
tabexp.head = xstrdup("");
|
|
tabexp.tails = array.ptrs;
|
|
tabexp.count = array.count;
|
|
}
|
|
|
|
free(path);
|
|
}
|
|
|
|
void expand_files_and_dirs(const char *src,
|
|
int (*filter)(const char *name, const struct stat *s))
|
|
{
|
|
char *slash;
|
|
|
|
/* split src to dir and file */
|
|
slash = strrchr(src, '/');
|
|
if (slash) {
|
|
char *dir;
|
|
const char *file;
|
|
|
|
/* split */
|
|
dir = xstrndup(src, slash - src + 1);
|
|
file = slash + 1;
|
|
/* get all dentries starting with file from dir */
|
|
tabexp_load_dir(dir, file, filter);
|
|
free(dir);
|
|
} else {
|
|
if (src[0] == '~') {
|
|
char *home = get_home(src + 1);
|
|
|
|
if (home) {
|
|
tabexp.head = xstrdup("");
|
|
tabexp.tails = xnew(char *, 1);
|
|
tabexp.tails[0] = home;
|
|
tabexp.count = 1;
|
|
}
|
|
} else {
|
|
tabexp_load_dir("", src, filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
void expand_env_path(const char *src,
|
|
int (*filter)(const char *name, const struct stat *s))
|
|
{
|
|
const char *env_path = getenv("PATH");
|
|
|
|
if (!env_path || strcmp(env_path, "") == 0)
|
|
return;
|
|
|
|
tabexp_load_env_path(env_path, src, filter);
|
|
}
|