Merge branch 'maint'
[pacman-ng.git] / src / pacman / query.c
blobd27044eb9d07099956c850db95e585852b9f0c58
1 /*
2 * query.c
4 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #include <unistd.h>
31 #include <alpm.h>
32 #include <alpm_list.h>
34 /* pacman */
35 #include "pacman.h"
36 #include "package.h"
37 #include "conf.h"
38 #include "util.h"
40 static char *resolve_path(const char *file)
42 char *str = NULL;
44 str = calloc(PATH_MAX + 1, sizeof(char));
45 if(!str) {
46 return NULL;
49 if(!realpath(file, str)) {
50 free(str);
51 return NULL;
54 return str;
57 /* check if filename exists in PATH */
58 static int search_path(char **filename, struct stat *bufptr)
60 char *envpath, *envpathsplit, *path, *fullname;
61 size_t flen;
63 if ((envpath = getenv("PATH")) == NULL) {
64 return -1;
66 if ((envpath = envpathsplit = strdup(envpath)) == NULL) {
67 return -1;
70 flen = strlen(*filename);
72 while ((path = strsep(&envpathsplit, ":")) != NULL) {
73 size_t plen = strlen(path);
75 /* strip the trailing slash if one exists */
76 while(path[plen - 1] == '/') {
77 path[--plen] = '\0';
80 fullname = malloc(plen + flen + 2);
81 sprintf(fullname, "%s/%s", path, *filename);
83 if(lstat(fullname, bufptr) == 0) {
84 free(*filename);
85 *filename = fullname;
86 free(envpath);
87 return 0;
89 free(fullname);
91 free(envpath);
92 return -1;
95 static void print_query_fileowner(const char *filename, pmpkg_t *info)
97 if (!config->quiet) {
98 printf(_("%s is owned by %s %s\n"), filename,
99 alpm_pkg_get_name(info), alpm_pkg_get_version(info));
100 } else {
101 printf("%s\n", alpm_pkg_get_name(info));
105 static int query_fileowner(alpm_list_t *targets)
107 int ret = 0;
108 char path[PATH_MAX];
109 const char *root;
110 char *append;
111 size_t max_length;
112 alpm_list_t *t;
113 pmdb_t *db_local;
115 /* This code is here for safety only */
116 if(targets == NULL) {
117 pm_fprintf(stderr, PM_LOG_ERROR, _("no file was specified for --owns\n"));
118 return 1;
121 /* Set up our root path buffer. We only need to copy the location of root in
122 * once, then we can just overwrite whatever file was there on the previous
123 * iteration. */
124 root = alpm_option_get_root();
125 strncpy(path, root, PATH_MAX - 1);
126 append = path + strlen(path);
127 max_length = PATH_MAX - (append - path) - 1;
129 db_local = alpm_option_get_localdb();
131 for(t = targets; t; t = alpm_list_next(t)) {
132 char *filename, *dname, *rpath;
133 const char *bname;
134 struct stat buf;
135 alpm_list_t *i;
136 int found = 0;
138 filename = strdup(alpm_list_getdata(t));
140 if(lstat(filename, &buf) == -1) {
141 /* if it is not a path but a program name, then check in PATH */
142 if(strchr(filename, '/') == NULL) {
143 if(search_path(&filename, &buf) == -1) {
144 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to find '%s' in PATH: %s\n"),
145 filename, strerror(errno));
146 ret++;
147 free(filename);
148 continue;
150 } else {
151 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to read file '%s': %s\n"),
152 filename, strerror(errno));
153 ret++;
154 free(filename);
155 continue;
159 if(S_ISDIR(buf.st_mode)) {
160 pm_fprintf(stderr, PM_LOG_ERROR,
161 _("cannot determine ownership of directory '%s'\n"), filename);
162 ret++;
163 free(filename);
164 continue;
167 bname = mbasename(filename);
168 dname = mdirname(filename);
169 /* for files in '/', there is no directory name to match */
170 if (strcmp(dname, "") == 0) {
171 rpath = NULL;
172 } else {
173 rpath = resolve_path(dname);
175 if(!rpath) {
176 pm_fprintf(stderr, PM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"),
177 filename, strerror(errno));
178 free(filename);
179 free(dname);
180 free(rpath);
181 ret++;
182 continue;
185 free(dname);
187 for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) {
188 alpm_list_t *j;
189 pmpkg_t *info = alpm_list_getdata(i);
191 for(j = alpm_pkg_get_files(info); j && !found; j = alpm_list_next(j)) {
192 char *ppath, *pdname;
193 const char *pkgfile = alpm_list_getdata(j);
195 /* avoid the costly resolve_path usage if the basenames don't match */
196 if(strcmp(mbasename(pkgfile), bname) != 0) {
197 continue;
200 /* for files in '/', there is no directory name to match */
201 if(!rpath) {
202 print_query_fileowner(filename, info);
203 found = 1;
204 continue;
207 if(strlen(pkgfile) > max_length) {
208 pm_fprintf(stderr, PM_LOG_ERROR, _("path too long: %s%s\n"), root, pkgfile);
210 /* concatenate our file and the root path */
211 strcpy(append, pkgfile);
213 pdname = mdirname(path);
214 ppath = resolve_path(pdname);
215 free(pdname);
217 if(ppath && strcmp(ppath, rpath) == 0) {
218 print_query_fileowner(filename, info);
219 found = 1;
221 free(ppath);
224 if(!found) {
225 pm_fprintf(stderr, PM_LOG_ERROR, _("No package owns %s\n"), filename);
226 ret++;
228 free(filename);
229 free(rpath);
232 return ret;
235 /* search the local database for a matching package */
236 static int query_search(alpm_list_t *targets)
238 alpm_list_t *i, *searchlist;
239 int freelist;
240 pmdb_t *db_local = alpm_option_get_localdb();
242 /* if we have a targets list, search for packages matching it */
243 if(targets) {
244 searchlist = alpm_db_search(db_local, targets);
245 freelist = 1;
246 } else {
247 searchlist = alpm_db_get_pkgcache(db_local);
248 freelist = 0;
250 if(searchlist == NULL) {
251 return 1;
254 for(i = searchlist; i; i = alpm_list_next(i)) {
255 alpm_list_t *grp;
256 pmpkg_t *pkg = alpm_list_getdata(i);
258 if (!config->quiet) {
259 printf("local/%s %s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
260 } else {
261 printf("%s", alpm_pkg_get_name(pkg));
264 /* print the package size with the output if ShowSize option set */
265 if(!config->quiet && config->showsize) {
266 /* Convert byte size to MB */
267 double mbsize = (double)alpm_pkg_get_size(pkg) / (1024.0 * 1024.0);
269 printf(" [%.2f MB]", mbsize);
273 if (!config->quiet) {
274 if((grp = alpm_pkg_get_groups(pkg)) != NULL) {
275 alpm_list_t *k;
276 printf(" (");
277 for(k = grp; k; k = alpm_list_next(k)) {
278 const char *group = alpm_list_getdata(k);
279 printf("%s", group);
280 if(alpm_list_next(k)) {
281 /* only print a spacer if there are more groups */
282 printf(" ");
285 printf(")");
288 /* we need a newline and initial indent first */
289 printf("\n ");
290 indentprint(alpm_pkg_get_desc(pkg), 4);
292 printf("\n");
295 /* we only want to free if the list was a search list */
296 if(freelist) {
297 alpm_list_free(searchlist);
299 return 0;
302 static int query_group(alpm_list_t *targets)
304 alpm_list_t *i, *j;
305 char *grpname = NULL;
306 int ret = 0;
307 pmdb_t *db_local = alpm_option_get_localdb();
309 if(targets == NULL) {
310 for(j = alpm_db_get_grpcache(db_local); j; j = alpm_list_next(j)) {
311 pmgrp_t *grp = alpm_list_getdata(j);
312 const alpm_list_t *p, *packages;
313 const char *grpname;
315 grpname = alpm_grp_get_name(grp);
316 packages = alpm_grp_get_pkgs(grp);
318 for(p = packages; p; p = alpm_list_next(p)) {
319 printf("%s %s\n", grpname, alpm_pkg_get_name(alpm_list_getdata(p)));
322 } else {
323 for(i = targets; i; i = alpm_list_next(i)) {
324 pmgrp_t *grp;
325 grpname = alpm_list_getdata(i);
326 grp = alpm_db_readgrp(db_local, grpname);
327 if(grp) {
328 const alpm_list_t *p, *packages = alpm_grp_get_pkgs(grp);
329 for(p = packages; p; p = alpm_list_next(p)) {
330 if(!config->quiet) {
331 printf("%s %s\n", grpname,
332 alpm_pkg_get_name(alpm_list_getdata(p)));
333 } else {
334 printf("%s\n", alpm_pkg_get_name(alpm_list_getdata(p)));
337 } else {
338 pm_fprintf(stderr, PM_LOG_ERROR, _("group \"%s\" was not found\n"), grpname);
339 ret++;
343 return ret;
346 static int is_foreign(pmpkg_t *pkg)
348 const char *pkgname = alpm_pkg_get_name(pkg);
349 alpm_list_t *j;
350 alpm_list_t *sync_dbs = alpm_option_get_syncdbs();
352 int match = 0;
353 for(j = sync_dbs; j; j = alpm_list_next(j)) {
354 pmdb_t *db = alpm_list_getdata(j);
355 pmpkg_t *findpkg = alpm_db_get_pkg(db, pkgname);
356 if(findpkg) {
357 match = 1;
358 break;
361 if(match == 0) {
362 return 1;
364 return 0;
367 static int is_unrequired(pmpkg_t *pkg)
369 alpm_list_t *requiredby = alpm_pkg_compute_requiredby(pkg);
370 if(requiredby == NULL) {
371 return 1;
373 FREELIST(requiredby);
374 return 0;
377 static int filter(pmpkg_t *pkg)
379 /* check if this package was explicitly installed */
380 if(config->op_q_explicit &&
381 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_EXPLICIT) {
382 return 0;
384 /* check if this package was installed as a dependency */
385 if(config->op_q_deps &&
386 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_DEPEND) {
387 return 0;
389 /* check if this pkg isn't in a sync DB */
390 if(config->op_q_foreign && !is_foreign(pkg)) {
391 return 0;
393 /* check if this pkg is unrequired */
394 if(config->op_q_unrequired && !is_unrequired(pkg)) {
395 return 0;
397 /* check if this pkg is outdated */
398 if(config->op_q_upgrade && (alpm_sync_newversion(pkg, alpm_option_get_syncdbs()) == NULL)) {
399 return 0;
401 return 1;
404 /* Loop through the packages. For each package,
405 * loop through files to check if they exist. */
406 static int check(pmpkg_t *pkg)
408 alpm_list_t *i;
409 const char *root;
410 int allfiles = 0, errors = 0;
411 size_t rootlen;
412 char f[PATH_MAX];
414 root = alpm_option_get_root();
415 rootlen = strlen(root);
416 if(rootlen + 1 > PATH_MAX) {
417 /* we are in trouble here */
418 pm_fprintf(stderr, PM_LOG_ERROR, _("path too long: %s%s\n"), root, "");
419 return 1;
421 strcpy(f, root);
423 const char *pkgname = alpm_pkg_get_name(pkg);
424 for(i = alpm_pkg_get_files(pkg); i; i = alpm_list_next(i)) {
425 struct stat st;
426 const char *path = alpm_list_getdata(i);
428 if(rootlen + 1 + strlen(path) > PATH_MAX) {
429 pm_fprintf(stderr, PM_LOG_WARNING, _("path too long: %s%s\n"), root, path);
430 continue;
432 strcpy(f + rootlen, path);
433 allfiles++;
434 /* use lstat to prevent errors from symlinks */
435 if(lstat(f, &st) != 0) {
436 if(config->quiet) {
437 printf("%s %s\n", pkgname, f);
438 } else {
439 pm_printf(PM_LOG_WARNING, "%s: %s (%s)\n",
440 pkgname, f, strerror(errno));
442 errors++;
446 if(!config->quiet) {
447 printf(_n("%s: %d total file, ", "%s: %d total files, ",
448 (unsigned long)allfiles), pkgname, allfiles);
449 printf(_n("%d missing file\n", "%d missing files\n",
450 (unsigned long)errors), errors);
453 return (errors != 0 ? 1 : 0);
456 static int display(pmpkg_t *pkg)
458 int ret = 0;
460 if(config->op_q_info) {
461 if(config->op_q_isfile) {
462 /* omit info that isn't applicable for a file package */
463 dump_pkg_full(pkg, 0);
464 } else {
465 dump_pkg_full(pkg, config->op_q_info);
468 if(config->op_q_list) {
469 dump_pkg_files(pkg, config->quiet);
471 if(config->op_q_changelog) {
472 dump_pkg_changelog(pkg);
474 if(config->op_q_check) {
475 ret = check(pkg);
477 if(!config->op_q_info && !config->op_q_list
478 && !config->op_q_changelog && !config->op_q_check) {
479 if (!config->quiet) {
480 printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
481 } else {
482 printf("%s\n", alpm_pkg_get_name(pkg));
485 return ret;
488 int pacman_query(alpm_list_t *targets)
490 int ret = 0;
491 int match = 0;
492 alpm_list_t *i;
493 pmpkg_t *pkg = NULL;
494 pmdb_t *db_local;
496 /* First: operations that do not require targets */
498 /* search for a package */
499 if(config->op_q_search) {
500 ret = query_search(targets);
501 return ret;
504 /* looking for groups */
505 if(config->group) {
506 ret = query_group(targets);
507 return ret;
510 if(config->op_q_foreign) {
511 /* ensure we have at least one valid sync db set up */
512 alpm_list_t *sync_dbs = alpm_option_get_syncdbs();
513 if(sync_dbs == NULL || alpm_list_count(sync_dbs) == 0) {
514 pm_printf(PM_LOG_ERROR, _("no usable package repositories configured.\n"));
515 return 1;
519 db_local = alpm_option_get_localdb();
521 /* operations on all packages in the local DB
522 * valid: no-op (plain -Q), list, info, check
523 * invalid: isfile, owns */
524 if(targets == NULL) {
525 if(config->op_q_isfile || config->op_q_owns) {
526 pm_printf(PM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
527 return 1;
530 for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) {
531 pkg = alpm_list_getdata(i);
532 if(filter(pkg)) {
533 int value = display(pkg);
534 if(value != 0) {
535 ret = 1;
537 match = 1;
540 if(!match) {
541 ret = 1;
543 return ret;
546 /* Second: operations that require target(s) */
548 /* determine the owner of a file */
549 if(config->op_q_owns) {
550 ret = query_fileowner(targets);
551 return ret;
554 /* operations on named packages in the local DB
555 * valid: no-op (plain -Q), list, info, check */
556 for(i = targets; i; i = alpm_list_next(i)) {
557 char *strname = alpm_list_getdata(i);
559 if(config->op_q_isfile) {
560 alpm_pkg_load(strname, 1, &pkg);
561 } else {
562 pkg = alpm_db_get_pkg(db_local, strname);
565 if(pkg == NULL) {
566 pm_fprintf(stderr, PM_LOG_ERROR, _("package \"%s\" not found\n"), strname);
567 ret = 1;
568 continue;
571 if(filter(pkg)) {
572 int value = display(pkg);
573 if(value != 0) {
574 ret = 1;
576 match = 1;
579 if(config->op_q_isfile) {
580 alpm_pkg_free(pkg);
581 pkg = NULL;
585 if(!match) {
586 ret = 1;
589 return ret;
592 /* vim: set ts=2 sw=2 noet: */