[PATCH] introduce the --optional flag (FS#61336)

Daniel M. Capella polyzen at archlinux.org
Tue Jul 28 04:55:49 UTC 2020


On June 5, 2020 7:19:14 PM EDT, Will Song <incertia at incertia.net> wrote:
> --optional[=DEPTH] will control the depth at which optional
> dependencies
> are printed. Its behavior is fairly simple. If the maximum depth is
> not
> yet exceeded, and it encounters an optional dependency, it will print
> it
> like a normal dependency except with an optional tag. A negative
> number
> means infinite depth.
> 
> Signed-off-by: Will Song <incertia at incertia.net>
> ---
> Hopefully this patch is a bit easier to apply. I took the email
> formatted patch and pasted it directly into mutt and am just modifying
> the commit comment after the ---, as you suggested. I have updated the
> manpage and the patch follows below.
> 
> --
> Will Song
> 
>  doc/pactree.8.txt |   6 ++
> src/pactree.c     | 160 +++++++++++++++++++++++++++++++++-------------
>  2 files changed, 120 insertions(+), 46 deletions(-)
> 
> diff --git a/doc/pactree.8.txt b/doc/pactree.8.txt
> index 296d04e..02eee5b 100644
> --- a/doc/pactree.8.txt
> +++ b/doc/pactree.8.txt
> @@ -58,6 +58,12 @@ Options
>  *-u, \--unique*::
>  	List dependent packages once. Implies '\--linear'.
>  
> +*-o, \--optional[=DEPTH]*::
> +	Additionally prints optional dependencies up to a certain depth,
> default 1
> +	for immediate optional dependencies. When used in conjunction with
> '-r' it
> +	shows which packages it is optional for. In Graphviz mode, produce
> dotted
> +	lines. Negative values mean infinite depth.
> +
>  *\--config <file>*::
>  	Specify an alternate pacman configuration file.
>  
> diff --git a/src/pactree.c b/src/pactree.c
> index cf83326..39328e7 100644
> --- a/src/pactree.c
> +++ b/src/pactree.c
> @@ -28,6 +28,8 @@
>  
>  #define LINE_MAX     512
>  
> +#include <limits.h>
> +
>  typedef struct tdepth {
>  	struct tdepth *prev;
>  	struct tdepth *next;
> @@ -37,6 +39,7 @@ typedef struct tdepth {
>  /* output */
>  struct graph_style {
>  	const char *provides;
> +	const char *optional;
>  	const char *tip;
>  	const char *last;
>  	const char *limb;
> @@ -50,6 +53,7 @@ struct graph_style {
>  
>  static struct graph_style graph_utf8 = {
>  	" provides",
> +	" (optional)",
>  	UTF_VR UTF_H,
>  	UTF_UR UTF_H,
>  	UTF_V " ",
> @@ -58,6 +62,7 @@ static struct graph_style graph_utf8 = {
>  
>  static struct graph_style graph_default = {
>  	" provides",
> +	" (optional)",
>  	"|-",
>  	"`-",
>  	"|",
> @@ -69,6 +74,7 @@ static struct graph_style graph_linear = {
>  	"",
>  	"",
>  	"",
> +	"",
>  	0
>  };
>  
> @@ -124,6 +130,7 @@ static int reverse = 0;
>  static int unique = 0;
>  static int searchsyncs = 0;
>  static int debug = 0;
> +static int opt_level = 0;
>  static const char *dbpath = DBPATH;
>  static const char *configfile = CONFFILE;
>  static const char *gpgdir = GPGDIR;
> @@ -243,20 +250,22 @@ static void usage(void)
>  	fprintf(stdout, "pactree v" PACKAGE_VERSION "\n\n"
>  			"A simple dependency tree viewer.\n\n"
>  			"Usage: pactree [options] PACKAGE\n\n"
> -			"  -a, --ascii          use ASCII characters for tree
> formatting\n"
> -			"  -b, --dbpath <path>  set an alternate database location\n"
> -			"  -c, --color          colorize output\n"
> -			"  -d, --depth <#>      limit the depth of recursion\n"
> -			"  -g, --graph          generate output for graphviz's dot\n"
> -			"  -h, --help           display this help message\n"
> -			"  -l, --linear         enable linear output\n"
> -			"  -r, --reverse        list packages that depend on the named
> package\n"
> -			"  -s, --sync           search sync databases instead of local\n"
> -			"  -u, --unique         show dependencies with no duplicates
> (implies -l)\n"
> -			"  -v, --version        display the version\n"
> -			"      --config <path>  set an alternate configuration file\n"
> -			"      --debug          display debug messages\n"
> -			"      --gpgdir <path>  set an alternate home directory for
> GnuPG\n");
> +			"  -a, --ascii            use ASCII characters for tree
> formatting\n"
> +			"  -b, --dbpath <path>    set an alternate database location\n"
> +			"  -c, --color            colorize output\n"
> +			"  -d, --depth <#>        limit the depth of recursion\n"
> +			"  -g, --graph            generate output for graphviz's dot\n"
> +			"  -h, --help             display this help message\n"
> +			"  -l, --linear           enable linear output\n"
> +			"  -r, --reverse          list packages that depend on the named
> package\n"
> +			"  -s, --sync             search sync databases instead of
> local\n"
> +			"  -u, --unique           show dependencies with no duplicates
> (implies -l)\n"
> +			"  -o, --optional[=DEPTH] controls at which depth to stop printing
> optional deps\n"
> +			"                         (-1 for no limit)\n"
> +			"  -v, --version          display the version\n"
> +			"      --config <path>    set an alternate configuration file\n"
> +			"      --debug            display debug messages\n"
> +			"      --gpgdir <path>    set an alternate home directory for
> GnuPG\n");
>  }
>  
>  static void version(void)
> @@ -280,6 +289,7 @@ static int parse_options(int argc, char *argv[])
>  		{"reverse", no_argument,          0, 'r'},
>  		{"sync",    no_argument,          0, 's'},
>  		{"unique",  no_argument,          0, 'u'},
> +		{"optional",optional_argument,    0, 'o'},
>  		{"version", no_argument,          0, 'v'},
>  
>  		{"config",  required_argument,    0, OP_CONFIG},
> @@ -294,7 +304,7 @@ static int parse_options(int argc, char *argv[])
>  		style = &graph_utf8;
>  	}
>  
> -	while((opt = getopt_long(argc, argv, "ab:cd:ghlrsuv", opts,
> &option_index))) {
> +	while((opt = getopt_long(argc, argv, "ab:cd:ghlrsuo::v", opts,
> &option_index))) {
>  		if(opt < 0) {
>  			break;
>  		}
> @@ -345,6 +355,17 @@ static int parse_options(int argc, char *argv[])
>  			case 'v':
>  				version();
>  				cleanup(0);
> +			case 'o':
> +				if(optarg) {
> +					opt_level = (int)strtol(optarg, &endptr, 10);
> +					if(*endptr != '\0') {
> +						fprintf(stderr, "error: invalid optional depth -- %s\n",
> optarg);
> +						return 1;
> +					}
> +				} else {
> +					opt_level = 1;
> +				}
> +				break;
>  			case 'h':
>  				usage();
>  				cleanup(0);
> @@ -364,9 +385,10 @@ static int parse_options(int argc, char *argv[])
>  
>  /* pkg provides provision */
>  static void print_text(const char *pkg, const char *provision,
> -		tdepth *depth, int last)
> +		tdepth *depth, int last, int opt_dep)
>  {
>  	const char *tip = "";
> +	const char *opt_str = opt_dep ? style->optional : "";
>  	int level = 1;
>  	if(!pkg && !provision) {
>  		/* not much we can do */
> @@ -395,38 +417,39 @@ static void print_text(const char *pkg, const
> char *provision,
> 	 * want to print the provided package. This makes output easier to
> parse and
>  	 * to reuse. */
>  	if(!pkg && provision) {
> -		printf("%s%s%s%s [unresolvable]%s\n", tip, color->leaf1,
> -				provision, color->branch1, color->off);
> +		printf("%s%s%s%s [unresolvable]%s%s\n", tip, color->leaf1,
> +				provision, color->branch1, opt_str, color->off);
> 	} else if(provision && strcmp(pkg, provision) != 0 &&
> *(style->provides) != '\0') {
> -		printf("%s%s%s%s%s %s%s%s\n", tip, color->leaf1, pkg,
> -				color->leaf2, style->provides, color->leaf1, provision,
> +		printf("%s%s%s%s%s %s%s%s%s\n", tip, color->leaf1, pkg,
> +				color->leaf2, style->provides, color->leaf1, provision, opt_str,
>  				color->off);
>  	} else {
> -		printf("%s%s%s%s\n", tip, color->leaf1, pkg, color->off);
> +		printf("%s%s%s%s%s\n", tip, color->leaf1, pkg, opt_str,
> color->off);
>  	}
>  }
>  
> -static void print_graph(const char *parentname, const char *pkgname,
> const char *depname)
> +static void print_graph(const char *parentname, const char *pkgname,
> const char *depname, int opt_dep)
>  {
> +	const char *style = opt_dep ? ", style=dotted" : "";
>  	if(depname) {
> -		printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname,
> depname);
> +		printf("\"%s\" -> \"%s\" [color=chocolate4%s];\n", parentname,
> depname, style);
> 		if(pkgname && strcmp(depname, pkgname) != 0 &&
> !alpm_list_find_str(provisions, depname)) {
> -			printf("\"%s\" -> \"%s\" [arrowhead=none, color=grey];\n",
> depname, pkgname);
> +			printf("\"%s\" -> \"%s\" [arrowhead=none, color=grey%s];\n",
> depname, pkgname, style);
>  			provisions = alpm_list_add(provisions, strdup(depname));
>  		}
>  	} else if(pkgname) {
> -		printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname,
> pkgname);
> +		printf("\"%s\" -> \"%s\" [color=chocolate4%s];\n", parentname,
> pkgname, style);
>  	}
>  }
>  
>  /* parent depends on dep which is satisfied by pkg */
>  static void print(const char *parentname, const char *pkgname,
> -		const char *depname, tdepth *depth, int last)
> +		const char *depname, tdepth *depth, int last, int opt_dep)
>  {
>  	if(graphviz) {
> -		print_graph(parentname, pkgname, depname);
> +		print_graph(parentname, pkgname, depname, opt_dep);
>  	} else {
> -		print_text(pkgname, depname, depth, last);
> +		print_text(pkgname, depname, depth, last, opt_dep);
>  	}
>  }
>  
> @@ -442,7 +465,7 @@ static void print_start(const char *pkgname, const
> char *provname)
>  			NULL,
>  			0
>  		};
> -		print_text(pkgname, provname, &d, 0);
> +		print_text(pkgname, provname, &d, 0, 0);
>  	}
>  }
>  
> @@ -465,23 +488,34 @@ static alpm_list_t *get_pkg_deps(alpm_pkg_t
> *pkg)
>  	return dep_strings;
>  }
>  
> -/**
> - * walk dependencies, showing dependencies of the target
> - */
> -static void walk_deps(alpm_list_t *dblist, alpm_pkg_t *pkg, tdepth
> *depth, int rev)
> +static alpm_list_t *get_pkg_optdeps(alpm_pkg_t *pkg)
>  {
> -	alpm_list_t *deps, *i;
> -
> -	if(!pkg || ((max_depth >= 0) && (depth->level > max_depth))) {
> -		return;
> +	alpm_list_t *i, *dep_strings = NULL;
> +	for(i = alpm_pkg_get_optdepends(pkg); i; i = alpm_list_next(i)) {
> +		alpm_depend_t *dep = i->data;
> +		char *ds = alpm_dep_compute_string(dep);
> +		dep_strings = alpm_list_add(dep_strings, ds);
>  	}
> +	return dep_strings;
> +}
>  
> -	walked = alpm_list_add(walked, (void *)alpm_pkg_get_name(pkg));
> +/* forward declaration */
> +static void walk_deps(alpm_list_t *dblist, alpm_pkg_t *pkg, tdepth
> *depth, int rev, int optional);
>  
> -	if(rev) {
> -		deps = alpm_pkg_compute_requiredby(pkg);
> +/**
> + * print the dependency list given, passing the optional parameter
> when required
> + */
> +static void print_dep_list(alpm_list_t *deps, alpm_list_t *dblist,
> alpm_pkg_t *pkg, tdepth *depth, int rev, int optional, int opt_dep)
> +{
> +	alpm_list_t *i;
> +	int new_optional;
> +
> +	if(optional > 0) {
> +		/* decrease the depth by 1 */
> +		new_optional = optional - 1;
>  	} else {
> -		deps = get_pkg_deps(pkg);
> +		/* preserve 0 (ran out of depth) and negative numbers (infinite
> depth) */
> +		new_optional = optional;
>  	}
>  
>  	for(i = deps; i; i = alpm_list_next(i)) {
> @@ -494,10 +528,10 @@ static void walk_deps(alpm_list_t *dblist,
> alpm_pkg_t *pkg, tdepth *depth, int r
> 			/* if we've already seen this package, don't print in "unique"
> output
>  			 * and don't recurse */
>  			if(!unique) {
> -				print(alpm_pkg_get_name(pkg), alpm_pkg_get_name(dep_pkg),
> pkgname, depth, last);
> +				print(alpm_pkg_get_name(pkg), alpm_pkg_get_name(dep_pkg),
> pkgname, depth, last, opt_dep);
>  			}
>  		} else {
> -			print(alpm_pkg_get_name(pkg), alpm_pkg_get_name(dep_pkg), pkgname,
> depth, last);
> +			print(alpm_pkg_get_name(pkg), alpm_pkg_get_name(dep_pkg), pkgname,
> depth, last, opt_dep);
>  			if(dep_pkg) {
>  				tdepth d = {
>  					depth,
> @@ -506,7 +540,7 @@ static void walk_deps(alpm_list_t *dblist,
> alpm_pkg_t *pkg, tdepth *depth, int r
>  				};
>  				depth->next = &d;
>  				/* last dep, cut off the limb here */
> -				if(last) {
> +				if((last && optional && opt_dep) || (last && !optional)) {
>  					if(depth->prev) {
>  						depth->prev->next = &d;
>  						d.prev = depth->prev;
> @@ -515,13 +549,47 @@ static void walk_deps(alpm_list_t *dblist,
> alpm_pkg_t *pkg, tdepth *depth, int r
>  						d.prev = NULL;
>  					}
>  				}
> -				walk_deps(dblist, dep_pkg, &d, rev);
> +				walk_deps(dblist, dep_pkg, &d, rev, new_optional);
>  				depth->next = NULL;
>  			}
>  		}
>  	}
> +}
> +
> +/**
> + * walk dependencies, showing dependencies of the target
> + */
> +static void walk_deps(alpm_list_t *dblist, alpm_pkg_t *pkg, tdepth
> *depth, int rev, int optional)
> +{
> +	alpm_list_t *deps;
> +
> +	if(!pkg || ((max_depth >= 0) && (depth->level > max_depth))) {
> +		return;
> +	}
> +
> +	walked = alpm_list_add(walked, (void *)alpm_pkg_get_name(pkg));
> +
> +	if(rev) {
> +		deps = alpm_pkg_compute_requiredby(pkg);
> +	} else {
> +		deps = get_pkg_deps(pkg);
> +	}
> +
> +	print_dep_list(deps, dblist, pkg, depth, rev, optional, 0);
>  
>  	FREELIST(deps);
> +
> +	if(optional){
> +		if(rev) {
> +			deps = alpm_pkg_compute_optionalfor(pkg);
> +		} else {
> +			deps = get_pkg_optdeps(pkg);
> +		}
> +
> +		print_dep_list(deps, dblist, pkg, depth, rev, optional, 1);
> +
> +		FREELIST(deps);
> +	}
>  }
>  
>  int main(int argc, char *argv[])
> @@ -576,7 +644,7 @@ int main(int argc, char *argv[])
>  		NULL,
>  		1
>  	};
> -	walk_deps(dblist, pkg, &d, reverse);
> +	walk_deps(dblist, pkg, &d, reverse, opt_level);
>  
>  	print_end();
>  

Merged, sorry for the wait.

--
Best,
Daniel <https://danielcapella.com>


More information about the pacman-contrib mailing list