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/>.
26 #include <alpm_list.h>
30 typedef struct tdepth
{
45 static struct graph_style graph_default
= {
53 static struct graph_style graph_linear
= {
62 struct color_choices
{
70 static struct color_choices use_color
= {
71 "\033[0;33m", /* yellow */
72 "\033[0;37m", /* white */
73 "\033[1;32m", /* bold green */
74 "\033[0;32m", /* green */
78 static struct color_choices no_color
= {
92 alpm_handle_t
*handle
= NULL
;
93 alpm_list_t
*walked
= NULL
;
94 alpm_list_t
*provisions
= NULL
;
97 struct color_choices
*color
= &no_color
;
98 struct graph_style
*style
= &graph_default
;
104 const char *dbpath
= DBPATH
;
105 const char *configfile
= CONFFILE
;
108 /* A quick and dirty implementation derived from glibc */
109 static size_t strnlen(const char *s
, size_t max
)
111 register const char *p
;
112 for(p
= s
; *p
&& max
--; ++p
);
116 char *strndup(const char *s
, size_t n
)
118 size_t len
= strnlen(s
, n
);
119 char *new = (char *) malloc(len
+ 1);
125 return (char *)memcpy(new, s
, len
);
129 static size_t strtrim(char *str
)
131 char *end
, *pch
= str
;
133 if(str
== NULL
|| *str
== '\0') {
134 /* string is empty, so we're done. */
138 while(isspace((unsigned char)*pch
)) {
142 size_t len
= strlen(pch
);
144 memmove(str
, pch
, len
+ 1);
150 /* check if there wasn't anything but whitespace in the string. */
155 end
= (str
+ strlen(str
) - 1);
156 while(isspace((unsigned char)*end
)) {
164 static int register_syncs(void) {
166 char *section
= NULL
;
168 const alpm_siglevel_t level
= ALPM_SIG_DATABASE
| ALPM_SIG_DATABASE_OPTIONAL
;
170 fp
= fopen(configfile
, "r");
172 fprintf(stderr
, "error: config file %s could not be read\n", configfile
);
176 while(fgets(line
, LINE_MAX
, fp
)) {
180 /* ignore whole line and end of line comments */
181 if((ptr
= strchr(line
, '#'))) {
185 linelen
= strtrim(line
);
191 if(line
[0] == '[' && line
[linelen
- 1] == ']') {
193 section
= strndup(&line
[1], linelen
- 2);
195 if(section
&& strcmp(section
, "options") != 0) {
196 alpm_register_syncdb(handle
, section
, level
);
207 static int parse_options(int argc
, char *argv
[])
209 int opt
, option_index
= 0;
212 static const struct option opts
[] = {
213 {"dbpath", required_argument
, 0, 'b'},
214 {"color", no_argument
, 0, 'c'},
215 {"depth", required_argument
, 0, 'd'},
216 {"graph", no_argument
, 0, 'g'},
217 {"help", no_argument
, 0, 'h'},
218 {"linear", no_argument
, 0, 'l'},
219 {"reverse", no_argument
, 0, 'r'},
220 {"sync", no_argument
, 0, 'S'},
221 {"unique", no_argument
, 0, 'u'},
223 {"config", required_argument
, 0, OP_CONFIG
},
227 while((opt
= getopt_long(argc
, argv
, "b:cd:ghlrsu", opts
, &option_index
))) {
244 max_depth
= (int)strtol(optarg
, &endptr
, 10);
245 if(*endptr
!= '\0') {
246 fprintf(stderr
, "error: invalid depth -- %s\n", optarg
);
254 style
= &graph_linear
;
264 style
= &graph_linear
;
280 static void usage(void)
282 fprintf(stderr
, "pactree v" PACKAGE_VERSION
"\n"
283 "Usage: pactree [options] PACKAGE\n\n"
284 " -b, --dbpath <path> set an alternate database location\n"
285 " -c, --color colorize output\n"
286 " -d, --depth <#> limit the depth of recursion\n"
287 " -g, --graph generate output for graphviz's dot\n"
288 " -h, --help display this help message\n"
289 " -l, --linear enable linear output\n"
290 " -r, --reverse show reverse dependencies\n"
291 " -s, --sync search sync DBs instead of local\n"
292 " -u, --unique show dependencies with no duplicates (implies -l)\n"
293 " --config <path> set an alternate configuration file\n");
296 static void cleanup(void)
298 alpm_list_free(walked
);
299 alpm_list_free(provisions
);
300 alpm_release(handle
);
303 /* pkg provides provision */
304 static void print_text(const char *pkg
, const char *provision
, tdepth
*depth
)
306 if(!pkg
&& !provision
) {
307 /* not much we can do */
315 printf("%s", color
->branch1
);
317 printf("%*s%-*s", style
->indent
* (depth
->level
- level
), "",
318 style
->indent
, style
->limb
);
319 level
= depth
->level
+ 1;
322 printf("%*s", style
->indent
* (depth
->level
- level
), "");
325 if(!pkg
&& provision
) {
326 printf("%s%s%s%s [unresolvable]%s\n", style
->tip1
, color
->leaf1
,
327 provision
, color
->branch1
, color
->off
);
328 } else if(provision
&& strcmp(pkg
, provision
) != 0) {
329 printf("%s%s%s%s%s %s%s%s\n", style
->tip2
, color
->leaf1
, pkg
,
330 color
->leaf2
, style
->provides
, color
->leaf1
, provision
,
333 printf("%s%s%s%s\n", style
->tip1
, color
->leaf1
, pkg
, color
->off
);
337 static void print_graph(const char *parentname
, const char *pkgname
, const char *depname
)
340 printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname
, depname
);
341 if(pkgname
&& strcmp(depname
, pkgname
) != 0 && !alpm_list_find_str(provisions
, depname
)) {
342 printf("\"%s\" -> \"%s\" [arrowhead=none, color=grey];\n", depname
, pkgname
);
343 provisions
= alpm_list_add(provisions
, (char *)depname
);
346 printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname
, pkgname
);
350 /* parent depends on dep which is satisfied by pkg */
351 static void print(const char *parentname
, const char *pkgname
, const char *depname
, tdepth
*depth
)
354 print_graph(parentname
, pkgname
, depname
);
356 print_text(pkgname
, depname
, depth
);
360 static void print_start(const char *pkgname
, const char *provname
)
363 printf("digraph G { START [color=red, style=filled];\n"
364 "node [style=filled, color=green];\n"
365 " \"START\" -> \"%s\";\n", pkgname
);
372 print_text(pkgname
, provname
, &d
);
376 static void print_end(void)
379 /* close graph output */
384 static alpm_list_t
*get_pkg_dep_names(alpm_pkg_t
*pkg
)
386 alpm_list_t
*i
, *names
= NULL
;
387 for(i
= alpm_pkg_get_depends(pkg
); i
; i
= alpm_list_next(i
)) {
388 alpm_depend_t
*d
= i
->data
;
389 names
= alpm_list_add(names
, d
->name
);
395 * walk dependencies, showing dependencies of the target
397 static void walk_deps(alpm_list_t
*dblist
, alpm_pkg_t
*pkg
, tdepth
*depth
, int rev
)
399 alpm_list_t
*deps
, *i
;
401 if(!pkg
|| ((max_depth
>= 0) && (depth
->level
> max_depth
))) {
405 walked
= alpm_list_add(walked
, (void *)alpm_pkg_get_name(pkg
));
408 deps
= alpm_pkg_compute_requiredby(pkg
);
410 deps
= get_pkg_dep_names(pkg
);
413 for(i
= deps
; i
; i
= alpm_list_next(i
)) {
414 const char *pkgname
= i
->data
;
416 alpm_pkg_t
*dep_pkg
= alpm_find_dbs_satisfier(handle
, dblist
, pkgname
);
418 if(alpm_list_find_str(walked
, dep_pkg
? alpm_pkg_get_name(dep_pkg
) : pkgname
)) {
419 /* if we've already seen this package, don't print in "unique" output
420 * and don't recurse */
422 print(alpm_pkg_get_name(pkg
), alpm_pkg_get_name(dep_pkg
), pkgname
, depth
);
425 print(alpm_pkg_get_name(pkg
), alpm_pkg_get_name(dep_pkg
), pkgname
, depth
);
433 /* last dep, cut off the limb here */
434 if(!alpm_list_next(i
)){
436 depth
->prev
->next
= &d
;
437 d
.prev
= depth
->prev
;
443 walk_deps(dblist
, dep_pkg
, &d
, rev
);
454 int main(int argc
, char *argv
[])
456 int freelist
= 0, ret
= 0;
458 const char *target_name
;
460 alpm_list_t
*dblist
= NULL
;
462 if(parse_options(argc
, argv
) != 0) {
468 handle
= alpm_initialize(ROOTDIR
, dbpath
, &err
);
470 fprintf(stderr
, "error: cannot initialize alpm: %s\n",
477 if(register_syncs() != 0) {
481 dblist
= alpm_get_syncdbs(handle
);
483 dblist
= alpm_list_add(dblist
, alpm_get_localdb(handle
));
487 /* we only care about the first non option arg for walking */
488 target_name
= argv
[optind
];
490 pkg
= alpm_find_dbs_satisfier(handle
, dblist
, target_name
);
492 fprintf(stderr
, "error: package '%s' not found\n", target_name
);
497 print_start(alpm_pkg_get_name(pkg
), target_name
);
504 walk_deps(dblist
, pkg
, &d
, reverse
);
509 alpm_list_free(dblist
);
517 /* vim: set ts=2 sw=2 noet: */