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