Line 0
Link Here
|
|
|
1 |
/* |
2 |
* Copyright 2005-2006 Gentoo Foundation |
3 |
* Distributed under the terms of the GNU General Public License v2 |
4 |
* $Header: /var/cvsroot/gentoo-projects/portage-utils/qfile.c,v 1.42 2007/01/13 19:17:39 solar Exp $ |
5 |
* |
6 |
* Copyright 2005-2006 Ned Ludd - <solar@gentoo.org> |
7 |
* Copyright 2005-2006 Mike Frysinger - <vapier@gentoo.org> |
8 |
*/ |
9 |
|
10 |
#ifdef APPLET_qfile |
11 |
|
12 |
#define QFILE_MAX_MAX_ARGS 5000000 |
13 |
#define QFILE_DEFAULT_MAX_ARGS 5000 |
14 |
#define QFILE_DEFAULT_MAX_ARGS_STR "5000" |
15 |
|
16 |
#define QFILE_FLAGS "ef:m:oRx:" COMMON_FLAGS |
17 |
static struct option const qfile_long_opts[] = { |
18 |
{"exact", no_argument, NULL, 'e'}, |
19 |
{"from", a_argument, NULL, 'f'}, |
20 |
{"max-args", a_argument, NULL, 'm'}, |
21 |
{"orphans", no_argument, NULL, 'o'}, |
22 |
{"root-prefix", no_argument, NULL, 'R'}, |
23 |
{"exclude", a_argument, NULL, 'x'}, |
24 |
COMMON_LONG_OPTS |
25 |
}; |
26 |
static const char *qfile_opts_help[] = { |
27 |
"Exact match", |
28 |
"Read arguments from file <arg> (\"-\" for stdin)", |
29 |
"Treat from file arguments by groups of <arg> (defaults to " QFILE_DEFAULT_MAX_ARGS_STR ")", |
30 |
"List orphan files", |
31 |
"Assume arguments are already prefixed by $ROOT", |
32 |
"Don't look in package <arg>", |
33 |
COMMON_OPTS_HELP |
34 |
}; |
35 |
static char qfile_rcsid[] = "$Id: qfile.c,v 1.42 2007/01/13 19:17:39 solar Exp $"; |
36 |
#define qfile_usage(ret) usage(ret, QFILE_FLAGS, qfile_long_opts, qfile_opts_help, lookup_applet_idx("qfile")) |
37 |
|
38 |
static inline short qfile_is_prefix(const char* path, const char* prefix, int prefix_length) |
39 |
{ |
40 |
return !prefix_length |
41 |
|| (strlen(path) >= prefix_length |
42 |
&& (path[prefix_length] == '/' || path[prefix_length] == '\0') |
43 |
&& !strncmp(path, prefix, prefix_length)); |
44 |
} |
45 |
|
46 |
typedef struct { |
47 |
int length; |
48 |
char **basenames; |
49 |
char **dirnames; |
50 |
char **realdirnames; |
51 |
char *bn_firstchars; |
52 |
short *non_orphans; |
53 |
char *real_root; |
54 |
char *exclude_pkg; |
55 |
char *exclude_slot; |
56 |
} qfile_args_t; |
57 |
|
58 |
void qfile(char *, const char *, qfile_args_t *); |
59 |
void qfile(char *path, const char *root, qfile_args_t *args) |
60 |
{ |
61 |
FILE *fp; |
62 |
DIR *dir; |
63 |
struct dirent *dentry; |
64 |
char *entry_basename; |
65 |
char *entry_dirname; |
66 |
char *p; |
67 |
char buf[1024]; |
68 |
char pkg[126]; |
69 |
depend_atom *atom; |
70 |
int i, path_ok; |
71 |
char bn_firstchar; |
72 |
char *real_root = args->real_root; |
73 |
char **base_names = args->basenames; |
74 |
char **dir_names = args->dirnames; |
75 |
char **real_dir_names = args->realdirnames; |
76 |
char *bn_firstchars = args->bn_firstchars; |
77 |
short *non_orphans = args->non_orphans; |
78 |
|
79 |
if (chdir(path) != 0 || (dir = opendir(".")) == NULL) |
80 |
return; |
81 |
|
82 |
while ((dentry = readdir(dir))) { |
83 |
if (dentry->d_name[0] == '.') |
84 |
continue; |
85 |
|
86 |
snprintf(pkg, sizeof(pkg), "%s/%s", basename(path), dentry->d_name); |
87 |
atom = NULL; /* Will be exploded once at most, as needed. */ |
88 |
|
89 |
/* If exclude_pkg is not NULL, check it. We are looking for files |
90 |
* collisions, and must exclude one package. |
91 |
*/ |
92 |
if (args->exclude_pkg != NULL) { |
93 |
if (strncmp(args->exclude_pkg, pkg, sizeof(pkg)) == 0) |
94 |
goto check_pkg_slot; /* CAT/PF matches */ |
95 |
if (strcmp(args->exclude_pkg, dentry->d_name) == 0) |
96 |
goto check_pkg_slot; /* PF matches */ |
97 |
if ((atom = atom_explode(pkg)) == NULL |
98 |
|| atom->PN == NULL || atom->CATEGORY == NULL) { |
99 |
warn("invalid atom %s", pkg); |
100 |
goto dont_skip_pkg; |
101 |
} |
102 |
snprintf(buf, sizeof(buf), "%s/%s", atom->CATEGORY, atom->PN); |
103 |
if (strncmp(args->exclude_pkg, buf, sizeof(buf)) != 0 |
104 |
&& strcmp(args->exclude_pkg, atom->PN) != 0) |
105 |
goto dont_skip_pkg; /* "(CAT/)?PN" doesn't match */ |
106 |
check_pkg_slot: /* Also compare slots, if any was specified */ |
107 |
if (args->exclude_slot == NULL) { |
108 |
if (atom != NULL) |
109 |
atom_implode(atom); |
110 |
continue; /* "(CAT/)?(PN|PF)" matches, and no SLOT specified */ |
111 |
} |
112 |
buf[0] = '0'; buf[1] = '\0'; |
113 |
xasprintf(&p, "%s/%s/SLOT", path, dentry->d_name); |
114 |
if ((fp = fopen(p, "r")) != NULL) { |
115 |
free(p); |
116 |
if (fgets(buf, sizeof(buf), fp) != NULL) |
117 |
if ((p = strchr(buf, '\n')) != NULL) |
118 |
*p = 0; |
119 |
fclose(fp); |
120 |
} else { |
121 |
free(p); |
122 |
} |
123 |
if (strncmp(args->exclude_slot, buf, sizeof(buf)) == 0) { |
124 |
if (atom != NULL) |
125 |
atom_implode(atom); |
126 |
continue; /* "(CAT/)?(PN|PF):SLOT" matches */ |
127 |
} |
128 |
} |
129 |
dont_skip_pkg: /* End of the package exclusion tests. */ |
130 |
|
131 |
xasprintf(&p, "%s/%s/CONTENTS", path, dentry->d_name); |
132 |
if ((fp = fopen(p, "r")) == NULL) { |
133 |
free(p); |
134 |
if (atom != NULL) |
135 |
atom_implode(atom); |
136 |
continue; |
137 |
} |
138 |
free(p); |
139 |
|
140 |
while ((fgets(buf, sizeof(buf), fp)) != NULL) { |
141 |
contents_entry *e; |
142 |
e = contents_parse_line(buf); |
143 |
if (!e) |
144 |
continue; |
145 |
|
146 |
/* much faster than using basename(), since no need to strdup */ |
147 |
if ((entry_basename = strrchr(e->name, '/')) == NULL) |
148 |
continue; |
149 |
entry_basename++; |
150 |
|
151 |
/* used to cut the number of strcmp() calls */ |
152 |
bn_firstchar = entry_basename[0]; |
153 |
|
154 |
for (i = 0; i < args->length; i++) { |
155 |
if (base_names[i] == NULL) |
156 |
continue; |
157 |
if (non_orphans != NULL && non_orphans[i]) |
158 |
continue; |
159 |
path_ok = (dir_names[i] == NULL && real_dir_names[i] == NULL); |
160 |
|
161 |
if (bn_firstchar != bn_firstchars[i] |
162 |
|| strcmp(entry_basename, base_names[i])) |
163 |
continue; |
164 |
|
165 |
|
166 |
if (!path_ok) { |
167 |
/* check the full filepath ... */ |
168 |
entry_dirname = xstrdup(e->name); |
169 |
if ((p = strrchr(entry_dirname, '/')) == NULL) { |
170 |
free(entry_dirname); |
171 |
continue; |
172 |
} |
173 |
if (p == entry_dirname) |
174 |
/* (e->name == "/foo") ==> dirname == "/" */ |
175 |
*(p + 1) = '\0'; |
176 |
else |
177 |
*p = '\0'; |
178 |
if (dir_names[i] != NULL && |
179 |
strcmp(entry_dirname, dir_names[i]) == 0) |
180 |
/* dir_name == dirname(CONTENTS) */ |
181 |
path_ok = 1; |
182 |
else if (real_dir_names[i] != NULL && |
183 |
strcmp(entry_dirname, real_dir_names[i]) == 0) |
184 |
/* real_dir_name == dirname(CONTENTS) */ |
185 |
path_ok = 1; |
186 |
else { |
187 |
char rpath[_Q_PATH_MAX+1]; |
188 |
char *fullpath = entry_dirname; |
189 |
errno = 0; |
190 |
if (real_root != NULL && real_root[0] != '\0') |
191 |
xasprintf(&fullpath, "%s%s", real_root, entry_dirname); |
192 |
realpath(fullpath, rpath); |
193 |
if (errno != 0) { |
194 |
if (verbose) { |
195 |
warnp("Could not read real path of \"%s\" (from %s)", fullpath, pkg); |
196 |
warn("We'll never know whether \"%s/%s\" was a result for your query...", |
197 |
entry_dirname, entry_basename); |
198 |
} |
199 |
} else if (!qfile_is_prefix(rpath, real_root, strlen(real_root))) { |
200 |
if (verbose) |
201 |
warn("Real path of \"%s\" is not under ROOT: %s", fullpath, rpath); |
202 |
} else if (dir_names[i] != NULL && |
203 |
strcmp(rpath + strlen(real_root), dir_names[i]) == 0) |
204 |
/* dir_name == realpath(dirname(CONTENTS)) */ |
205 |
path_ok = 1; |
206 |
else if (real_dir_names[i] != NULL && |
207 |
strcmp(rpath + strlen(real_root), real_dir_names[i]) == 0) |
208 |
/* real_dir_name == realpath(dirname(CONTENTS)) */ |
209 |
path_ok = 1; |
210 |
if (fullpath != entry_dirname) |
211 |
free(fullpath); |
212 |
} |
213 |
free(entry_dirname); |
214 |
} |
215 |
if (!path_ok) |
216 |
continue; |
217 |
|
218 |
if (non_orphans == NULL) { |
219 |
if (atom == NULL && (atom = atom_explode(pkg)) == NULL) { |
220 |
warn("invalid atom %s", pkg); |
221 |
continue; |
222 |
} |
223 |
|
224 |
printf("%s%s/%s%s%s", BOLD, atom->CATEGORY, BLUE, |
225 |
(exact ? dentry->d_name : atom->PN), NORM); |
226 |
if (quiet) |
227 |
puts(""); |
228 |
else if (root != NULL) |
229 |
printf(" (%s%s)\n", root, e->name); |
230 |
else |
231 |
printf(" (%s)\n", e->name); |
232 |
|
233 |
} else { |
234 |
non_orphans[i] = 1; |
235 |
} |
236 |
found++; |
237 |
} |
238 |
} |
239 |
fclose(fp); |
240 |
if (atom != NULL) |
241 |
atom_implode(atom); |
242 |
} |
243 |
closedir(dir); |
244 |
|
245 |
return; |
246 |
} |
247 |
|
248 |
qfile_args_t *create_qfile_args(); |
249 |
qfile_args_t *create_qfile_args() |
250 |
{ |
251 |
qfile_args_t *qfile_args; |
252 |
|
253 |
qfile_args = xmalloc(sizeof(qfile_args_t)); |
254 |
|
255 |
memset(qfile_args, 0, sizeof(qfile_args_t)); |
256 |
return qfile_args; |
257 |
} |
258 |
|
259 |
void destroy_qfile_args(qfile_args_t *); |
260 |
void destroy_qfile_args(qfile_args_t *qfile_args) |
261 |
{ |
262 |
int i; |
263 |
|
264 |
for (i = 0; i < qfile_args->length; ++i) { |
265 |
if (qfile_args->basenames != NULL && qfile_args->basenames[i] != NULL) |
266 |
free(qfile_args->basenames[i]); |
267 |
if (qfile_args->dirnames != NULL && qfile_args->dirnames[i] != NULL) |
268 |
free(qfile_args->dirnames[i]); |
269 |
if (qfile_args->realdirnames != NULL && qfile_args->realdirnames[i] != NULL) |
270 |
free(qfile_args->realdirnames[i]); |
271 |
} |
272 |
|
273 |
if (qfile_args->basenames != NULL) |
274 |
free(qfile_args->basenames); |
275 |
if (qfile_args->dirnames != NULL) |
276 |
free(qfile_args->dirnames); |
277 |
if (qfile_args->realdirnames != NULL) |
278 |
free(qfile_args->realdirnames); |
279 |
|
280 |
if (qfile_args->bn_firstchars != NULL) |
281 |
free(qfile_args->bn_firstchars); |
282 |
|
283 |
if (qfile_args->non_orphans != NULL) |
284 |
free(qfile_args->non_orphans); |
285 |
|
286 |
if (qfile_args->real_root != NULL) |
287 |
free(qfile_args->real_root); |
288 |
|
289 |
if (qfile_args->exclude_pkg != NULL) |
290 |
free(qfile_args->exclude_pkg); |
291 |
/* don't free qfile_args->exclude_slot, it's the same chunk */ |
292 |
|
293 |
memset(qfile_args, 0, sizeof(qfile_args_t)); |
294 |
} |
295 |
|
296 |
int prepare_qfile_args(const int, const char **, |
297 |
const short, const short, const char *, qfile_args_t *); |
298 |
int prepare_qfile_args(const int argc, const char **argv, |
299 |
const short assume_root_prefix, const short search_orphans, |
300 |
const char *exclude_pkg_arg, qfile_args_t *qfile_args) |
301 |
{ |
302 |
int i; |
303 |
int nb_of_queries = argc; |
304 |
char *pwd = NULL; |
305 |
int real_root_length; |
306 |
char *real_root = NULL; |
307 |
char **basenames = NULL; |
308 |
char **dirnames = NULL; |
309 |
char **realdirnames = NULL; |
310 |
char *basenames_firstchars = NULL; |
311 |
char tmppath[_Q_PATH_MAX+1]; |
312 |
char abspath[_Q_PATH_MAX+1]; |
313 |
|
314 |
/* Try to get $PWD. Must be absolute, with no trailing slash. */ |
315 |
if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/') { |
316 |
pwd = xstrdup(pwd); |
317 |
if ((pwd[strlen(pwd) - 1] == '/')) |
318 |
pwd[strlen(pwd) - 1] = '\0'; |
319 |
} else |
320 |
pwd = NULL; |
321 |
|
322 |
/* Get realpath of $ROOT, with no trailing slash */ |
323 |
if (portroot[0] == '/') |
324 |
strncpy(tmppath, portroot, _Q_PATH_MAX); |
325 |
else if (pwd != NULL) |
326 |
snprintf(tmppath, _Q_PATH_MAX, "%s/%s", pwd, portroot); |
327 |
else { |
328 |
free(pwd); |
329 |
warn("Could not get absolute path for ROOT (\"%s\"), because of missing or not absolute $PWD", tmppath); |
330 |
return -1; |
331 |
} |
332 |
errno = 0; |
333 |
realpath(tmppath, abspath); |
334 |
if (errno != 0) { |
335 |
free(pwd); |
336 |
warnp("Could not read real path of ROOT (\"%s\")", tmppath); |
337 |
return -1; |
338 |
} |
339 |
if (strlen(abspath) == 1) |
340 |
abspath[0] = '\0'; |
341 |
real_root = xstrdup(abspath); |
342 |
real_root_length = strlen(real_root); |
343 |
|
344 |
/* For each argument, we store its basename, its absolute dirname, |
345 |
* and the realpath of its dirname. Dirnames and their realpaths |
346 |
* are stored without their $ROOT prefix, but $ROOT is used when |
347 |
* checking realpaths. |
348 |
*/ |
349 |
basenames = xcalloc(argc, sizeof(char*)); |
350 |
dirnames = xcalloc(argc, sizeof(char*)); |
351 |
realdirnames = xcalloc(argc, sizeof(char*)); |
352 |
/* For optimization of qfile(), we also give it an array of the first char |
353 |
* of each basename. This way we avoid numerous strcmp() calls. |
354 |
*/ |
355 |
basenames_firstchars = xcalloc(argc, sizeof(char)); |
356 |
|
357 |
for (i = 0; i < argc; ++i) { |
358 |
/* Record basename, but if it is ".", ".." or "/" */ |
359 |
strncpy(abspath, argv[i], _Q_PATH_MAX); /* strncopy so that "argv" can be "const" */ |
360 |
strncpy(tmppath, basename(abspath), _Q_PATH_MAX); |
361 |
if ((strlen(tmppath) > 2) || |
362 |
(strncmp(tmppath, "..", strlen(tmppath)) |
363 |
&& strncmp(tmppath, "/", strlen(tmppath)))) |
364 |
{ |
365 |
basenames[i] = xstrdup(tmppath); |
366 |
basenames_firstchars[i] = basenames[i][0]; |
367 |
/* If there is no "/" in the argument, then it's over. |
368 |
* (we are searching a simple file name) |
369 |
*/ |
370 |
if (strchr(argv[i], '/') == NULL) |
371 |
continue; |
372 |
} |
373 |
|
374 |
/* Make sure we have an absolute path available (with "realpath(ROOT)" prefix) */ |
375 |
if (argv[i][0] == '/') { |
376 |
if (assume_root_prefix) |
377 |
strncpy(abspath, argv[i], _Q_PATH_MAX); |
378 |
else |
379 |
snprintf(abspath, _Q_PATH_MAX, "%s%s", real_root, argv[i]); |
380 |
} else if (pwd != NULL) { |
381 |
if (assume_root_prefix) |
382 |
snprintf(abspath, _Q_PATH_MAX, "%s/%s", pwd, argv[i]); |
383 |
else |
384 |
snprintf(abspath, _Q_PATH_MAX, "%s%s/%s", real_root, pwd, argv[i]); |
385 |
} else { |
386 |
warn("$PWD was not found in environment, or is not an absolute path"); |
387 |
goto skip_query_item; |
388 |
} |
389 |
|
390 |
if (basenames[i] != NULL) { |
391 |
/* Get both the dirname and its realpath. This paths will |
392 |
* have no trailing slash, but if it is the only char (ie., |
393 |
* when searching for "/foobar"). |
394 |
*/ |
395 |
strncpy(tmppath, abspath, _Q_PATH_MAX); |
396 |
strncpy(abspath, dirname(tmppath), _Q_PATH_MAX); |
397 |
if (abspath[real_root_length] == '\0') |
398 |
strncat(abspath, "/", 1); |
399 |
dirnames[i] = xstrdup(abspath + real_root_length); |
400 |
errno = 0; |
401 |
realpath(abspath, tmppath); |
402 |
if (errno != 0) { |
403 |
if (verbose) { |
404 |
warnp("Could not read real path of \"%s\"", abspath); |
405 |
warn("Results for query item \"%s\" may be inaccurate.", argv[i]); |
406 |
} |
407 |
continue; |
408 |
} |
409 |
if (!qfile_is_prefix(tmppath, real_root, real_root_length)) { |
410 |
warn("Real path of \"%s\" is not under ROOT: %s", abspath, tmppath); |
411 |
goto skip_query_item; |
412 |
} |
413 |
if (tmppath[real_root_length] == '\0') |
414 |
strncat(tmppath, "/", 1); |
415 |
if (strcmp(dirnames[i], tmppath + real_root_length)) |
416 |
realdirnames[i] = xstrdup(tmppath + real_root_length); |
417 |
} else { |
418 |
/* No basename means we are looking for something like "/foo/bar/.." |
419 |
* Dirname is meaningless here, we can only get realpath of the full |
420 |
* path and then split it. |
421 |
*/ |
422 |
errno = 0; |
423 |
realpath(abspath, tmppath); |
424 |
if (errno != 0) { |
425 |
warnp("Could not read real path of \"%s\"", abspath); |
426 |
goto skip_query_item; |
427 |
} |
428 |
if (!qfile_is_prefix(tmppath, real_root, real_root_length)) { |
429 |
warn("Real path of \"%s\" is not under ROOT: %s", abspath, tmppath); |
430 |
goto skip_query_item; |
431 |
} |
432 |
strncpy(abspath, tmppath, _Q_PATH_MAX); |
433 |
basenames[i] = xstrdup(basename(abspath)); |
434 |
basenames_firstchars[i] = basenames[i][0]; |
435 |
strncpy(abspath, dirname(tmppath), _Q_PATH_MAX); |
436 |
if (tmppath[real_root_length] == '\0') |
437 |
strncat(tmppath, "/", 1); |
438 |
realdirnames[i] = xstrdup(abspath + real_root_length); |
439 |
} |
440 |
continue; |
441 |
|
442 |
skip_query_item: |
443 |
--nb_of_queries; |
444 |
warn("Skipping query item \"%s\".", argv[i]); |
445 |
if (basenames[i] != NULL) free(basenames[i]); |
446 |
if (dirnames[i] != NULL) free(dirnames[i]); |
447 |
if (realdirnames[i] != NULL) free(realdirnames[i]); |
448 |
basenames[i] = dirnames[i] = realdirnames[i] = NULL; |
449 |
} |
450 |
|
451 |
if (pwd != NULL) |
452 |
free(pwd); |
453 |
|
454 |
qfile_args->real_root = real_root; |
455 |
qfile_args->basenames = basenames; |
456 |
qfile_args->dirnames = dirnames; |
457 |
qfile_args->realdirnames = realdirnames; |
458 |
qfile_args->bn_firstchars = basenames_firstchars; |
459 |
qfile_args->length = argc; |
460 |
|
461 |
if (search_orphans) { |
462 |
qfile_args->non_orphans = xcalloc(argc, sizeof(short)); |
463 |
memset(qfile_args->non_orphans, 0, argc); |
464 |
} |
465 |
|
466 |
if (exclude_pkg_arg != NULL) { |
467 |
qfile_args->exclude_pkg = xstrdup(exclude_pkg_arg); |
468 |
if ((qfile_args->exclude_slot = strchr(qfile_args->exclude_pkg, ':')) != NULL) |
469 |
*qfile_args->exclude_slot++ = '\0'; |
470 |
/* Maybe this should be atom-exploded instead (to check syntax, etc.) */ |
471 |
} |
472 |
|
473 |
return nb_of_queries; |
474 |
} |
475 |
|
476 |
int qfile_main(int argc, char **argv) |
477 |
{ |
478 |
DIR *dir; |
479 |
struct dirent *dentry; |
480 |
int i, nb_of_queries; |
481 |
char *p; |
482 |
short search_orphans = 0; |
483 |
short assume_root_prefix = 0; |
484 |
char *root_prefix = NULL; |
485 |
char *exclude_pkg_arg = NULL; |
486 |
qfile_args_t *qfile_args = NULL; |
487 |
int qargc = 0; |
488 |
char **qargv = NULL; |
489 |
short done = 0; |
490 |
FILE *args_file = NULL; |
491 |
int max_args = QFILE_DEFAULT_MAX_ARGS; |
492 |
char path[_Q_PATH_MAX]; |
493 |
|
494 |
DBG("argc=%d argv[0]=%s argv[1]=%s", |
495 |
argc, argv[0], argc > 1 ? argv[1] : "NULL?"); |
496 |
|
497 |
while ((i = GETOPT_LONG(QFILE, qfile, "")) != -1) { |
498 |
switch (i) { |
499 |
COMMON_GETOPTS_CASES(qfile) |
500 |
case 'e': exact = 1; break; |
501 |
case 'f': |
502 |
if (args_file != NULL) { |
503 |
warn("Don't use -f twice!"); |
504 |
goto exit; |
505 |
} |
506 |
if (strcmp(optarg, "-") == 0) |
507 |
args_file = stdin; |
508 |
else if ((args_file = fopen(optarg, "r")) == NULL) { |
509 |
warnp("%s", optarg); |
510 |
goto exit; |
511 |
} |
512 |
break; |
513 |
case 'm': |
514 |
errno = 0; |
515 |
max_args = strtol(optarg, &p, 10); |
516 |
if (errno != 0) { |
517 |
warnp("%s: not a valid integer", optarg); |
518 |
goto exit; |
519 |
} else if (p == optarg || *p != '\0') { |
520 |
warn("%s: not a valid integer", optarg); |
521 |
goto exit; |
522 |
} |
523 |
if (max_args <= 0 || max_args > QFILE_MAX_MAX_ARGS) { |
524 |
warn("%s: silly value!", optarg); |
525 |
goto exit; |
526 |
} |
527 |
break; |
528 |
case 'o': search_orphans = 1; break; |
529 |
case 'R': assume_root_prefix = 1; break; |
530 |
case 'x': |
531 |
if (exclude_pkg_arg != NULL) { |
532 |
warn("--exclude can only be used once."); |
533 |
goto exit; |
534 |
} |
535 |
exclude_pkg_arg = optarg; |
536 |
break; |
537 |
} |
538 |
} |
539 |
if (!exact && verbose) exact++; |
540 |
if ((argc == optind) && (args_file == NULL)) |
541 |
qfile_usage(EXIT_FAILURE); |
542 |
|
543 |
if ((args_file == NULL) && (max_args != QFILE_DEFAULT_MAX_ARGS)) |
544 |
warn("--max-args is only used when reading arguments from a file (with -f)"); |
545 |
|
546 |
if (chdir(portroot)) { |
547 |
warnp("could not chdir(%s) for ROOT", portroot); |
548 |
goto exit; |
549 |
} |
550 |
|
551 |
if (chdir(portvdb) != 0) { |
552 |
warnp("could not chdir(ROOT/%s) for installed packages database", portvdb); |
553 |
goto exit; |
554 |
} |
555 |
|
556 |
/* Get a copy of $ROOT, with no trailing slash |
557 |
* (this one is just for qfile(...) output) |
558 |
*/ |
559 |
root_prefix = xstrdup(portroot); |
560 |
if (root_prefix[strlen(root_prefix) - 1] == '/') |
561 |
root_prefix[strlen(root_prefix) - 1] = '\0'; |
562 |
|
563 |
/* Are we using --from ? */ |
564 |
if (args_file == NULL) { |
565 |
qargc = argc - optind; |
566 |
qargv = argv + optind; |
567 |
done = 1; |
568 |
} else { |
569 |
qargv = xcalloc(max_args, sizeof(char*)); |
570 |
} |
571 |
|
572 |
do { /* This block may be repeated if using --from with a big files list */ |
573 |
if (args_file != NULL) { |
574 |
qargc = 0; |
575 |
/* Read up to max_args files from the input file */ |
576 |
while ((fgets(path, _Q_PATH_MAX, args_file)) != NULL) { |
577 |
if ((p = strchr(path, '\n')) != NULL) |
578 |
*p = '\0'; |
579 |
if (path == p) continue; |
580 |
qargv[qargc] = xstrdup(path); |
581 |
qargc++; |
582 |
if (qargc >= max_args) break; |
583 |
} |
584 |
} |
585 |
|
586 |
if (qargc == 0) break; |
587 |
|
588 |
if (qfile_args == NULL) { /* qfile_args is allocated only once */ |
589 |
if ((qfile_args = create_qfile_args()) == NULL) { |
590 |
warn("Out of memory"); |
591 |
goto exit; |
592 |
} |
593 |
} else { |
594 |
destroy_qfile_args(qfile_args); |
595 |
} |
596 |
|
597 |
/* Prepare the qfile(...) arguments structure */ |
598 |
nb_of_queries = prepare_qfile_args(qargc, (const char **) qargv, |
599 |
assume_root_prefix, search_orphans, exclude_pkg_arg, qfile_args); |
600 |
if (nb_of_queries < 0) |
601 |
goto exit; |
602 |
|
603 |
if (chdir(portroot) |
604 |
|| chdir(portvdb) != 0 |
605 |
|| (dir = opendir(".")) == NULL) { |
606 |
warnp("could not chdir(ROOT/%s) for installed packages database", portvdb); |
607 |
goto exit; |
608 |
} |
609 |
|
610 |
/* Iteration over VDB categories */ |
611 |
while (nb_of_queries && (dentry = q_vdb_get_next_dir(dir))) { |
612 |
snprintf(path, _Q_PATH_MAX, "%s/%s/%s", qfile_args->real_root, portvdb, dentry->d_name); |
613 |
qfile(path, (assume_root_prefix ? root_prefix : NULL), qfile_args); |
614 |
} |
615 |
|
616 |
if (qfile_args->non_orphans != NULL) { |
617 |
/* display orphan files */ |
618 |
for (i = 0; i < qfile_args->length; i++) { |
619 |
if (qfile_args->non_orphans[i]) |
620 |
continue; |
621 |
if (qfile_args->basenames[i] != NULL) { |
622 |
found = 0; /* ~inverse return code (as soon as an orphan is found, return non-zero) */ |
623 |
if (!quiet) |
624 |
printf("%s\n", qargv[i]); |
625 |
else break; |
626 |
} |
627 |
} |
628 |
} |
629 |
|
630 |
if (args_file != NULL && qargv != NULL) { |
631 |
for (i = 0; i < qargc; i++) { |
632 |
if (qargv[i] != NULL) free(qargv[i]); |
633 |
qargv[i] = NULL; |
634 |
} |
635 |
} |
636 |
} while (args_file != NULL && qargc == max_args); |
637 |
|
638 |
exit: |
639 |
|
640 |
if (args_file != NULL && qargv != NULL) { |
641 |
for (i = 0; i < qargc; i++) |
642 |
if (qargv[i] != NULL) free(qargv[i]); |
643 |
free(qargv); |
644 |
} |
645 |
|
646 |
if (args_file != NULL && args_file != stdin) |
647 |
fclose(args_file); |
648 |
|
649 |
if (qfile_args != NULL) { |
650 |
destroy_qfile_args(qfile_args); |
651 |
free(qfile_args); |
652 |
} |
653 |
|
654 |
if (root_prefix != NULL) |
655 |
free(root_prefix); |
656 |
|
657 |
return (found ? EXIT_SUCCESS : EXIT_FAILURE); |
658 |
} |
659 |
|
660 |
#else |
661 |
DEFINE_APPLET_STUB(qfile) |
662 |
#endif |