Header inclusion cleanup
[pacman-ng.git] / src / pacman / query.c
bloba3546badac1209facee1aeadd7ecbd932da2a4ae
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>
30 #include <alpm.h>
31 #include <alpm_list.h>
33 /* pacman */
34 #include "pacman.h"
35 #include "package.h"
36 #include "conf.h"
37 #include "util.h"
39 static char *resolve_path(const char *file)
41 char *str = NULL;
43 str = calloc(PATH_MAX + 1, sizeof(char));
44 if(!str) {
45 return NULL;
48 if(!realpath(file, str)) {
49 free(str);
50 return NULL;
53 return str;
56 /* check if filename exists in PATH */
57 static int search_path(char **filename, struct stat *bufptr)
59 char *envpath, *envpathsplit, *path, *fullname;
60 size_t flen;
62 if((envpath = getenv("PATH")) == NULL) {
63 return -1;
65 if((envpath = envpathsplit = strdup(envpath)) == NULL) {
66 return -1;
69 flen = strlen(*filename);
71 while((path = strsep(&envpathsplit, ":")) != NULL) {
72 size_t plen = strlen(path);
74 /* strip the trailing slash if one exists */
75 while(path[plen - 1] == '/') {
76 path[--plen] = '\0';
79 fullname = malloc(plen + flen + 2);
80 sprintf(fullname, "%s/%s", path, *filename);
82 if(lstat(fullname, bufptr) == 0) {
83 free(*filename);
84 *filename = fullname;
85 free(envpath);
86 return 0;
88 free(fullname);
90 free(envpath);
91 return -1;
94 static void print_query_fileowner(const char *filename, pmpkg_t *info)
96 if(!config->quiet) {
97 printf(_("%s is owned by %s %s\n"), filename,
98 alpm_pkg_get_name(info), alpm_pkg_get_version(info));
99 } else {
100 printf("%s\n", alpm_pkg_get_name(info));
104 static int query_fileowner(alpm_list_t *targets)
106 int ret = 0;
107 char path[PATH_MAX];
108 const char *root;
109 char *append;
110 size_t max_length;
111 alpm_list_t *t;
112 pmdb_t *db_local;
114 /* This code is here for safety only */
115 if(targets == NULL) {
116 pm_fprintf(stderr, PM_LOG_ERROR, _("no file was specified for --owns\n"));
117 return 1;
120 /* Set up our root path buffer. We only need to copy the location of root in
121 * once, then we can just overwrite whatever file was there on the previous
122 * iteration. */
123 root = alpm_option_get_root();
124 strncpy(path, root, PATH_MAX - 1);
125 append = path + strlen(path);
126 max_length = PATH_MAX - (append - path) - 1;
128 db_local = alpm_option_get_localdb();
130 for(t = targets; t; t = alpm_list_next(t)) {
131 char *filename, *dname, *rpath;
132 const char *bname;
133 struct stat buf;
134 alpm_list_t *i;
135 int found = 0;
137 filename = strdup(alpm_list_getdata(t));
139 if(lstat(filename, &buf) == -1) {
140 /* if it is not a path but a program name, then check in PATH */
141 if(strchr(filename, '/') == NULL) {
142 if(search_path(&filename, &buf) == -1) {
143 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to find '%s' in PATH: %s\n"),
144 filename, strerror(errno));
145 ret++;
146 free(filename);
147 continue;
149 } else {
150 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to read file '%s': %s\n"),
151 filename, strerror(errno));
152 ret++;
153 free(filename);
154 continue;
158 if(S_ISDIR(buf.st_mode)) {
159 pm_fprintf(stderr, PM_LOG_ERROR,
160 _("cannot determine ownership of directory '%s'\n"), filename);
161 ret++;
162 free(filename);
163 continue;
166 bname = mbasename(filename);
167 dname = mdirname(filename);
168 /* for files in '/', there is no directory name to match */
169 if(strcmp(dname, "") == 0) {
170 rpath = NULL;
171 } else {
172 rpath = resolve_path(dname);
174 if(!rpath) {
175 pm_fprintf(stderr, PM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"),
176 filename, strerror(errno));
177 free(filename);
178 free(dname);
179 free(rpath);
180 ret++;
181 continue;
184 free(dname);
186 for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) {
187 alpm_list_t *j;
188 pmpkg_t *info = alpm_list_getdata(i);
190 for(j = alpm_pkg_get_files(info); j && !found; j = alpm_list_next(j)) {
191 char *ppath, *pdname;
192 const char *pkgfile = alpm_list_getdata(j);
194 /* avoid the costly resolve_path usage if the basenames don't match */
195 if(strcmp(mbasename(pkgfile), bname) != 0) {
196 continue;
199 /* for files in '/', there is no directory name to match */
200 if(!rpath) {
201 print_query_fileowner(filename, info);
202 found = 1;
203 continue;
206 if(strlen(pkgfile) > max_length) {
207 pm_fprintf(stderr, PM_LOG_ERROR, _("path too long: %s%s\n"), root, pkgfile);
209 /* concatenate our file and the root path */
210 strcpy(append, pkgfile);
212 pdname = mdirname(path);
213 ppath = resolve_path(pdname);
214 free(pdname);
216 if(ppath && strcmp(ppath, rpath) == 0) {
217 print_query_fileowner(filename, info);
218 found = 1;
220 free(ppath);
223 if(!found) {
224 pm_fprintf(stderr, PM_LOG_ERROR, _("No package owns %s\n"), filename);
225 ret++;
227 free(filename);
228 free(rpath);
231 return ret;
234 /* search the local database for a matching package */
235 static int query_search(alpm_list_t *targets)
237 alpm_list_t *i, *searchlist;
238 int freelist;
239 pmdb_t *db_local = alpm_option_get_localdb();
241 /* if we have a targets list, search for packages matching it */
242 if(targets) {
243 searchlist = alpm_db_search(db_local, targets);
244 freelist = 1;
245 } else {
246 searchlist = alpm_db_get_pkgcache(db_local);
247 freelist = 0;
249 if(searchlist == NULL) {
250 return 1;
253 for(i = searchlist; i; i = alpm_list_next(i)) {
254 alpm_list_t *grp;
255 pmpkg_t *pkg = alpm_list_getdata(i);
257 if(!config->quiet) {
258 printf("local/%s %s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
259 } else {
260 printf("%s", alpm_pkg_get_name(pkg));
264 if(!config->quiet) {
265 if((grp = alpm_pkg_get_groups(pkg)) != NULL) {
266 alpm_list_t *k;
267 printf(" (");
268 for(k = grp; k; k = alpm_list_next(k)) {
269 const char *group = alpm_list_getdata(k);
270 printf("%s", group);
271 if(alpm_list_next(k)) {
272 /* only print a spacer if there are more groups */
273 printf(" ");
276 printf(")");
279 /* we need a newline and initial indent first */
280 printf("\n ");
281 indentprint(alpm_pkg_get_desc(pkg), 4);
283 printf("\n");
286 /* we only want to free if the list was a search list */
287 if(freelist) {
288 alpm_list_free(searchlist);
290 return 0;
293 static int query_group(alpm_list_t *targets)
295 alpm_list_t *i, *j;
296 char *grpname = NULL;
297 int ret = 0;
298 pmdb_t *db_local = alpm_option_get_localdb();
300 if(targets == NULL) {
301 for(j = alpm_db_get_grpcache(db_local); j; j = alpm_list_next(j)) {
302 pmgrp_t *grp = alpm_list_getdata(j);
303 const alpm_list_t *p, *packages;
304 const char *grpname;
306 grpname = alpm_grp_get_name(grp);
307 packages = alpm_grp_get_pkgs(grp);
309 for(p = packages; p; p = alpm_list_next(p)) {
310 printf("%s %s\n", grpname, alpm_pkg_get_name(alpm_list_getdata(p)));
313 } else {
314 for(i = targets; i; i = alpm_list_next(i)) {
315 pmgrp_t *grp;
316 grpname = alpm_list_getdata(i);
317 grp = alpm_db_readgrp(db_local, grpname);
318 if(grp) {
319 const alpm_list_t *p, *packages = alpm_grp_get_pkgs(grp);
320 for(p = packages; p; p = alpm_list_next(p)) {
321 if(!config->quiet) {
322 printf("%s %s\n", grpname,
323 alpm_pkg_get_name(alpm_list_getdata(p)));
324 } else {
325 printf("%s\n", alpm_pkg_get_name(alpm_list_getdata(p)));
328 } else {
329 pm_fprintf(stderr, PM_LOG_ERROR, _("group \"%s\" was not found\n"), grpname);
330 ret++;
334 return ret;
337 static int is_foreign(pmpkg_t *pkg)
339 const char *pkgname = alpm_pkg_get_name(pkg);
340 alpm_list_t *j;
341 alpm_list_t *sync_dbs = alpm_option_get_syncdbs();
343 int match = 0;
344 for(j = sync_dbs; j; j = alpm_list_next(j)) {
345 pmdb_t *db = alpm_list_getdata(j);
346 pmpkg_t *findpkg = alpm_db_get_pkg(db, pkgname);
347 if(findpkg) {
348 match = 1;
349 break;
352 if(match == 0) {
353 return 1;
355 return 0;
358 static int is_unrequired(pmpkg_t *pkg)
360 alpm_list_t *requiredby = alpm_pkg_compute_requiredby(pkg);
361 if(requiredby == NULL) {
362 return 1;
364 FREELIST(requiredby);
365 return 0;
368 static int filter(pmpkg_t *pkg)
370 /* check if this package was explicitly installed */
371 if(config->op_q_explicit &&
372 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_EXPLICIT) {
373 return 0;
375 /* check if this package was installed as a dependency */
376 if(config->op_q_deps &&
377 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_DEPEND) {
378 return 0;
380 /* check if this pkg isn't in a sync DB */
381 if(config->op_q_foreign && !is_foreign(pkg)) {
382 return 0;
384 /* check if this pkg is unrequired */
385 if(config->op_q_unrequired && !is_unrequired(pkg)) {
386 return 0;
388 /* check if this pkg is outdated */
389 if(config->op_q_upgrade && (alpm_sync_newversion(pkg, alpm_option_get_syncdbs()) == NULL)) {
390 return 0;
392 return 1;
395 /* Loop through the packages. For each package,
396 * loop through files to check if they exist. */
397 static int check(pmpkg_t *pkg)
399 alpm_list_t *i;
400 const char *root;
401 int allfiles = 0, errors = 0;
402 size_t rootlen;
403 char f[PATH_MAX];
405 root = alpm_option_get_root();
406 rootlen = strlen(root);
407 if(rootlen + 1 > PATH_MAX) {
408 /* we are in trouble here */
409 pm_fprintf(stderr, PM_LOG_ERROR, _("path too long: %s%s\n"), root, "");
410 return 1;
412 strcpy(f, root);
414 const char *pkgname = alpm_pkg_get_name(pkg);
415 for(i = alpm_pkg_get_files(pkg); i; i = alpm_list_next(i)) {
416 struct stat st;
417 const char *path = alpm_list_getdata(i);
419 if(rootlen + 1 + strlen(path) > PATH_MAX) {
420 pm_fprintf(stderr, PM_LOG_WARNING, _("path too long: %s%s\n"), root, path);
421 continue;
423 strcpy(f + rootlen, path);
424 allfiles++;
425 /* use lstat to prevent errors from symlinks */
426 if(lstat(f, &st) != 0) {
427 if(config->quiet) {
428 printf("%s %s\n", pkgname, f);
429 } else {
430 pm_printf(PM_LOG_WARNING, "%s: %s (%s)\n",
431 pkgname, f, strerror(errno));
433 errors++;
437 if(!config->quiet) {
438 printf(_n("%s: %d total file, ", "%s: %d total files, ",
439 (unsigned long)allfiles), pkgname, allfiles);
440 printf(_n("%d missing file\n", "%d missing files\n",
441 (unsigned long)errors), errors);
444 return (errors != 0 ? 1 : 0);
447 static int display(pmpkg_t *pkg)
449 int ret = 0;
451 if(config->op_q_info) {
452 if(config->op_q_isfile) {
453 /* omit info that isn't applicable for a file package */
454 dump_pkg_full(pkg, 0);
455 } else {
456 dump_pkg_full(pkg, config->op_q_info);
459 if(config->op_q_list) {
460 dump_pkg_files(pkg, config->quiet);
462 if(config->op_q_changelog) {
463 dump_pkg_changelog(pkg);
465 if(config->op_q_check) {
466 ret = check(pkg);
468 if(!config->op_q_info && !config->op_q_list
469 && !config->op_q_changelog && !config->op_q_check) {
470 if(!config->quiet) {
471 printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
472 } else {
473 printf("%s\n", alpm_pkg_get_name(pkg));
476 return ret;
479 int pacman_query(alpm_list_t *targets)
481 int ret = 0;
482 int match = 0;
483 alpm_list_t *i;
484 pmpkg_t *pkg = NULL;
485 pmdb_t *db_local;
487 /* First: operations that do not require targets */
489 /* search for a package */
490 if(config->op_q_search) {
491 ret = query_search(targets);
492 return ret;
495 /* looking for groups */
496 if(config->group) {
497 ret = query_group(targets);
498 return ret;
501 if(config->op_q_foreign) {
502 /* ensure we have at least one valid sync db set up */
503 alpm_list_t *sync_dbs = alpm_option_get_syncdbs();
504 if(sync_dbs == NULL || alpm_list_count(sync_dbs) == 0) {
505 pm_printf(PM_LOG_ERROR, _("no usable package repositories configured.\n"));
506 return 1;
510 db_local = alpm_option_get_localdb();
512 /* operations on all packages in the local DB
513 * valid: no-op (plain -Q), list, info, check
514 * invalid: isfile, owns */
515 if(targets == NULL) {
516 if(config->op_q_isfile || config->op_q_owns) {
517 pm_printf(PM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
518 return 1;
521 for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) {
522 pkg = alpm_list_getdata(i);
523 if(filter(pkg)) {
524 int value = display(pkg);
525 if(value != 0) {
526 ret = 1;
528 match = 1;
531 if(!match) {
532 ret = 1;
534 return ret;
537 /* Second: operations that require target(s) */
539 /* determine the owner of a file */
540 if(config->op_q_owns) {
541 ret = query_fileowner(targets);
542 return ret;
545 /* operations on named packages in the local DB
546 * valid: no-op (plain -Q), list, info, check */
547 for(i = targets; i; i = alpm_list_next(i)) {
548 char *strname = alpm_list_getdata(i);
550 if(config->op_q_isfile) {
551 alpm_pkg_load(strname, 1, &pkg);
552 } else {
553 pkg = alpm_db_get_pkg(db_local, strname);
556 if(pkg == NULL) {
557 pm_fprintf(stderr, PM_LOG_ERROR, _("package \"%s\" not found\n"), strname);
558 ret = 1;
559 continue;
562 if(filter(pkg)) {
563 int value = display(pkg);
564 if(value != 0) {
565 ret = 1;
567 match = 1;
570 if(config->op_q_isfile) {
571 alpm_pkg_free(pkg);
572 pkg = NULL;
576 if(!match) {
577 ret = 1;
580 return ret;
583 /* vim: set ts=2 sw=2 noet: */