Graphical package manager for pacman based on pamac 5.x.x
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
7.8KB

  1. /*
  2. * pactree.vala - a simple dependency tree viewer translated in Vala
  3. *
  4. * Copyright (C) 2014-2015 Guillaume Benoit <guillaume@manjaro.org>
  5. * Copyright (c) 2010-2011 Pacman Development Team <pacman-dev@archlinux.org>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. // Compile with: valac --pkg=libalpm --vapidir=../vapi pactree.vala
  21. using Alpm;
  22. /* output */
  23. string provides;
  24. string unresolvable;
  25. string branch_tip1;
  26. string branch_tip2;
  27. int indent_size;
  28. /* color */
  29. string branch1_color;
  30. string branch2_color;
  31. string leaf1_color;
  32. string leaf2_color;
  33. string color_off;
  34. /* globals */
  35. Handle handle;
  36. unowned DB localdb;
  37. Alpm.List<string> walked;
  38. Alpm.List<string> provisions;
  39. /* options */
  40. bool color;
  41. bool graphviz;
  42. bool linear;
  43. int max_depth;
  44. bool reverse;
  45. bool unique;
  46. string dbpath;
  47. const OptionEntry[] options = {
  48. { "dbpath", 'b', 0, OptionArg.STRING, ref dbpath, "set an alternate database location", "path" },
  49. { "color", 'c', 0, OptionArg.NONE, ref color, "colorize output", null },
  50. { "depth", 'd', 0, OptionArg.INT, ref max_depth, "limit the depth of recursion", "number" },
  51. { "graph", 'g', 0, OptionArg.NONE, ref graphviz, "generate output for graphviz", null },
  52. { "linear", 'l', 0, OptionArg.NONE, ref linear, "enable linear output", null },
  53. { "reverse", 'r', 0, OptionArg.NONE, ref reverse, "show reverse dependencies", null },
  54. { "unique", 'u', 0, OptionArg.NONE, ref unique, "show dependencies with no duplicates (implies -l)", null },
  55. { null }
  56. };
  57. static void init_options() {
  58. /* initialize options */
  59. color = false;
  60. graphviz = false;
  61. linear = false;
  62. max_depth = -1;
  63. reverse = false;
  64. unique = false;
  65. dbpath = "/var/lib/pacman";
  66. /* output */
  67. provides = " provides";
  68. unresolvable = " [unresolvable]";
  69. branch_tip1 = "|--";
  70. branch_tip2 = "+--";
  71. indent_size = 3;
  72. /* color */
  73. branch1_color = "\033[0;33m"; /* yellow */
  74. branch2_color = "\033[0;37m"; /* white */
  75. leaf1_color = "\033[1;32m"; /* bold green */
  76. leaf2_color = "\033[0;32m"; /* green */
  77. color_off = "\033[0m";
  78. }
  79. static int parse_options(ref unowned string[] args) {
  80. var opts = new OptionContext("");
  81. opts.set_help_enabled(true);
  82. opts.add_main_entries(options, null);
  83. try {
  84. bool b = opts.parse(ref args);
  85. if (!b) {
  86. stderr.puts(opts.get_help(false, null));
  87. return 1;
  88. }
  89. }
  90. catch (OptionError e)
  91. {
  92. stderr.puts("Unable to parse options : " + e.message + "\n");
  93. return 1;
  94. }
  95. /* there must be (at least) one argument left */
  96. if (args.length == 1) return 1;
  97. /* unique implies linear */
  98. if (unique) linear = true;
  99. /* no color */
  100. if (!color) {
  101. branch1_color = branch2_color = "";
  102. leaf1_color = leaf2_color = "";
  103. color_off = "";
  104. }
  105. /* linear */
  106. if (linear) {
  107. provides = "";
  108. branch_tip1 = branch_tip2 = "";
  109. indent_size = 0;
  110. }
  111. return 0;
  112. }
  113. static void local_init() {
  114. Alpm.Errno error;
  115. handle = new Handle ("/", dbpath, out error);
  116. assert (error == 0);
  117. localdb = handle.localdb;
  118. assert (localdb != null);
  119. }
  120. static int main (string[] args) {
  121. init_options();
  122. int ret = parse_options(ref args);
  123. if (ret != 0) return ret;
  124. local_init();
  125. string? target_name = args[1];
  126. unowned Package? pkg = find_satisfier(localdb.pkgcache, target_name);
  127. if (pkg == null) {
  128. stderr.printf("Error: package '%s' not found\n", target_name);
  129. return 1;
  130. }
  131. /* begin writing */
  132. print_start(pkg.name, target_name);
  133. if(reverse)
  134. walk_reverse_deps(pkg, 1);
  135. else
  136. walk_deps(pkg, 1);
  137. print_end();
  138. return 0;
  139. }
  140. static void print_text(string? pkg, string? provision, int depth)
  141. {
  142. int indent_sz = (depth + 1) * indent_size;
  143. if ((pkg == null) && (provision == null)) return;
  144. if (pkg == null) {
  145. /* we failed to resolve provision */
  146. stdout.printf("%s%*s%s%s%s%s%s\n", branch1_color, indent_sz, branch_tip1,
  147. leaf1_color, provision, branch1_color, unresolvable, color_off);
  148. } else if ((provision != null) && (provision != pkg)) {
  149. /* pkg provides provision */
  150. stdout.printf("%s%*s%s%s%s%s %s%s%s\n", branch2_color, indent_sz, branch_tip2,
  151. leaf1_color, pkg, leaf2_color, provides, leaf1_color, provision,
  152. color_off);
  153. } else {
  154. /* pkg is a normal package */
  155. stdout.printf("%s%*s%s%s%s\n", branch1_color, indent_sz, branch_tip1, leaf1_color,
  156. pkg, color_off);
  157. }
  158. }
  159. /**
  160. * walk dependencies in reverse, showing packages which require the target
  161. */
  162. static void walk_reverse_deps(Package pkg, int depth) {
  163. if((max_depth >= 0) && (depth > max_depth)) return;
  164. walked.add(pkg.name);
  165. Alpm.List<string> required_by = pkg.compute_requiredby();
  166. unowned Alpm.List<string> list = required_by;
  167. while (list != null) {
  168. unowned string pkgname = list.data;
  169. if (walked.find_str(pkgname) != null) {
  170. /* if we've already seen this package, don't print in "unique" output
  171. * and don't recurse */
  172. if (!unique) {
  173. print(pkg.name, pkgname, null, depth);
  174. }
  175. } else {
  176. print(pkg.name, pkgname, null, depth);
  177. walk_reverse_deps(localdb.get_pkg(pkgname), depth + 1);
  178. }
  179. list.next ();
  180. }
  181. required_by.free_inner(GLib.free);
  182. }
  183. /**
  184. * walk dependencies, showing dependencies of the target
  185. */
  186. static void walk_deps(Package pkg, int depth)
  187. {
  188. if((max_depth >= 0) && (depth > max_depth)) return;
  189. walked.add(pkg.name);
  190. unowned Alpm.List<unowned Depend> depends = pkg.depends;
  191. while (depends != null) {
  192. unowned Alpm.Depend depend = depends.data;
  193. unowned string depname = depend.name;
  194. unowned Package? provider = find_satisfier (localdb.pkgcache, depname);
  195. if (provider != null) {
  196. string provname = provider.name;
  197. if (walked.find_str (provname) != null) {
  198. /* if we've already seen this package, don't print in "unique" output
  199. * and don't recurse */
  200. if (!unique) {
  201. print (pkg.name, provname, depname, depth);
  202. }
  203. } else {
  204. print (pkg.name, provname, depname, depth);
  205. walk_deps(provider, depth + 1);
  206. }
  207. } else {
  208. /* unresolvable package */
  209. print(pkg.name, null, depname, depth);
  210. }
  211. depends.next ();
  212. }
  213. }
  214. static void print_graph(string parentname, string? pkgname, string? depname)
  215. {
  216. if(depname != null) {
  217. stdout.printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname, depname);
  218. if((pkgname != null) && (depname != pkgname) && (provisions.find_str(depname) != null)) {
  219. stdout.printf("\"%s\" -> \"%s\" [arrowhead=none, color=grey];\n", depname, pkgname);
  220. provisions.add(depname);
  221. }
  222. } else if(pkgname != null) {
  223. stdout.printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname, pkgname);
  224. }
  225. }
  226. /* parent depends on dep which is satisfied by pkg */
  227. static void print(string? parentname, string? pkgname, string? depname, int depth)
  228. {
  229. if(graphviz) {
  230. print_graph(parentname, pkgname, depname);
  231. } else {
  232. print_text(pkgname, depname, depth);
  233. }
  234. }
  235. static void print_start(string pkgname, string provname)
  236. {
  237. if(graphviz) {
  238. stdout.printf("digraph G { START [color=red, style=filled];\n" +
  239. "node [style=filled, color=green];\n" +
  240. " \"START\" -> \"%s\";\n", pkgname);
  241. } else {
  242. print_text(pkgname, provname, 0);
  243. }
  244. }
  245. static void print_end()
  246. {
  247. if(graphviz) {
  248. /* close graph output */
  249. stdout.printf("}\n");
  250. }
  251. }