Improve pkghash_remove algorithm
[pacman-ng.git] / src / pacman / query.c
blob734875beee9cfff36b4f7c210cbbc6f279973e25
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 int query_fileowner(alpm_list_t *targets)
97 int ret = 0;
98 char path[PATH_MAX];
99 const char *root;
100 char *append;
101 size_t max_length;
102 alpm_list_t *t;
103 pmdb_t *db_local;
105 /* This code is here for safety only */
106 if(targets == NULL) {
107 pm_fprintf(stderr, PM_LOG_ERROR, _("no file was specified for --owns\n"));
108 return(1);
111 /* Set up our root path buffer. We only need to copy the location of root in
112 * once, then we can just overwrite whatever file was there on the previous
113 * iteration. */
114 root = alpm_option_get_root();
115 strncpy(path, root, PATH_MAX - 1);
116 append = path + strlen(path);
117 max_length = PATH_MAX - (append - path) - 1;
119 db_local = alpm_option_get_localdb();
121 for(t = targets; t; t = alpm_list_next(t)) {
122 char *filename, *dname, *rpath;
123 const char *bname;
124 struct stat buf;
125 alpm_list_t *i;
126 int found = 0;
128 filename = strdup(alpm_list_getdata(t));
130 if(lstat(filename, &buf) == -1) {
131 /* if it is not a path but a program name, then check in PATH */
132 if(strchr(filename, '/') == NULL) {
133 if(search_path(&filename, &buf) == -1) {
134 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to find '%s' in PATH: %s\n"),
135 filename, strerror(errno));
136 ret++;
137 free(filename);
138 continue;
140 } else {
141 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to read file '%s': %s\n"),
142 filename, strerror(errno));
143 ret++;
144 free(filename);
145 continue;
149 if(S_ISDIR(buf.st_mode)) {
150 pm_fprintf(stderr, PM_LOG_ERROR,
151 _("cannot determine ownership of a directory\n"));
152 ret++;
153 free(filename);
154 continue;
157 bname = mbasename(filename);
158 dname = mdirname(filename);
159 rpath = resolve_path(dname);
161 /* this odd conditional is to ensure files in '/' can be checked */
162 if(!rpath && strcmp(dname, "") != 0) {
163 pm_fprintf(stderr, PM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"),
164 filename, strerror(errno));
165 free(filename);
166 free(dname);
167 free(rpath);
168 ret++;
169 continue;
171 free(dname);
173 for(i = alpm_db_get_pkgcache_list(db_local); i && !found; i = alpm_list_next(i)) {
174 alpm_list_t *j;
175 pmpkg_t *info = alpm_list_getdata(i);
177 for(j = alpm_pkg_get_files(info); j && !found; j = alpm_list_next(j)) {
178 char *ppath, *pdname;
179 const char *pkgfile = alpm_list_getdata(j);
181 /* avoid the costly resolve_path usage if the basenames don't match */
182 if(strcmp(mbasename(pkgfile), bname) != 0) {
183 continue;
186 if(strlen(pkgfile) > max_length) {
187 pm_fprintf(stderr, PM_LOG_ERROR, _("Path too long: %s%s\n"), root, pkgfile);
189 /* concatenate our file and the root path */
190 strcpy(append, pkgfile);
192 pdname = mdirname(path);
193 ppath = resolve_path(pdname);
194 free(pdname);
196 if(ppath && strcmp(ppath, rpath) == 0) {
197 if (!config->quiet) {
198 printf(_("%s is owned by %s %s\n"), filename,
199 alpm_pkg_get_name(info), alpm_pkg_get_version(info));
200 } else {
201 printf("%s\n", alpm_pkg_get_name(info));
203 found = 1;
205 free(ppath);
208 if(!found) {
209 pm_fprintf(stderr, PM_LOG_ERROR, _("No package owns %s\n"), filename);
210 ret++;
212 free(filename);
213 free(rpath);
216 return ret;
219 /* search the local database for a matching package */
220 static int query_search(alpm_list_t *targets)
222 alpm_list_t *i, *searchlist;
223 int freelist;
224 pmdb_t *db_local = alpm_option_get_localdb();
226 /* if we have a targets list, search for packages matching it */
227 if(targets) {
228 searchlist = alpm_db_search(db_local, targets);
229 freelist = 1;
230 } else {
231 searchlist = alpm_db_get_pkgcache_list(db_local);
232 freelist = 0;
234 if(searchlist == NULL) {
235 return(1);
238 for(i = searchlist; i; i = alpm_list_next(i)) {
239 alpm_list_t *grp;
240 pmpkg_t *pkg = alpm_list_getdata(i);
242 if (!config->quiet) {
243 printf("local/%s %s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
244 } else {
245 printf("%s", alpm_pkg_get_name(pkg));
248 /* print the package size with the output if ShowSize option set */
249 if(!config->quiet && config->showsize) {
250 /* Convert byte size to MB */
251 double mbsize = (double)alpm_pkg_get_size(pkg) / (1024.0 * 1024.0);
253 printf(" [%.2f MB]", mbsize);
257 if (!config->quiet) {
258 if((grp = alpm_pkg_get_groups(pkg)) != NULL) {
259 alpm_list_t *k;
260 printf(" (");
261 for(k = grp; k; k = alpm_list_next(k)) {
262 const char *group = alpm_list_getdata(k);
263 printf("%s", group);
264 if(alpm_list_next(k)) {
265 /* only print a spacer if there are more groups */
266 printf(" ");
269 printf(")");
272 /* we need a newline and initial indent first */
273 printf("\n ");
274 indentprint(alpm_pkg_get_desc(pkg), 4);
276 printf("\n");
279 /* we only want to free if the list was a search list */
280 if(freelist) {
281 alpm_list_free(searchlist);
283 return(0);
286 static int query_group(alpm_list_t *targets)
288 alpm_list_t *i, *j;
289 char *grpname = NULL;
290 int ret = 0;
291 pmdb_t *db_local = alpm_option_get_localdb();
293 if(targets == NULL) {
294 for(j = alpm_db_get_grpcache(db_local); j; j = alpm_list_next(j)) {
295 pmgrp_t *grp = alpm_list_getdata(j);
296 const alpm_list_t *p, *packages;
297 const char *grpname;
299 grpname = alpm_grp_get_name(grp);
300 packages = alpm_grp_get_pkgs(grp);
302 for(p = packages; p; p = alpm_list_next(p)) {
303 printf("%s %s\n", grpname, alpm_pkg_get_name(alpm_list_getdata(p)));
306 } else {
307 for(i = targets; i; i = alpm_list_next(i)) {
308 pmgrp_t *grp;
309 grpname = alpm_list_getdata(i);
310 grp = alpm_db_readgrp(db_local, grpname);
311 if(grp) {
312 const alpm_list_t *p, *packages = alpm_grp_get_pkgs(grp);
313 for(p = packages; p; p = alpm_list_next(p)) {
314 if(!config->quiet) {
315 printf("%s %s\n", grpname,
316 alpm_pkg_get_name(alpm_list_getdata(p)));
317 } else {
318 printf("%s\n", alpm_pkg_get_name(alpm_list_getdata(p)));
321 } else {
322 pm_fprintf(stderr, PM_LOG_ERROR, _("group \"%s\" was not found\n"), grpname);
323 ret++;
327 return ret;
330 static int is_foreign(pmpkg_t *pkg)
332 const char *pkgname = alpm_pkg_get_name(pkg);
333 alpm_list_t *j;
334 alpm_list_t *sync_dbs = alpm_option_get_syncdbs();
336 int match = 0;
337 for(j = sync_dbs; j; j = alpm_list_next(j)) {
338 pmdb_t *db = alpm_list_getdata(j);
339 pmpkg_t *findpkg = alpm_db_get_pkg(db, pkgname);
340 if(findpkg) {
341 match = 1;
342 break;
345 if(match == 0) {
346 return(1);
348 return(0);
351 static int is_unrequired(pmpkg_t *pkg)
353 alpm_list_t *requiredby = alpm_pkg_compute_requiredby(pkg);
354 if(requiredby == NULL) {
355 return(1);
357 FREELIST(requiredby);
358 return(0);
361 static int filter(pmpkg_t *pkg)
363 /* check if this package was explicitly installed */
364 if(config->op_q_explicit &&
365 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_EXPLICIT) {
366 return(0);
368 /* check if this package was installed as a dependency */
369 if(config->op_q_deps &&
370 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_DEPEND) {
371 return(0);
373 /* check if this pkg isn't in a sync DB */
374 if(config->op_q_foreign && !is_foreign(pkg)) {
375 return(0);
377 /* check if this pkg is unrequired */
378 if(config->op_q_unrequired && !is_unrequired(pkg)) {
379 return(0);
381 /* check if this pkg is outdated */
382 if(config->op_q_upgrade && (alpm_sync_newversion(pkg, alpm_option_get_syncdbs()) == NULL)) {
383 return(0);
385 return(1);
388 /* Loop through the packages. For each package,
389 * loop through files to check if they exist. */
390 static int check(pmpkg_t *pkg)
392 alpm_list_t *i;
393 const char *root;
394 int allfiles = 0, errors = 0;
395 size_t rootlen;
396 char f[PATH_MAX];
398 root = alpm_option_get_root();
399 rootlen = strlen(root);
400 if(rootlen + 1 > PATH_MAX) {
401 /* we are in trouble here */
402 pm_fprintf(stderr, PM_LOG_ERROR, _("root path too long\n"));
403 return(1);
405 strcpy(f, root);
407 const char *pkgname = alpm_pkg_get_name(pkg);
408 for(i = alpm_pkg_get_files(pkg); i; i = alpm_list_next(i)) {
409 struct stat st;
410 const char *path = alpm_list_getdata(i);
412 if(rootlen + 1 + strlen(path) > PATH_MAX) {
413 pm_fprintf(stderr, PM_LOG_WARNING, _("file path too long\n"));
414 continue;
416 strcpy(f + rootlen, path);
417 allfiles++;
418 /* use lstat to prevent errors from symlinks */
419 if(lstat(f, &st) != 0) {
420 if(config->quiet) {
421 printf("%s %s\n", pkgname, f);
422 } else {
423 pm_printf(PM_LOG_WARNING, "%s: %s (%s)\n",
424 pkgname, f, strerror(errno));
426 errors++;
430 if(!config->quiet) {
431 printf(_n("%s: %d total file, ", "%s: %d total files, ",
432 (unsigned long)allfiles), pkgname, allfiles);
433 printf(_n("%d missing file\n", "%d missing files\n",
434 (unsigned long)errors), errors);
437 return(errors != 0 ? 1 : 0);
440 static int display(pmpkg_t *pkg)
442 int ret = 0;
444 if(config->op_q_info) {
445 if(config->op_q_isfile) {
446 /* omit info that isn't applicable for a file package */
447 dump_pkg_full(pkg, 0);
448 } else {
449 dump_pkg_full(pkg, config->op_q_info);
452 if(config->op_q_list) {
453 dump_pkg_files(pkg, config->quiet);
455 if(config->op_q_changelog) {
456 dump_pkg_changelog(pkg);
458 if(config->op_q_check) {
459 ret = check(pkg);
461 if(!config->op_q_info && !config->op_q_list
462 && !config->op_q_changelog && !config->op_q_check) {
463 if (!config->quiet) {
464 printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
465 } else {
466 printf("%s\n", alpm_pkg_get_name(pkg));
469 return(ret);
472 int pacman_query(alpm_list_t *targets)
474 int ret = 0;
475 int match = 0;
476 alpm_list_t *i;
477 pmpkg_t *pkg = NULL;
478 pmdb_t *db_local;
480 /* First: operations that do not require targets */
482 /* search for a package */
483 if(config->op_q_search) {
484 ret = query_search(targets);
485 return(ret);
488 /* looking for groups */
489 if(config->group) {
490 ret = query_group(targets);
491 return(ret);
494 if(config->op_q_foreign) {
495 /* ensure we have at least one valid sync db set up */
496 alpm_list_t *sync_dbs = alpm_option_get_syncdbs();
497 if(sync_dbs == NULL || alpm_list_count(sync_dbs) == 0) {
498 pm_printf(PM_LOG_ERROR, _("no usable package repositories configured.\n"));
499 return(1);
503 db_local = alpm_option_get_localdb();
505 /* operations on all packages in the local DB
506 * valid: no-op (plain -Q), list, info, check
507 * invalid: isfile, owns */
508 if(targets == NULL) {
509 if(config->op_q_isfile || config->op_q_owns) {
510 pm_printf(PM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
511 return(1);
514 for(i = alpm_db_get_pkgcache_list(db_local); i; i = alpm_list_next(i)) {
515 pkg = alpm_list_getdata(i);
516 if(filter(pkg)) {
517 int value = display(pkg);
518 if(value != 0) {
519 ret = 1;
521 match = 1;
524 if(!match) {
525 ret = 1;
527 return(ret);
530 /* Second: operations that require target(s) */
532 /* determine the owner of a file */
533 if(config->op_q_owns) {
534 ret = query_fileowner(targets);
535 return(ret);
538 /* operations on named packages in the local DB
539 * valid: no-op (plain -Q), list, info, check */
540 for(i = targets; i; i = alpm_list_next(i)) {
541 char *strname = alpm_list_getdata(i);
543 if(config->op_q_isfile) {
544 alpm_pkg_load(strname, 1, &pkg);
545 } else {
546 pkg = alpm_db_get_pkg(db_local, strname);
549 if(pkg == NULL) {
550 pm_fprintf(stderr, PM_LOG_ERROR, _("package \"%s\" not found\n"), strname);
551 ret = 1;
552 continue;
555 if(filter(pkg)) {
556 int value = display(pkg);
557 if(value != 0) {
558 ret = 1;
560 match = 1;
563 if(config->op_q_isfile) {
564 alpm_pkg_free(pkg);
565 pkg = NULL;
569 if(!match) {
570 ret = 1;
573 return(ret);
576 /* vim: set ts=2 sw=2 noet: */