2 * pactree.c - a simple dependency tree viewer
4 * Copyright (c) 2010-2011 Pacman Development Team <pacman-dev@archlinux.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include <alpm_list.h>
30 char *provides
= " provides";
31 char *unresolvable
= " [unresolvable]";
32 char *branch_tip1
= "|--";
33 char *branch_tip2
= "+--";
37 char *branch1_color
= "\033[0;33m"; /* yellow */
38 char *branch2_color
= "\033[0;37m"; /* white */
39 char *leaf1_color
= "\033[1;32m"; /* bold green */
40 char *leaf2_color
= "\033[0;32m"; /* green */
41 char *color_off
= "\033[0m";
45 alpm_list_t
*walked
= NULL
;
46 alpm_list_t
*provisions
= NULL
;
57 static int alpm_local_init(void)
61 ret
= alpm_initialize();
66 ret
= alpm_option_set_root(ROOTDIR
);
72 ret
= alpm_option_set_dbpath(dbpath
);
74 ret
= alpm_option_set_dbpath(DBPATH
);
80 db_local
= alpm_option_get_localdb();
88 static int parse_options(int argc
, char *argv
[])
90 int opt
, option_index
= 0;
93 static struct option opts
[] = {
94 {"dbpath", required_argument
, 0, 'b'},
95 {"color", no_argument
, 0, 'c'},
96 {"depth", required_argument
, 0, 'd'},
97 {"graph", no_argument
, 0, 'g'},
98 {"help", no_argument
, 0, 'h'},
99 {"linear", no_argument
, 0, 'l'},
100 {"reverse", no_argument
, 0, 'r'},
101 {"unique", no_argument
, 0, 'u'},
105 while((opt
= getopt_long(argc
, argv
, "b:cd:ghlru", opts
, &option_index
))) {
112 dbpath
= strdup(optarg
);
119 max_depth
= (int)strtol(optarg
, &endptr
, 10);
120 if(*endptr
!= '\0') {
121 fprintf(stderr
, "error: invalid depth -- %s\n", optarg
);
165 static void usage(void)
167 fprintf(stderr
, "pactree v" PACKAGE_VERSION
"\n"
168 "Usage: pactree [options] PACKAGE\n\n"
169 " -b, --dbpath <path> set an alternate database location\n"
170 " -c, --color colorize output\n"
171 " -d, --depth <#> limit the depth of recursion\n"
172 " -g, --graph generate output for graphviz's dot\n"
173 " -l, --linear enable linear output\n"
174 " -r, --reverse show reverse dependencies\n"
175 " -u, --unique show dependencies with no duplicates (implies -l)\n\n"
176 " -h, --help display this help message\n");
179 static void cleanup(void)
185 alpm_list_free(walked
);
186 alpm_list_free(provisions
);
190 /* pkg provides provision */
191 static void print_text(const char *pkg
, const char *provision
, int depth
)
193 int indent_sz
= (depth
+ 1) * indent_size
;
195 if(!pkg
&& !provision
) {
196 /* not much we can do */
200 if(!pkg
&& provision
) {
201 /* we failed to resolve provision */
202 printf("%s%*s%s%s%s%s%s\n", branch1_color
, indent_sz
, branch_tip1
,
203 leaf1_color
, provision
, branch1_color
, unresolvable
, color_off
);
204 } else if(provision
&& strcmp(pkg
, provision
) != 0) {
205 /* pkg provides provision */
206 printf("%s%*s%s%s%s%s %s%s%s\n", branch2_color
, indent_sz
, branch_tip2
,
207 leaf1_color
, pkg
, leaf2_color
, provides
, leaf1_color
, provision
,
210 /* pkg is a normal package */
211 printf("%s%*s%s%s%s\n", branch1_color
, indent_sz
, branch_tip1
, leaf1_color
,
216 static void print_graph(const char *parentname
, const char *pkgname
, const char *depname
)
219 printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname
, depname
);
220 if(pkgname
&& strcmp(depname
, pkgname
) != 0 && !alpm_list_find_str(provisions
, depname
)) {
221 printf("\"%s\" -> \"%s\" [arrowhead=none, color=grey];\n", depname
, pkgname
);
222 provisions
= alpm_list_add(provisions
, (char *)depname
);
225 printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname
, pkgname
);
229 /* parent depends on dep which is satisfied by pkg */
230 static void print(const char *parentname
, const char *pkgname
, const char *depname
, int depth
)
233 print_graph(parentname
, pkgname
, depname
);
235 print_text(pkgname
, depname
, depth
);
239 static void print_start(const char *pkgname
, const char *provname
)
242 printf("digraph G { START [color=red, style=filled];\n"
243 "node [style=filled, color=green];\n"
244 " \"START\" -> \"%s\";\n", pkgname
);
246 print_text(pkgname
, provname
, 0);
250 static void print_end(void)
253 /* close graph output */
260 * walk dependencies in reverse, showing packages which require the target
262 static void walk_reverse_deps(pmpkg_t
*pkg
, int depth
)
264 alpm_list_t
*required_by
, *i
;
266 if((max_depth
>= 0) && (depth
== max_depth
+ 1)) {
270 walked
= alpm_list_add(walked
, (void*)alpm_pkg_get_name(pkg
));
271 required_by
= alpm_pkg_compute_requiredby(pkg
);
273 for(i
= required_by
; i
; i
= alpm_list_next(i
)) {
274 const char *pkgname
= alpm_list_getdata(i
);
276 if(alpm_list_find_str(walked
, pkgname
)) {
277 /* if we've already seen this package, don't print in "unique" output
278 * and don't recurse */
280 print(alpm_pkg_get_name(pkg
), pkgname
, NULL
, depth
);
283 print(alpm_pkg_get_name(pkg
), pkgname
, NULL
, depth
);
284 walk_reverse_deps(alpm_db_get_pkg(db_local
, pkgname
), depth
+ 1);
288 FREELIST(required_by
);
292 * walk dependencies, showing dependencies of the target
294 static void walk_deps(pmpkg_t
*pkg
, int depth
)
298 if((max_depth
>= 0) && (depth
== max_depth
+ 1)) {
302 walked
= alpm_list_add(walked
, (void*)alpm_pkg_get_name(pkg
));
304 for(i
= alpm_pkg_get_depends(pkg
); i
; i
= alpm_list_next(i
)) {
305 pmdepend_t
*depend
= alpm_list_getdata(i
);
306 pmpkg_t
*provider
= alpm_find_satisfier(alpm_db_get_pkgcache_list(db_local
),
307 alpm_dep_get_name(depend
));
310 const char *provname
= alpm_pkg_get_name(provider
);
312 if(alpm_list_find_str(walked
, provname
)) {
313 /* if we've already seen this package, don't print in "unique" output
314 * and don't recurse */
316 print(alpm_pkg_get_name(pkg
), provname
, alpm_dep_get_name(depend
), depth
);
319 print(alpm_pkg_get_name(pkg
), provname
, alpm_dep_get_name(depend
), depth
);
320 walk_deps(provider
, depth
+ 1);
323 /* unresolvable package */
324 print(alpm_pkg_get_name(pkg
), NULL
, alpm_dep_get_name(depend
), depth
);
329 int main(int argc
, char *argv
[])
332 const char *target_name
;
335 ret
= parse_options(argc
, argv
);
341 ret
= alpm_local_init();
343 fprintf(stderr
, "error: cannot initialize alpm: %s\n", alpm_strerrorlast());
347 /* we only care about the first non option arg for walking */
348 target_name
= argv
[optind
];
350 pkg
= alpm_find_satisfier(alpm_db_get_pkgcache_list(db_local
), target_name
);
352 fprintf(stderr
, "error: package '%s' not found\n", target_name
);
357 print_start(alpm_pkg_get_name(pkg
), target_name
);
360 walk_reverse_deps(pkg
, 1);
372 /* vim: set ts=2 sw=2 noet: */