|
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 |