makepkg: remove VCS package support
[pacman-ng.git] / src / util / pactree.c
blob4488645fb22a52ae383c69bdeeee5c047beab17e
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 <ctype.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <string.h>
25 #include <alpm.h>
26 #include <alpm_list.h>
28 #define LINE_MAX 512
30 typedef struct tdepth {
31 struct tdepth *prev;
32 struct tdepth *next;
33 int level;
34 } tdepth;
36 /* output */
37 struct graph_style {
38 const char *provides;
39 const char *tip1;
40 const char *tip2;
41 const char *limb;
42 int indent;
45 static struct graph_style graph_default = {
46 " provides",
47 "|--",
48 "+--",
49 "|",
53 static struct graph_style graph_linear = {
54 "",
55 "",
56 "",
57 "",
61 /* color choices */
62 struct color_choices {
63 const char *branch1;
64 const char *branch2;
65 const char *leaf1;
66 const char *leaf2;
67 const char *off;
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 */
75 "\033[0m"
78 static struct color_choices no_color = {
79 "",
80 "",
81 "",
82 "",
86 /* long operations */
87 enum {
88 OP_CONFIG = 1000
91 /* globals */
92 alpm_handle_t *handle = NULL;
93 alpm_list_t *walked = NULL;
94 alpm_list_t *provisions = NULL;
96 /* options */
97 struct color_choices *color = &no_color;
98 struct graph_style *style = &graph_default;
99 int graphviz = 0;
100 int max_depth = -1;
101 int reverse = 0;
102 int unique = 0;
103 int searchsyncs = 0;
104 const char *dbpath = DBPATH;
105 const char *configfile = CONFFILE;
107 #ifndef HAVE_STRNDUP
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);
113 return (p - s);
116 char *strndup(const char *s, size_t n)
118 size_t len = strnlen(s, n);
119 char *new = (char *) malloc(len + 1);
121 if(new == NULL)
122 return NULL;
124 new[len] = '\0';
125 return (char *)memcpy(new, s, len);
127 #endif
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. */
135 return 0;
138 while(isspace((unsigned char)*pch)) {
139 pch++;
141 if(pch != str) {
142 size_t len = strlen(pch);
143 if(len) {
144 memmove(str, pch, len + 1);
145 } else {
146 *str = '\0';
150 /* check if there wasn't anything but whitespace in the string. */
151 if(*str == '\0') {
152 return 0;
155 end = (str + strlen(str) - 1);
156 while(isspace((unsigned char)*end)) {
157 end--;
159 *++end = '\0';
161 return end - pch;
164 static int register_syncs(void) {
165 FILE *fp;
166 char *section = NULL;
167 char line[LINE_MAX];
168 const alpm_siglevel_t level = ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
170 fp = fopen(configfile, "r");
171 if(!fp) {
172 fprintf(stderr, "error: config file %s could not be read\n", configfile);
173 return 1;
176 while(fgets(line, LINE_MAX, fp)) {
177 size_t linelen;
178 char *ptr;
180 /* ignore whole line and end of line comments */
181 if((ptr = strchr(line, '#'))) {
182 *ptr = '\0';
185 linelen = strtrim(line);
187 if(linelen == 0) {
188 continue;
191 if(line[0] == '[' && line[linelen - 1] == ']') {
192 free(section);
193 section = strndup(&line[1], linelen - 2);
195 if(section && strcmp(section, "options") != 0) {
196 alpm_register_syncdb(handle, section, level);
201 free(section);
202 fclose(fp);
204 return 0;
207 static int parse_options(int argc, char *argv[])
209 int opt, option_index = 0;
210 char *endptr = NULL;
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},
224 {0, 0, 0, 0}
227 while((opt = getopt_long(argc, argv, "b:cd:ghlrsu", opts, &option_index))) {
228 if(opt < 0) {
229 break;
232 switch(opt) {
233 case OP_CONFIG:
234 configfile = optarg;
235 break;
236 case 'b':
237 dbpath = optarg;
238 break;
239 case 'c':
240 color = &use_color;
241 break;
242 case 'd':
243 /* validate depth */
244 max_depth = (int)strtol(optarg, &endptr, 10);
245 if(*endptr != '\0') {
246 fprintf(stderr, "error: invalid depth -- %s\n", optarg);
247 return 1;
249 break;
250 case 'g':
251 graphviz = 1;
252 break;
253 case 'l':
254 style = &graph_linear;
255 break;
256 case 'r':
257 reverse = 1;
258 break;
259 case 's':
260 searchsyncs = 1;
261 break;
262 case 'u':
263 unique = 1;
264 style = &graph_linear;
265 break;
266 case 'h':
267 case '?':
268 default:
269 return 1;
273 if(!argv[optind]) {
274 return 1;
277 return 0;
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 */
308 return;
311 /* print limbs */
312 while(depth->prev)
313 depth = depth->prev;
314 int level = 0;
315 printf("%s", color->branch1);
316 while(depth->next){
317 printf("%*s%-*s", style->indent * (depth->level - level), "",
318 style->indent, style->limb);
319 level = depth->level + 1;
320 depth = depth->next;
322 printf("%*s", style->indent * (depth->level - level), "");
324 /* print tip */
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,
331 color->off);
332 } else {
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)
339 if(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);
345 } else if(pkgname) {
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)
353 if(graphviz) {
354 print_graph(parentname, pkgname, depname);
355 } else {
356 print_text(pkgname, depname, depth);
360 static void print_start(const char *pkgname, const char *provname)
362 if(graphviz) {
363 printf("digraph G { START [color=red, style=filled];\n"
364 "node [style=filled, color=green];\n"
365 " \"START\" -> \"%s\";\n", pkgname);
366 } else {
367 tdepth d = {
368 NULL,
369 NULL,
372 print_text(pkgname, provname, &d);
376 static void print_end(void)
378 if(graphviz) {
379 /* close graph output */
380 printf("}\n");
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);
391 return names;
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))) {
402 return;
405 walked = alpm_list_add(walked, (void *)alpm_pkg_get_name(pkg));
407 if(rev) {
408 deps = alpm_pkg_compute_requiredby(pkg);
409 } else {
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 */
421 if(!unique) {
422 print(alpm_pkg_get_name(pkg), alpm_pkg_get_name(dep_pkg), pkgname, depth);
424 } else {
425 print(alpm_pkg_get_name(pkg), alpm_pkg_get_name(dep_pkg), pkgname, depth);
426 if(dep_pkg) {
427 tdepth d = {
428 depth,
429 NULL,
430 depth->level + 1
432 depth->next = &d;
433 /* last dep, cut off the limb here */
434 if(!alpm_list_next(i)){
435 if(depth->prev){
436 depth->prev->next = &d;
437 d.prev = depth->prev;
438 depth = &d;
439 } else {
440 d.prev = NULL;
443 walk_deps(dblist, dep_pkg, &d, rev);
444 depth->next = NULL;
449 if(rev) {
450 FREELIST(deps);
454 int main(int argc, char *argv[])
456 int freelist = 0, ret = 0;
457 alpm_errno_t err;
458 const char *target_name;
459 alpm_pkg_t *pkg;
460 alpm_list_t *dblist = NULL;
462 if(parse_options(argc, argv) != 0) {
463 usage();
464 ret = 1;
465 goto finish;
468 handle = alpm_initialize(ROOTDIR, dbpath, &err);
469 if(!handle) {
470 fprintf(stderr, "error: cannot initialize alpm: %s\n",
471 alpm_strerror(err));
472 ret = 1;
473 goto finish;
476 if(searchsyncs) {
477 if(register_syncs() != 0) {
478 ret = 1;
479 goto finish;
481 dblist = alpm_get_syncdbs(handle);
482 } else {
483 dblist = alpm_list_add(dblist, alpm_get_localdb(handle));
484 freelist = 1;
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);
491 if(!pkg) {
492 fprintf(stderr, "error: package '%s' not found\n", target_name);
493 ret = 1;
494 goto finish;
497 print_start(alpm_pkg_get_name(pkg), target_name);
499 tdepth d = {
500 NULL,
501 NULL,
504 walk_deps(dblist, pkg, &d, reverse);
506 print_end();
508 if(freelist) {
509 alpm_list_free(dblist);
512 finish:
513 cleanup();
514 return ret;
517 /* vim: set ts=2 sw=2 noet: */