Streamline alpm_splitdep() comparisons
[pacman-ng.git] / src / pacman / query.c
blob5f2b37eb59e2e69090625468ff5cc19ce3fe5a7c
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, 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 if(!fullname) {
81 free(envpath);
82 return -1;
84 sprintf(fullname, "%s/%s", path, *filename);
86 if(lstat(fullname, bufptr) == 0) {
87 free(*filename);
88 *filename = fullname;
89 free(envpath);
90 return 0;
92 free(fullname);
94 free(envpath);
95 return -1;
98 static void print_query_fileowner(const char *filename, alpm_pkg_t *info)
100 if(!config->quiet) {
101 printf(_("%s is owned by %s %s\n"), filename,
102 alpm_pkg_get_name(info), alpm_pkg_get_version(info));
103 } else {
104 printf("%s\n", alpm_pkg_get_name(info));
108 static int query_fileowner(alpm_list_t *targets)
110 int ret = 0;
111 char path[PATH_MAX];
112 const char *root;
113 size_t rootlen;
114 alpm_list_t *t;
115 alpm_db_t *db_local;
117 /* This code is here for safety only */
118 if(targets == NULL) {
119 pm_fprintf(stderr, ALPM_LOG_ERROR, _("no file was specified for --owns\n"));
120 return 1;
123 /* Set up our root path buffer. We only need to copy the location of root in
124 * once, then we can just overwrite whatever file was there on the previous
125 * iteration. */
126 root = alpm_option_get_root(config->handle);
127 rootlen = strlen(root);
128 if(rootlen + 1 > PATH_MAX) {
129 /* we are in trouble here */
130 pm_fprintf(stderr, ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, "");
131 return 1;
133 strcpy(path, root);
135 db_local = alpm_option_get_localdb(config->handle);
137 for(t = targets; t; t = alpm_list_next(t)) {
138 char *filename, *dname, *rpath;
139 const char *bname;
140 struct stat buf;
141 alpm_list_t *i;
142 int found = 0;
144 filename = strdup(alpm_list_getdata(t));
146 if(lstat(filename, &buf) == -1) {
147 /* if it is not a path but a program name, then check in PATH */
148 if(strchr(filename, '/') == NULL) {
149 if(search_path(&filename, &buf) == -1) {
150 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to find '%s' in PATH: %s\n"),
151 filename, strerror(errno));
152 ret++;
153 free(filename);
154 continue;
156 } else {
157 pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to read file '%s': %s\n"),
158 filename, strerror(errno));
159 ret++;
160 free(filename);
161 continue;
165 if(S_ISDIR(buf.st_mode)) {
166 pm_fprintf(stderr, ALPM_LOG_ERROR,
167 _("cannot determine ownership of directory '%s'\n"), filename);
168 ret++;
169 free(filename);
170 continue;
173 bname = mbasename(filename);
174 dname = mdirname(filename);
175 /* for files in '/', there is no directory name to match */
176 if(strcmp(dname, "") == 0) {
177 rpath = NULL;
178 } else {
179 rpath = resolve_path(dname);
181 if(!rpath) {
182 pm_fprintf(stderr, ALPM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"),
183 filename, strerror(errno));
184 free(filename);
185 free(dname);
186 free(rpath);
187 ret++;
188 continue;
191 free(dname);
193 for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) {
194 alpm_pkg_t *info = alpm_list_getdata(i);
195 alpm_filelist_t *filelist = alpm_pkg_get_files(info);
196 size_t j;
198 for(j = 0; j < filelist->count; j++) {
199 const alpm_file_t *file = filelist->files + j;
200 char *ppath, *pdname;
201 const char *pkgfile = file->name;
203 /* avoid the costly resolve_path usage if the basenames don't match */
204 if(strcmp(mbasename(pkgfile), bname) != 0) {
205 continue;
208 /* for files in '/', there is no directory name to match */
209 if(!rpath) {
210 print_query_fileowner(filename, info);
211 found = 1;
212 continue;
215 if(rootlen + 1 + strlen(pkgfile) > PATH_MAX) {
216 pm_fprintf(stderr, ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, pkgfile);
218 /* concatenate our file and the root path */
219 strcpy(path + rootlen, pkgfile);
221 pdname = mdirname(path);
222 ppath = resolve_path(pdname);
223 free(pdname);
225 if(ppath && strcmp(ppath, rpath) == 0) {
226 print_query_fileowner(filename, info);
227 found = 1;
229 free(ppath);
232 if(!found) {
233 pm_fprintf(stderr, ALPM_LOG_ERROR, _("No package owns %s\n"), filename);
234 ret++;
236 free(filename);
237 free(rpath);
240 return ret;
243 /* search the local database for a matching package */
244 static int query_search(alpm_list_t *targets)
246 alpm_list_t *i, *searchlist;
247 int freelist;
248 alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
250 /* if we have a targets list, search for packages matching it */
251 if(targets) {
252 searchlist = alpm_db_search(db_local, targets);
253 freelist = 1;
254 } else {
255 searchlist = alpm_db_get_pkgcache(db_local);
256 freelist = 0;
258 if(searchlist == NULL) {
259 return 1;
262 for(i = searchlist; i; i = alpm_list_next(i)) {
263 alpm_list_t *grp;
264 alpm_pkg_t *pkg = alpm_list_getdata(i);
266 if(!config->quiet) {
267 printf("local/%s %s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
268 } else {
269 printf("%s", alpm_pkg_get_name(pkg));
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 alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
309 if(targets == NULL) {
310 for(j = alpm_db_get_groupcache(db_local); j; j = alpm_list_next(j)) {
311 alpm_group_t *grp = alpm_list_getdata(j);
312 const alpm_list_t *p;
314 for(p = grp->packages; p; p = alpm_list_next(p)) {
315 alpm_pkg_t *pkg = alpm_list_getdata(p);
316 printf("%s %s\n", grp->name, alpm_pkg_get_name(pkg));
319 } else {
320 for(i = targets; i; i = alpm_list_next(i)) {
321 alpm_group_t *grp;
322 grpname = alpm_list_getdata(i);
323 grp = alpm_db_readgroup(db_local, grpname);
324 if(grp) {
325 const alpm_list_t *p;
326 for(p = grp->packages; p; p = alpm_list_next(p)) {
327 if(!config->quiet) {
328 printf("%s %s\n", grpname,
329 alpm_pkg_get_name(alpm_list_getdata(p)));
330 } else {
331 printf("%s\n", alpm_pkg_get_name(alpm_list_getdata(p)));
334 } else {
335 pm_fprintf(stderr, ALPM_LOG_ERROR, _("group '%s' was not found\n"), grpname);
336 ret++;
340 return ret;
343 static int is_foreign(alpm_pkg_t *pkg)
345 const char *pkgname = alpm_pkg_get_name(pkg);
346 alpm_list_t *j;
347 alpm_list_t *sync_dbs = alpm_option_get_syncdbs(config->handle);
349 int match = 0;
350 for(j = sync_dbs; j; j = alpm_list_next(j)) {
351 alpm_db_t *db = alpm_list_getdata(j);
352 alpm_pkg_t *findpkg = alpm_db_get_pkg(db, pkgname);
353 if(findpkg) {
354 match = 1;
355 break;
358 if(match == 0) {
359 return 1;
361 return 0;
364 static int is_unrequired(alpm_pkg_t *pkg)
366 alpm_list_t *requiredby = alpm_pkg_compute_requiredby(pkg);
367 if(requiredby == NULL) {
368 return 1;
370 FREELIST(requiredby);
371 return 0;
374 static int filter(alpm_pkg_t *pkg)
376 /* check if this package was explicitly installed */
377 if(config->op_q_explicit &&
378 alpm_pkg_get_reason(pkg) != ALPM_PKG_REASON_EXPLICIT) {
379 return 0;
381 /* check if this package was installed as a dependency */
382 if(config->op_q_deps &&
383 alpm_pkg_get_reason(pkg) != ALPM_PKG_REASON_DEPEND) {
384 return 0;
386 /* check if this pkg isn't in a sync DB */
387 if(config->op_q_foreign && !is_foreign(pkg)) {
388 return 0;
390 /* check if this pkg is unrequired */
391 if(config->op_q_unrequired && !is_unrequired(pkg)) {
392 return 0;
394 /* check if this pkg is outdated */
395 if(config->op_q_upgrade && (alpm_sync_newversion(pkg,
396 alpm_option_get_syncdbs(config->handle)) == NULL)) {
397 return 0;
399 return 1;
402 /* Loop through the packages. For each package,
403 * loop through files to check if they exist. */
404 static int check(alpm_pkg_t *pkg)
406 const char *root, *pkgname;
407 int allfiles = 0, errors = 0;
408 size_t rootlen;
409 char f[PATH_MAX];
410 alpm_filelist_t *filelist;
411 size_t i;
413 root = alpm_option_get_root(config->handle);
414 rootlen = strlen(root);
415 if(rootlen + 1 > PATH_MAX) {
416 /* we are in trouble here */
417 pm_fprintf(stderr, ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, "");
418 return 1;
420 strcpy(f, root);
422 pkgname = alpm_pkg_get_name(pkg);
423 filelist = alpm_pkg_get_files(pkg);
424 for(i = 0; i < filelist->count; i++) {
425 const alpm_file_t *file = filelist->files + i;
426 struct stat st;
427 const char *path = file->name;
429 if(rootlen + 1 + strlen(path) > PATH_MAX) {
430 pm_fprintf(stderr, ALPM_LOG_WARNING, _("path too long: %s%s\n"), root, path);
431 continue;
433 strcpy(f + rootlen, path);
434 allfiles++;
435 /* use lstat to prevent errors from symlinks */
436 if(lstat(f, &st) != 0) {
437 if(config->quiet) {
438 printf("%s %s\n", pkgname, f);
439 } else {
440 pm_printf(ALPM_LOG_WARNING, "%s: %s (%s)\n",
441 pkgname, f, strerror(errno));
443 errors++;
447 if(!config->quiet) {
448 printf(_n("%s: %d total file, ", "%s: %d total files, ",
449 (unsigned long)allfiles), pkgname, allfiles);
450 printf(_n("%d missing file\n", "%d missing files\n",
451 (unsigned long)errors), errors);
454 return (errors != 0 ? 1 : 0);
457 static int display(alpm_pkg_t *pkg)
459 int ret = 0;
461 if(config->op_q_info) {
462 if(config->op_q_isfile) {
463 dump_pkg_full(pkg, 0);
464 } else {
465 dump_pkg_full(pkg, config->op_q_info > 1);
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 alpm_pkg_t *pkg = NULL;
494 alpm_db_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 || config->op_q_upgrade) {
511 if(check_syncdbs(1, 1)) {
512 return 1;
516 db_local = alpm_option_get_localdb(config->handle);
518 /* operations on all packages in the local DB
519 * valid: no-op (plain -Q), list, info, check
520 * invalid: isfile, owns */
521 if(targets == NULL) {
522 if(config->op_q_isfile || config->op_q_owns) {
523 pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
524 return 1;
527 for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) {
528 pkg = alpm_list_getdata(i);
529 if(filter(pkg)) {
530 int value = display(pkg);
531 if(value != 0) {
532 ret = 1;
534 match = 1;
537 if(!match) {
538 ret = 1;
540 return ret;
543 /* Second: operations that require target(s) */
545 /* determine the owner of a file */
546 if(config->op_q_owns) {
547 ret = query_fileowner(targets);
548 return ret;
551 /* operations on named packages in the local DB
552 * valid: no-op (plain -Q), list, info, check */
553 for(i = targets; i; i = alpm_list_next(i)) {
554 char *strname = alpm_list_getdata(i);
556 if(config->op_q_isfile) {
557 alpm_pkg_load(config->handle, strname, 1, 0, &pkg);
558 } else {
559 pkg = alpm_db_get_pkg(db_local, strname);
562 if(pkg == NULL) {
563 switch(alpm_errno(config->handle)) {
564 case ALPM_ERR_PKG_NOT_FOUND:
565 pm_fprintf(stderr, ALPM_LOG_ERROR,
566 _("package '%s' was not found\n"), strname);
567 break;
568 default:
569 pm_fprintf(stderr, ALPM_LOG_ERROR,
570 _("could not load package '%s': %s\n"), strname,
571 alpm_strerror(alpm_errno(config->handle)));
572 break;
574 ret = 1;
575 continue;
578 if(filter(pkg)) {
579 int value = display(pkg);
580 if(value != 0) {
581 ret = 1;
583 match = 1;
586 if(config->op_q_isfile) {
587 alpm_pkg_free(pkg);
588 pkg = NULL;
592 if(!match) {
593 ret = 1;
596 return ret;
599 /* vim: set ts=2 sw=2 noet: */