1 /*
2  * Copyright 2008-2013 Various Authors
3  * Copyright 2004-2005 Timo Hirvonen
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "tabexp_file.h"
20 #include "tabexp.h"
21 #include "load_dir.h"
22 #include "misc.h"
23 #include "xmalloc.h"
24 #include "xstrjoin.h"
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <pwd.h>
30 #include <dirent.h>
31 
get_home(const char * user)32 static char *get_home(const char *user)
33 {
34 	struct passwd *passwd;
35 	char *home;
36 	int len;
37 
38 	if (user[0] == 0) {
39 		passwd = getpwuid(getuid());
40 	} else {
41 		passwd = getpwnam(user);
42 	}
43 	if (passwd == NULL)
44 		return NULL;
45 	len = strlen(passwd->pw_dir);
46 	home = xnew(char, len + 2);
47 	memcpy(home, passwd->pw_dir, len);
48 	home[len] = '/';
49 	home[len + 1] = 0;
50 	return home;
51 }
52 
get_full_dir_name(const char * dir)53 static char *get_full_dir_name(const char *dir)
54 {
55 	char *full;
56 
57 	if (dir[0] == 0) {
58 		full = xstrdup("./");
59 	} else if (dir[0] == '~') {
60 		char *first_slash, *tmp, *home;
61 
62 		first_slash = strchr(dir, '/');
63 		tmp = xstrndup(dir, first_slash - dir);
64 		home = get_home(tmp + 1);
65 		free(tmp);
66 		if (home == NULL)
67 			return NULL;
68 		full = xstrjoin(home, first_slash);
69 		free(home);
70 	} else {
71 		full = xstrdup(dir);
72 	}
73 	return full;
74 }
75 
load_dir(struct ptr_array * array,const char * dirname,const char * start,int (* filter)(const char *,const struct stat *))76 static void load_dir(struct ptr_array *array,
77 		const char *dirname, const char *start,
78 		int (*filter)(const char *, const struct stat *))
79 {
80 	int start_len = strlen(start);
81 	struct directory dir;
82 	char *full_dir_name;
83 	const char *name;
84 
85 	full_dir_name = get_full_dir_name(dirname);
86 	if (!full_dir_name)
87 		return;
88 
89 	if (dir_open(&dir, full_dir_name))
90 		goto out;
91 
92 	while ((name = dir_read(&dir))) {
93 		char *str;
94 
95 		if (!start_len) {
96 			if (name[0] == '.')
97 				continue;
98 		} else {
99 			if (strncmp(name, start, start_len))
100 				continue;
101 		}
102 
103 		if (!filter(name, &dir.st))
104 			continue;
105 
106 		if (S_ISDIR(dir.st.st_mode)) {
107 			int len = strlen(name);
108 
109 			str = xnew(char, len + 2);
110 			memcpy(str, name, len);
111 			str[len++] = '/';
112 			str[len] = 0;
113 		} else {
114 			str = xstrdup(name);
115 		}
116 		ptr_array_add(array, str);
117 	}
118 	dir_close(&dir);
119 out:
120 	free(full_dir_name);
121 }
122 
123 /*
124  * load all directory entries from directory 'dir' starting with 'start' and
125  * filtered with 'filter'
126  */
tabexp_load_dir(const char * dirname,const char * start,int (* filter)(const char *,const struct stat *))127 static void tabexp_load_dir(const char *dirname, const char *start,
128 		int (*filter)(const char *, const struct stat *))
129 {
130 	PTR_ARRAY(array);
131 
132 	/* tabexp is reset */
133 	load_dir(&array, dirname, start, filter);
134 
135 	if (array.count) {
136 		ptr_array_sort(&array, strptrcmp);
137 
138 		tabexp.head = xstrdup(dirname);
139 		tabexp.tails = array.ptrs;
140 		tabexp.count = array.count;
141 	}
142 }
143 
tabexp_load_env_path(const char * env_path,const char * start,int (* filter)(const char *,const struct stat *))144 static void tabexp_load_env_path(const char *env_path, const char *start,
145 		int (*filter)(const char *, const struct stat *))
146 {
147 	char *path = xstrdup(env_path);
148 	PTR_ARRAY(array);
149 	char cwd[1024];
150 	char *p = path, *n;
151 
152 	/* tabexp is reset */
153 	do {
154 		n = strchr(p, ':');
155 		if (n)
156 			*n = '\0';
157 		if (strcmp(p, "") == 0 && getcwd(cwd, sizeof(cwd)))
158 			p = cwd;
159 		load_dir(&array, p, start, filter);
160 		p = n + 1;
161 	} while (n);
162 
163 	if (array.count) {
164 		ptr_array_sort(&array, strptrcoll);
165 		ptr_array_unique(&array, strptrcmp);
166 
167 		tabexp.head = xstrdup("");
168 		tabexp.tails = array.ptrs;
169 		tabexp.count = array.count;
170 	}
171 
172 	free(path);
173 }
174 
expand_files_and_dirs(const char * src,int (* filter)(const char * name,const struct stat * s))175 void expand_files_and_dirs(const char *src,
176 		int (*filter)(const char *name, const struct stat *s))
177 {
178 	char *slash;
179 
180 	/* split src to dir and file */
181 	slash = strrchr(src, '/');
182 	if (slash) {
183 		char *dir;
184 		const char *file;
185 
186 		/* split */
187 		dir = xstrndup(src, slash - src + 1);
188 		file = slash + 1;
189 		/* get all dentries starting with file from dir */
190 		tabexp_load_dir(dir, file, filter);
191 		free(dir);
192 	} else {
193 		if (src[0] == '~') {
194 			char *home = get_home(src + 1);
195 
196 			if (home) {
197 				tabexp.head = xstrdup("");
198 				tabexp.tails = xnew(char *, 1);
199 				tabexp.tails[0] = home;
200 				tabexp.count = 1;
201 			}
202 		} else {
203 			tabexp_load_dir("", src, filter);
204 		}
205 	}
206 }
207 
expand_env_path(const char * src,int (* filter)(const char * name,const struct stat * s))208 void expand_env_path(const char *src,
209 		int (*filter)(const char *name, const struct stat *s))
210 {
211 	const char *env_path = getenv("PATH");
212 
213 	if (!env_path || strcmp(env_path, "") == 0)
214 		return;
215 
216 	tabexp_load_env_path(env_path, src, filter);
217 }
218