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/>.
31 #include <alpm_list.h>
39 static char *resolve_path(const char *file
)
43 str
= calloc(PATH_MAX
, sizeof(char));
48 if(!realpath(file
, str
)) {
56 /* check if filename exists in PATH */
57 static int search_path(char **filename
, struct stat
*bufptr
)
59 char *envpath
, *envpathsplit
, *path
, *fullname
;
62 if((envpath
= getenv("PATH")) == NULL
) {
65 if((envpath
= envpathsplit
= strdup(envpath
)) == NULL
) {
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] == '/') {
79 fullname
= malloc(plen
+ flen
+ 2);
84 sprintf(fullname
, "%s/%s", path
, *filename
);
86 if(lstat(fullname
, bufptr
) == 0) {
98 static void print_query_fileowner(const char *filename
, alpm_pkg_t
*info
)
101 printf(_("%s is owned by %s %s\n"), filename
,
102 alpm_pkg_get_name(info
), alpm_pkg_get_version(info
));
104 printf("%s\n", alpm_pkg_get_name(info
));
108 static int query_fileowner(alpm_list_t
*targets
)
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"));
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
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
, "");
135 db_local
= alpm_option_get_localdb(config
->handle
);
137 for(t
= targets
; t
; t
= alpm_list_next(t
)) {
138 char *filename
, *dname
, *rpath
;
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
));
157 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("failed to read file '%s': %s\n"),
158 filename
, strerror(errno
));
165 if(S_ISDIR(buf
.st_mode
)) {
166 pm_fprintf(stderr
, ALPM_LOG_ERROR
,
167 _("cannot determine ownership of directory '%s'\n"), filename
);
173 bname
= mbasename(filename
);
174 dname
= mdirname(filename
);
175 /* for files in '/', there is no directory name to match */
176 if(strcmp(dname
, "") == 0) {
179 rpath
= resolve_path(dname
);
182 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("cannot determine real path for '%s': %s\n"),
183 filename
, strerror(errno
));
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
);
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) {
208 /* for files in '/', there is no directory name to match */
210 print_query_fileowner(filename
, info
);
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
);
225 if(ppath
&& strcmp(ppath
, rpath
) == 0) {
226 print_query_fileowner(filename
, info
);
233 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("No package owns %s\n"), filename
);
243 /* search the local database for a matching package */
244 static int query_search(alpm_list_t
*targets
)
246 alpm_list_t
*i
, *searchlist
;
248 alpm_db_t
*db_local
= alpm_option_get_localdb(config
->handle
);
250 /* if we have a targets list, search for packages matching it */
252 searchlist
= alpm_db_search(db_local
, targets
);
255 searchlist
= alpm_db_get_pkgcache(db_local
);
258 if(searchlist
== NULL
) {
262 for(i
= searchlist
; i
; i
= alpm_list_next(i
)) {
264 alpm_pkg_t
*pkg
= alpm_list_getdata(i
);
267 printf("local/%s %s", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
269 printf("%s", alpm_pkg_get_name(pkg
));
274 if((grp
= alpm_pkg_get_groups(pkg
)) != NULL
) {
277 for(k
= grp
; k
; k
= alpm_list_next(k
)) {
278 const char *group
= alpm_list_getdata(k
);
280 if(alpm_list_next(k
)) {
281 /* only print a spacer if there are more groups */
288 /* we need a newline and initial indent first */
290 indentprint(alpm_pkg_get_desc(pkg
), 4);
295 /* we only want to free if the list was a search list */
297 alpm_list_free(searchlist
);
302 static int query_group(alpm_list_t
*targets
)
305 char *grpname
= NULL
;
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
));
320 for(i
= targets
; i
; i
= alpm_list_next(i
)) {
322 grpname
= alpm_list_getdata(i
);
323 grp
= alpm_db_readgroup(db_local
, grpname
);
325 const alpm_list_t
*p
;
326 for(p
= grp
->packages
; p
; p
= alpm_list_next(p
)) {
328 printf("%s %s\n", grpname
,
329 alpm_pkg_get_name(alpm_list_getdata(p
)));
331 printf("%s\n", alpm_pkg_get_name(alpm_list_getdata(p
)));
335 pm_fprintf(stderr
, ALPM_LOG_ERROR
, _("group '%s' was not found\n"), grpname
);
343 static int is_foreign(alpm_pkg_t
*pkg
)
345 const char *pkgname
= alpm_pkg_get_name(pkg
);
347 alpm_list_t
*sync_dbs
= alpm_option_get_syncdbs(config
->handle
);
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
);
364 static int is_unrequired(alpm_pkg_t
*pkg
)
366 alpm_list_t
*requiredby
= alpm_pkg_compute_requiredby(pkg
);
367 if(requiredby
== NULL
) {
370 FREELIST(requiredby
);
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
) {
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
) {
386 /* check if this pkg isn't in a sync DB */
387 if(config
->op_q_foreign
&& !is_foreign(pkg
)) {
390 /* check if this pkg is unrequired */
391 if(config
->op_q_unrequired
&& !is_unrequired(pkg
)) {
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
)) {
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;
410 alpm_filelist_t
*filelist
;
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
, "");
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
;
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
);
433 strcpy(f
+ rootlen
, path
);
435 /* use lstat to prevent errors from symlinks */
436 if(lstat(f
, &st
) != 0) {
438 printf("%s %s\n", pkgname
, f
);
440 pm_printf(ALPM_LOG_WARNING
, "%s: %s (%s)\n",
441 pkgname
, f
, strerror(errno
));
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
)
461 if(config
->op_q_info
) {
462 if(config
->op_q_isfile
) {
463 dump_pkg_full(pkg
, 0);
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
) {
477 if(!config
->op_q_info
&& !config
->op_q_list
478 && !config
->op_q_changelog
&& !config
->op_q_check
) {
480 printf("%s %s\n", alpm_pkg_get_name(pkg
), alpm_pkg_get_version(pkg
));
482 printf("%s\n", alpm_pkg_get_name(pkg
));
488 int pacman_query(alpm_list_t
*targets
)
493 alpm_pkg_t
*pkg
= NULL
;
496 /* First: operations that do not require targets */
498 /* search for a package */
499 if(config
->op_q_search
) {
500 ret
= query_search(targets
);
504 /* looking for groups */
506 ret
= query_group(targets
);
510 if(config
->op_q_foreign
|| config
->op_q_upgrade
) {
511 if(check_syncdbs(1, 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"));
527 for(i
= alpm_db_get_pkgcache(db_local
); i
; i
= alpm_list_next(i
)) {
528 pkg
= alpm_list_getdata(i
);
530 int value
= display(pkg
);
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
);
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
);
559 pkg
= alpm_db_get_pkg(db_local
, strname
);
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
);
569 pm_fprintf(stderr
, ALPM_LOG_ERROR
,
570 _("could not load package '%s': %s\n"), strname
,
571 alpm_strerror(alpm_errno(config
->handle
)));
579 int value
= display(pkg
);
586 if(config
->op_q_isfile
) {
599 /* vim: set ts=2 sw=2 noet: */