Rename public functions with grp in their name
[pacman-ng.git] / src / pacman / query.c
blob826c2262f4d72a17a9f3a72a97c0361a9d6e477b
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 char *append;
114 size_t max_length;
115 alpm_list_t *t;
116 alpm_db_t *db_local;
118 /* This code is here for safety only */
119 if(targets == NULL) {
120 pm_fprintf(stderr, PM_LOG_ERROR, _("no file was specified for --owns\n"));
121 return 1;
124 /* Set up our root path buffer. We only need to copy the location of root in
125 * once, then we can just overwrite whatever file was there on the previous
126 * iteration. */
127 root = alpm_option_get_root(config->handle);
128 strncpy(path, root, PATH_MAX - 1);
129 append = path + strlen(path);
130 max_length = PATH_MAX - (append - path) - 1;
132 db_local = alpm_option_get_localdb(config->handle);
134 for(t = targets; t; t = alpm_list_next(t)) {
135 char *filename, *dname, *rpath;
136 const char *bname;
137 struct stat buf;
138 alpm_list_t *i;
139 int found = 0;
141 filename = strdup(alpm_list_getdata(t));
143 if(lstat(filename, &buf) == -1) {
144 /* if it is not a path but a program name, then check in PATH */
145 if(strchr(filename, '/') == NULL) {
146 if(search_path(&filename, &buf) == -1) {
147 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to find '%s' in PATH: %s\n"),
148 filename, strerror(errno));
149 ret++;
150 free(filename);
151 continue;
153 } else {
154 pm_fprintf(stderr, PM_LOG_ERROR, _("failed to read file '%s': %s\n"),
155 filename, strerror(errno));
156 ret++;
157 free(filename);
158 continue;
162 if(S_ISDIR(buf.st_mode)) {
163 pm_fprintf(stderr, PM_LOG_ERROR,
164 _("cannot determine ownership of directory '%s'\n"), filename);
165 ret++;
166 free(filename);
167 continue;
170 bname = mbasename(filename);
171 dname = mdirname(filename);
172 /* for files in '/', there is no directory name to match */
173 if(strcmp(dname, "") == 0) {
174 rpath = NULL;
175 } else {
176 rpath = resolve_path(dname);
178 if(!rpath) {
179 pm_fprintf(stderr, PM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"),
180 filename, strerror(errno));
181 free(filename);
182 free(dname);
183 free(rpath);
184 ret++;
185 continue;
188 free(dname);
190 for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) {
191 alpm_list_t *j;
192 alpm_pkg_t *info = alpm_list_getdata(i);
194 for(j = alpm_pkg_get_files(info); j && !found; j = alpm_list_next(j)) {
195 char *ppath, *pdname;
196 const char *pkgfile = alpm_list_getdata(j);
198 /* avoid the costly resolve_path usage if the basenames don't match */
199 if(strcmp(mbasename(pkgfile), bname) != 0) {
200 continue;
203 /* for files in '/', there is no directory name to match */
204 if(!rpath) {
205 print_query_fileowner(filename, info);
206 found = 1;
207 continue;
210 if(strlen(pkgfile) > max_length) {
211 pm_fprintf(stderr, PM_LOG_ERROR, _("path too long: %s%s\n"), root, pkgfile);
213 /* concatenate our file and the root path */
214 strcpy(append, pkgfile);
216 pdname = mdirname(path);
217 ppath = resolve_path(pdname);
218 free(pdname);
220 if(ppath && strcmp(ppath, rpath) == 0) {
221 print_query_fileowner(filename, info);
222 found = 1;
224 free(ppath);
227 if(!found) {
228 pm_fprintf(stderr, PM_LOG_ERROR, _("No package owns %s\n"), filename);
229 ret++;
231 free(filename);
232 free(rpath);
235 return ret;
238 /* search the local database for a matching package */
239 static int query_search(alpm_list_t *targets)
241 alpm_list_t *i, *searchlist;
242 int freelist;
243 alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
245 /* if we have a targets list, search for packages matching it */
246 if(targets) {
247 searchlist = alpm_db_search(db_local, targets);
248 freelist = 1;
249 } else {
250 searchlist = alpm_db_get_pkgcache(db_local);
251 freelist = 0;
253 if(searchlist == NULL) {
254 return 1;
257 for(i = searchlist; i; i = alpm_list_next(i)) {
258 alpm_list_t *grp;
259 alpm_pkg_t *pkg = alpm_list_getdata(i);
261 if(!config->quiet) {
262 printf("local/%s %s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
263 } else {
264 printf("%s", alpm_pkg_get_name(pkg));
268 if(!config->quiet) {
269 if((grp = alpm_pkg_get_groups(pkg)) != NULL) {
270 alpm_list_t *k;
271 printf(" (");
272 for(k = grp; k; k = alpm_list_next(k)) {
273 const char *group = alpm_list_getdata(k);
274 printf("%s", group);
275 if(alpm_list_next(k)) {
276 /* only print a spacer if there are more groups */
277 printf(" ");
280 printf(")");
283 /* we need a newline and initial indent first */
284 printf("\n ");
285 indentprint(alpm_pkg_get_desc(pkg), 4);
287 printf("\n");
290 /* we only want to free if the list was a search list */
291 if(freelist) {
292 alpm_list_free(searchlist);
294 return 0;
297 static int query_group(alpm_list_t *targets)
299 alpm_list_t *i, *j;
300 char *grpname = NULL;
301 int ret = 0;
302 alpm_db_t *db_local = alpm_option_get_localdb(config->handle);
304 if(targets == NULL) {
305 for(j = alpm_db_get_groupcache(db_local); j; j = alpm_list_next(j)) {
306 alpm_group_t *grp = alpm_list_getdata(j);
307 const alpm_list_t *p;
309 for(p = grp->packages; p; p = alpm_list_next(p)) {
310 alpm_pkg_t *pkg = alpm_list_getdata(p);
311 printf("%s %s\n", grp->name, alpm_pkg_get_name(pkg));
314 } else {
315 for(i = targets; i; i = alpm_list_next(i)) {
316 alpm_group_t *grp;
317 grpname = alpm_list_getdata(i);
318 grp = alpm_db_readgroup(db_local, grpname);
319 if(grp) {
320 const alpm_list_t *p;
321 for(p = grp->packages; p; p = alpm_list_next(p)) {
322 if(!config->quiet) {
323 printf("%s %s\n", grpname,
324 alpm_pkg_get_name(alpm_list_getdata(p)));
325 } else {
326 printf("%s\n", alpm_pkg_get_name(alpm_list_getdata(p)));
329 } else {
330 pm_fprintf(stderr, PM_LOG_ERROR, _("group \"%s\" was not found\n"), grpname);
331 ret++;
335 return ret;
338 static int is_foreign(alpm_pkg_t *pkg)
340 const char *pkgname = alpm_pkg_get_name(pkg);
341 alpm_list_t *j;
342 alpm_list_t *sync_dbs = alpm_option_get_syncdbs(config->handle);
344 int match = 0;
345 for(j = sync_dbs; j; j = alpm_list_next(j)) {
346 alpm_db_t *db = alpm_list_getdata(j);
347 alpm_pkg_t *findpkg = alpm_db_get_pkg(db, pkgname);
348 if(findpkg) {
349 match = 1;
350 break;
353 if(match == 0) {
354 return 1;
356 return 0;
359 static int is_unrequired(alpm_pkg_t *pkg)
361 alpm_list_t *requiredby = alpm_pkg_compute_requiredby(pkg);
362 if(requiredby == NULL) {
363 return 1;
365 FREELIST(requiredby);
366 return 0;
369 static int filter(alpm_pkg_t *pkg)
371 /* check if this package was explicitly installed */
372 if(config->op_q_explicit &&
373 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_EXPLICIT) {
374 return 0;
376 /* check if this package was installed as a dependency */
377 if(config->op_q_deps &&
378 alpm_pkg_get_reason(pkg) != PM_PKG_REASON_DEPEND) {
379 return 0;
381 /* check if this pkg isn't in a sync DB */
382 if(config->op_q_foreign && !is_foreign(pkg)) {
383 return 0;
385 /* check if this pkg is unrequired */
386 if(config->op_q_unrequired && !is_unrequired(pkg)) {
387 return 0;
389 /* check if this pkg is outdated */
390 if(config->op_q_upgrade && (alpm_sync_newversion(pkg,
391 alpm_option_get_syncdbs(config->handle)) == NULL)) {
392 return 0;
394 return 1;
397 /* Loop through the packages. For each package,
398 * loop through files to check if they exist. */
399 static int check(alpm_pkg_t *pkg)
401 alpm_list_t *i;
402 const char *root;
403 int allfiles = 0, errors = 0;
404 size_t rootlen;
405 char f[PATH_MAX];
407 root = alpm_option_get_root(config->handle);
408 rootlen = strlen(root);
409 if(rootlen + 1 > PATH_MAX) {
410 /* we are in trouble here */
411 pm_fprintf(stderr, PM_LOG_ERROR, _("path too long: %s%s\n"), root, "");
412 return 1;
414 strcpy(f, root);
416 const char *pkgname = alpm_pkg_get_name(pkg);
417 for(i = alpm_pkg_get_files(pkg); i; i = alpm_list_next(i)) {
418 struct stat st;
419 const char *path = alpm_list_getdata(i);
421 if(rootlen + 1 + strlen(path) > PATH_MAX) {
422 pm_fprintf(stderr, PM_LOG_WARNING, _("path too long: %s%s\n"), root, path);
423 continue;
425 strcpy(f + rootlen, path);
426 allfiles++;
427 /* use lstat to prevent errors from symlinks */
428 if(lstat(f, &st) != 0) {
429 if(config->quiet) {
430 printf("%s %s\n", pkgname, f);
431 } else {
432 pm_printf(PM_LOG_WARNING, "%s: %s (%s)\n",
433 pkgname, f, strerror(errno));
435 errors++;
439 if(!config->quiet) {
440 printf(_n("%s: %d total file, ", "%s: %d total files, ",
441 (unsigned long)allfiles), pkgname, allfiles);
442 printf(_n("%d missing file\n", "%d missing files\n",
443 (unsigned long)errors), errors);
446 return (errors != 0 ? 1 : 0);
449 static int display(alpm_pkg_t *pkg)
451 int ret = 0;
453 if(config->op_q_info) {
454 if(config->op_q_isfile) {
455 dump_pkg_full(pkg, PKG_FROM_FILE, 0);
456 } else {
457 dump_pkg_full(pkg, PKG_FROM_LOCALDB, config->op_q_info > 1);
460 if(config->op_q_list) {
461 dump_pkg_files(pkg, config->quiet);
463 if(config->op_q_changelog) {
464 dump_pkg_changelog(pkg);
466 if(config->op_q_check) {
467 ret = check(pkg);
469 if(!config->op_q_info && !config->op_q_list
470 && !config->op_q_changelog && !config->op_q_check) {
471 if(!config->quiet) {
472 printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
473 } else {
474 printf("%s\n", alpm_pkg_get_name(pkg));
477 return ret;
480 int pacman_query(alpm_list_t *targets)
482 int ret = 0;
483 int match = 0;
484 alpm_list_t *i;
485 alpm_pkg_t *pkg = NULL;
486 alpm_db_t *db_local;
488 /* First: operations that do not require targets */
490 /* search for a package */
491 if(config->op_q_search) {
492 ret = query_search(targets);
493 return ret;
496 /* looking for groups */
497 if(config->group) {
498 ret = query_group(targets);
499 return ret;
502 if(config->op_q_foreign) {
503 /* ensure we have at least one valid sync db set up */
504 alpm_list_t *sync_dbs = alpm_option_get_syncdbs(config->handle);
505 if(sync_dbs == NULL) {
506 pm_printf(PM_LOG_ERROR, _("no usable package repositories configured.\n"));
507 return 1;
511 db_local = alpm_option_get_localdb(config->handle);
513 /* operations on all packages in the local DB
514 * valid: no-op (plain -Q), list, info, check
515 * invalid: isfile, owns */
516 if(targets == NULL) {
517 if(config->op_q_isfile || config->op_q_owns) {
518 pm_printf(PM_LOG_ERROR, _("no targets specified (use -h for help)\n"));
519 return 1;
522 for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) {
523 pkg = alpm_list_getdata(i);
524 if(filter(pkg)) {
525 int value = display(pkg);
526 if(value != 0) {
527 ret = 1;
529 match = 1;
532 if(!match) {
533 ret = 1;
535 return ret;
538 /* Second: operations that require target(s) */
540 /* determine the owner of a file */
541 if(config->op_q_owns) {
542 ret = query_fileowner(targets);
543 return ret;
546 /* operations on named packages in the local DB
547 * valid: no-op (plain -Q), list, info, check */
548 for(i = targets; i; i = alpm_list_next(i)) {
549 char *strname = alpm_list_getdata(i);
551 if(config->op_q_isfile) {
552 alpm_pkg_load(config->handle, strname, 1, PM_PGP_VERIFY_OPTIONAL, &pkg);
553 } else {
554 pkg = alpm_db_get_pkg(db_local, strname);
557 if(pkg == NULL) {
558 pm_fprintf(stderr, PM_LOG_ERROR, _("package \"%s\" not found\n"), strname);
559 ret = 1;
560 continue;
563 if(filter(pkg)) {
564 int value = display(pkg);
565 if(value != 0) {
566 ret = 1;
568 match = 1;
571 if(config->op_q_isfile) {
572 alpm_pkg_free(pkg);
573 pkg = NULL;
577 if(!match) {
578 ret = 1;
581 return ret;
584 /* vim: set ts=2 sw=2 noet: */