Add new alpm_list_remove_item() function
[pacman-ng.git] / src / util / pactree.c
blob947ed61c8cef1a812e510d9127cdf6d49863beec
1 /*
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/>.
20 #include "config.h"
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <string.h>
26 #include <alpm.h>
27 #include <alpm_list.h>
29 /* output */
30 char *provides = " provides";
31 char *unresolvable = " [unresolvable]";
32 char *branch_tip1 = "|--";
33 char *branch_tip2 = "+--";
34 int indent_size = 3;
36 /* color */
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";
43 /* globals */
44 pmdb_t *db_local;
45 alpm_list_t *walked = NULL;
46 alpm_list_t *provisions = NULL;
48 /* options */
49 int color = 0;
50 int graphviz = 0;
51 int linear = 0;
52 int max_depth = -1;
53 int reverse = 0;
54 int unique = 0;
55 char *dbpath = NULL;
57 static int alpm_local_init(void)
59 int ret;
61 ret = alpm_initialize();
62 if(ret != 0) {
63 return(ret);
66 ret = alpm_option_set_root(ROOTDIR);
67 if(ret != 0) {
68 return(ret);
71 if(dbpath) {
72 ret = alpm_option_set_dbpath(dbpath);
73 } else {
74 ret = alpm_option_set_dbpath(DBPATH);
76 if(ret != 0) {
77 return(ret);
80 db_local = alpm_option_get_localdb();
81 if(!db_local) {
82 return(1);
85 return(0);
88 static int parse_options(int argc, char *argv[])
90 int opt, option_index = 0;
91 char *endptr = NULL;
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'},
102 {0, 0, 0, 0}
105 while((opt = getopt_long(argc, argv, "b:cd:ghlru", opts, &option_index))) {
106 if(opt < 0) {
107 break;
110 switch(opt) {
111 case 'b':
112 dbpath = strdup(optarg);
113 break;
114 case 'c':
115 color = 1;
116 break;
117 case 'd':
118 /* validate depth */
119 max_depth = (int)strtol(optarg, &endptr, 10);
120 if(*endptr != '\0') {
121 fprintf(stderr, "error: invalid depth -- %s\n", optarg);
122 return 1;
124 break;
125 case 'g':
126 graphviz = 1;
127 break;
128 case 'l':
129 linear = 1;
130 break;
131 case 'r':
132 reverse = 1;
133 break;
134 case 'u':
135 unique = linear = 1;
136 break;
137 case 'h':
138 case '?':
139 default:
140 return(1);
144 if(!argv[optind]) {
145 return(1);
148 if(!color) {
149 branch1_color = "";
150 branch2_color = "";
151 leaf1_color = "";
152 leaf2_color = "";
153 color_off = "";
155 if(linear) {
156 provides = "";
157 branch_tip1 = "";
158 branch_tip2 = "";
159 indent_size = 0;
162 return(0);
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)
181 if(dbpath) {
182 free(dbpath);
185 alpm_list_free(walked);
186 alpm_list_free(provisions);
187 alpm_release();
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 */
197 return;
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,
208 color_off);
209 } else {
210 /* pkg is a normal package */
211 printf("%s%*s%s%s%s\n", branch1_color, indent_sz, branch_tip1, leaf1_color,
212 pkg, color_off);
216 static void print_graph(const char *parentname, const char *pkgname, const char *depname)
218 if(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);
224 } else if(pkgname) {
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)
232 if(graphviz) {
233 print_graph(parentname, pkgname, depname);
234 } else {
235 print_text(pkgname, depname, depth);
239 static void print_start(const char *pkgname, const char *provname)
241 if(graphviz) {
242 printf("digraph G { START [color=red, style=filled];\n"
243 "node [style=filled, color=green];\n"
244 " \"START\" -> \"%s\";\n", pkgname);
245 } else {
246 print_text(pkgname, provname, 0);
250 static void print_end(void)
252 if(graphviz) {
253 /* close graph output */
254 printf("}\n");
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)) {
267 return;
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 */
279 if(!unique) {
280 print(alpm_pkg_get_name(pkg), pkgname, NULL, depth);
282 } else {
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)
296 alpm_list_t *i;
298 if((max_depth >= 0) && (depth == max_depth + 1)) {
299 return;
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));
309 if(provider) {
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 */
315 if(!unique) {
316 print(alpm_pkg_get_name(pkg), provname, alpm_dep_get_name(depend), depth);
318 } else {
319 print(alpm_pkg_get_name(pkg), provname, alpm_dep_get_name(depend), depth);
320 walk_deps(provider, depth + 1);
322 } else {
323 /* unresolvable package */
324 print(alpm_pkg_get_name(pkg), NULL, alpm_dep_get_name(depend), depth);
329 int main(int argc, char *argv[])
331 int ret;
332 const char *target_name;
333 pmpkg_t *pkg;
335 ret = parse_options(argc, argv);
336 if(ret != 0) {
337 usage();
338 goto finish;
341 ret = alpm_local_init();
342 if(ret != 0) {
343 fprintf(stderr, "error: cannot initialize alpm: %s\n", alpm_strerrorlast());
344 goto finish;
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);
351 if(!pkg) {
352 fprintf(stderr, "error: package '%s' not found\n", target_name);
353 ret = 1;
354 goto finish;
357 print_start(alpm_pkg_get_name(pkg), target_name);
359 if(reverse) {
360 walk_reverse_deps(pkg, 1);
361 } else {
362 walk_deps(pkg, 1);
365 print_end();
367 finish:
368 cleanup();
369 return(ret);
372 /* vim: set ts=2 sw=2 noet: */