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>
39 static struct graph_style graph_default
= {
46 static struct graph_style graph_linear
= {
54 struct color_choices
{
62 static struct color_choices use_color
= {
63 "\033[0;33m", /* yellow */
64 "\033[0;37m", /* white */
65 "\033[1;32m", /* bold green */
66 "\033[0;32m", /* green */
70 static struct color_choices no_color
= {
79 alpm_handle_t
*handle
= NULL
;
80 alpm_list_t
*walked
= NULL
;
81 alpm_list_t
*provisions
= NULL
;
84 struct color_choices
*color
= &no_color
;
85 struct graph_style
*style
= &graph_default
;
91 const char *dbpath
= DBPATH
;
93 static char *strtrim(char *str
)
97 if(str
== NULL
|| *str
== '\0') {
98 /* string is empty, so we're done. */
102 while(isspace((unsigned char)*pch
)) {
106 memmove(str
, pch
, (strlen(pch
) + 1));
109 /* check if there wasn't anything but whitespace in the string. */
114 pch
= (str
+ (strlen(str
) - 1));
115 while(isspace((unsigned char)*pch
)) {
123 static int register_syncs(void) {
125 char *ptr
, *section
= NULL
;
128 fp
= fopen(CONFFILE
, "r");
133 while(fgets(line
, LINE_MAX
, fp
)) {
136 if(line
[0] == '#' || !strlen(line
)) {
140 if((ptr
= strchr(line
, '#'))) {
145 if(line
[0] == '[' && line
[strlen(line
) - 1] == ']') {
147 section
= strndup(&line
[1], strlen(line
) - 2);
149 if(section
&& strcmp(section
, "options") != 0) {
150 alpm_db_register_sync(handle
, section
, PM_PGP_VERIFY_OPTIONAL
);
161 static int parse_options(int argc
, char *argv
[])
163 int opt
, option_index
= 0;
166 static struct option opts
[] = {
167 {"dbpath", required_argument
, 0, 'b'},
168 {"color", no_argument
, 0, 'c'},
169 {"depth", required_argument
, 0, 'd'},
170 {"graph", no_argument
, 0, 'g'},
171 {"help", no_argument
, 0, 'h'},
172 {"linear", no_argument
, 0, 'l'},
173 {"reverse", no_argument
, 0, 'r'},
174 {"sync", no_argument
, 0, 'S'},
175 {"unique", no_argument
, 0, 'u'},
179 while((opt
= getopt_long(argc
, argv
, "b:cd:ghlrsu", opts
, &option_index
))) {
193 max_depth
= (int)strtol(optarg
, &endptr
, 10);
194 if(*endptr
!= '\0') {
195 fprintf(stderr
, "error: invalid depth -- %s\n", optarg
);
203 style
= &graph_linear
;
213 style
= &graph_linear
;
229 static void usage(void)
231 fprintf(stderr
, "pactree v" PACKAGE_VERSION
"\n"
232 "Usage: pactree [options] PACKAGE\n\n"
233 " -b, --dbpath <path> set an alternate database location\n"
234 " -c, --color colorize output\n"
235 " -d, --depth <#> limit the depth of recursion\n"
236 " -g, --graph generate output for graphviz's dot\n"
237 " -l, --linear enable linear output\n"
238 " -r, --reverse show reverse dependencies\n"
239 " -s, --sync search sync DBs instead of local\n"
240 " -u, --unique show dependencies with no duplicates (implies -l)\n\n"
241 " -h, --help display this help message\n");
244 static void cleanup(void)
246 alpm_list_free(walked
);
247 alpm_list_free(provisions
);
248 alpm_release(handle
);
251 /* pkg provides provision */
252 static void print_text(const char *pkg
, const char *provision
, int depth
)
254 int indent_sz
= (depth
+ 1) * style
->indent
;
256 if(!pkg
&& !provision
) {
257 /* not much we can do */
261 if(!pkg
&& provision
) {
262 /* we failed to resolve provision */
263 printf("%s%*s%s%s%s [unresolvable]%s\n", color
->branch1
, indent_sz
,
264 style
->tip1
, color
->leaf1
, provision
, color
->branch1
, color
->off
);
265 } else if(provision
&& strcmp(pkg
, provision
) != 0) {
266 /* pkg provides provision */
267 printf("%s%*s%s%s%s%s %s%s%s\n", color
->branch2
, indent_sz
, style
->tip2
,
268 color
->leaf1
, pkg
, color
->leaf2
, style
->provides
, color
->leaf1
, provision
,
271 /* pkg is a normal package */
272 printf("%s%*s%s%s%s\n", color
->branch1
, indent_sz
, style
->tip1
, color
->leaf1
,
277 static void print_graph(const char *parentname
, const char *pkgname
, const char *depname
)
280 printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname
, depname
);
281 if(pkgname
&& strcmp(depname
, pkgname
) != 0 && !alpm_list_find_str(provisions
, depname
)) {
282 printf("\"%s\" -> \"%s\" [arrowhead=none, color=grey];\n", depname
, pkgname
);
283 provisions
= alpm_list_add(provisions
, (char *)depname
);
286 printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname
, pkgname
);
290 /* parent depends on dep which is satisfied by pkg */
291 static void print(const char *parentname
, const char *pkgname
, const char *depname
, int depth
)
294 print_graph(parentname
, pkgname
, depname
);
296 print_text(pkgname
, depname
, depth
);
300 static void print_start(const char *pkgname
, const char *provname
)
303 printf("digraph G { START [color=red, style=filled];\n"
304 "node [style=filled, color=green];\n"
305 " \"START\" -> \"%s\";\n", pkgname
);
307 print_text(pkgname
, provname
, 0);
311 static void print_end(void)
314 /* close graph output */
319 static alpm_pkg_t
*get_pkg_from_dbs(alpm_list_t
*dbs
, const char *needle
) {
323 for(i
= dbs
; i
; i
= alpm_list_next(i
)) {
324 ret
= alpm_db_get_pkg(alpm_list_getdata(i
), needle
);
333 * walk dependencies in reverse, showing packages which require the target
335 static void walk_reverse_deps(alpm_list_t
*dblist
, alpm_pkg_t
*pkg
, int depth
)
337 alpm_list_t
*required_by
, *i
;
339 if(!pkg
|| ((max_depth
>= 0) && (depth
== max_depth
+ 1))) {
343 walked
= alpm_list_add(walked
, (void *)alpm_pkg_get_name(pkg
));
344 required_by
= alpm_pkg_compute_requiredby(pkg
);
346 for(i
= required_by
; i
; i
= alpm_list_next(i
)) {
347 const char *pkgname
= alpm_list_getdata(i
);
349 if(alpm_list_find_str(walked
, pkgname
)) {
350 /* if we've already seen this package, don't print in "unique" output
351 * and don't recurse */
353 print(alpm_pkg_get_name(pkg
), pkgname
, NULL
, depth
);
356 print(alpm_pkg_get_name(pkg
), pkgname
, NULL
, depth
);
357 walk_reverse_deps(dblist
, get_pkg_from_dbs(dblist
, pkgname
), depth
+ 1);
361 FREELIST(required_by
);
365 * walk dependencies, showing dependencies of the target
367 static void walk_deps(alpm_list_t
*dblist
, alpm_pkg_t
*pkg
, int depth
)
371 if((max_depth
>= 0) && (depth
== max_depth
+ 1)) {
375 walked
= alpm_list_add(walked
, (void *)alpm_pkg_get_name(pkg
));
377 for(i
= alpm_pkg_get_depends(pkg
); i
; i
= alpm_list_next(i
)) {
378 pmdepend_t
*depend
= alpm_list_getdata(i
);
379 alpm_pkg_t
*provider
= alpm_find_dbs_satisfier(handle
, dblist
, depend
->name
);
382 const char *provname
= alpm_pkg_get_name(provider
);
384 if(alpm_list_find_str(walked
, provname
)) {
385 /* if we've already seen this package, don't print in "unique" output
386 * and don't recurse */
388 print(alpm_pkg_get_name(pkg
), provname
, depend
->name
, depth
);
391 print(alpm_pkg_get_name(pkg
), provname
, depend
->name
, depth
);
392 walk_deps(dblist
, provider
, depth
+ 1);
395 /* unresolvable package */
396 print(alpm_pkg_get_name(pkg
), NULL
, depend
->name
, depth
);
401 int main(int argc
, char *argv
[])
403 int freelist
= 0, ret
= 0;
405 const char *target_name
;
407 alpm_list_t
*dblist
= NULL
;
409 if(parse_options(argc
, argv
) != 0) {
415 handle
= alpm_initialize(ROOTDIR
, dbpath
, &err
);
417 fprintf(stderr
, "error: cannot initialize alpm: %s\n",
424 if(register_syncs() != 0) {
425 fprintf(stderr
, "error: failed to register sync DBs\n");
429 dblist
= alpm_option_get_syncdbs(handle
);
431 dblist
= alpm_list_add(dblist
, alpm_option_get_localdb(handle
));
435 /* we only care about the first non option arg for walking */
436 target_name
= argv
[optind
];
438 pkg
= alpm_find_dbs_satisfier(handle
, dblist
, target_name
);
440 fprintf(stderr
, "error: package '%s' not found\n", target_name
);
445 print_start(alpm_pkg_get_name(pkg
), target_name
);
448 walk_reverse_deps(dblist
, pkg
, 1);
450 walk_deps(dblist
, pkg
, 1);
456 alpm_list_free(dblist
);
464 /* vim: set ts=2 sw=2 noet: */