public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Fabian Groffen" <grobian@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/portage-utils:master commit in: man/include/, /, man/
Date: Fri, 27 Dec 2019 16:57:06 +0000 (UTC)	[thread overview]
Message-ID: <1577465758.032bd7e9200d1071b79f3a5d33906020fc805048.grobian@gentoo> (raw)

commit:     032bd7e9200d1071b79f3a5d33906020fc805048
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Fri Dec 27 16:55:58 2019 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Fri Dec 27 16:55:58 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=032bd7e9

main: add masks support

Expose masks via q -m, store masks in preparation for applying masks
when listing available ebuilds.

Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 main.c             | 221 +++++++++++++++++++++++++++++++++--------------------
 main.h             |   2 +
 man/include/q.desc |   2 +
 man/q.1            |  13 +++-
 q.c                |  56 +++++++++++++-
 5 files changed, 209 insertions(+), 85 deletions(-)

diff --git a/main.c b/main.c
index 869bf31..b5404cb 100644
--- a/main.c
+++ b/main.c
@@ -22,6 +22,7 @@
 #include "eat_file.h"
 #include "rmspace.h"
 #include "scandirat.h"
+#include "set.h"
 #include "xasprintf.h"
 
 /* variables to control runtime behavior */
@@ -354,10 +355,12 @@ set_portage_env_var(env_vars *var, const char *value, const char *src)
 	}
 }
 
-/* Helper to read a portage env file (e.g. make.conf), or recursively if
- * it points to a directory */
+/* Helper to read a portage file (e.g. make.conf, package.mask), or
+ * recursively if it points to a directory (we don't care about EAPI for
+ * dirs, basically PMS 5.2.5 EAPI restriction is ignored) */
+enum portage_file_type { ENV_FILE, PMASK_FILE };
 static void
-read_portage_env_file(const char *file, env_vars vars[])
+read_portage_file(const char *file, enum portage_file_type type, void *data)
 {
 	FILE *fp;
 	struct dirent **dents;
@@ -368,6 +371,8 @@ read_portage_env_file(const char *file, env_vars vars[])
 	size_t buflen = 0;
 	size_t line;
 	int i;
+	env_vars *vars = data;
+	set *masks = data;
 
 	if (getenv("DEBUG"))
 		fprintf(stderr, "profile %s\n", file);
@@ -384,7 +389,7 @@ read_portage_env_file(const char *file, env_vars vars[])
 					d->d_name[strlen(d->d_name) - 1] == '~')
 				continue;
 			snprintf(npath, sizeof(npath), "%s/%s", file, d->d_name);
-			read_portage_env_file(npath, vars);
+			read_portage_file(npath, type, data);
 		}
 		scandir_free(dents, dentslen);
 		goto done;
@@ -402,84 +407,107 @@ read_portage_env_file(const char *file, env_vars vars[])
 			continue;
 
 		/* Handle "source" keyword */
-		if (strncmp(buf, "source ", 7) == 0) {
-			const char *sfile = buf + 7;
-			char npath[_Q_PATH_MAX * 2];
+		if (type == ENV_FILE) {
+			if (strncmp(buf, "source ", 7) == 0) {
+				const char *sfile = buf + 7;
+				char npath[_Q_PATH_MAX * 2];
 
-			if (sfile[0] != '/') {
-				/* handle relative paths */
-				size_t file_path_len;
+				if (sfile[0] != '/') {
+					/* handle relative paths */
+					size_t file_path_len;
 
-				s = strrchr(file, '/');
-				file_path_len = s - file + 1;
+					s = strrchr(file, '/');
+					file_path_len = s - file + 1;
 
-				snprintf(npath, sizeof(npath), "%.*s/%s",
-						(int)file_path_len, file, sfile);
-				sfile = npath;
-			}
-
-			read_portage_env_file(sfile, vars);
-			continue;
-		}
+					snprintf(npath, sizeof(npath), "%.*s/%s",
+							(int)file_path_len, file, sfile);
+					sfile = npath;
+				}
 
-		/* look for our desired variables and grab their value */
-		for (i = 0; vars[i].name; ++i) {
-			if (buf[vars[i].name_len] != '=' && buf[vars[i].name_len] != ' ')
-				continue;
-			if (strncmp(buf, vars[i].name, vars[i].name_len))
+				read_portage_file(sfile, type, data);
 				continue;
+			}
 
-			/* make sure we handle spaces between the varname, the =,
-			 * and the value:
-			 * VAR=val   VAR = val   VAR="val"
-			 */
-			s = buf + vars[i].name_len;
-			if ((p = strchr(s, '=')) != NULL)
-				s = p + 1;
-			while (isspace(*s))
-				++s;
-			if (*s == '"' || *s == '\'') {
-				char *endq;
-				char q = *s;
-
-				/* make sure we handle spacing/comments after the quote */
-				endq = strchr(s + 1, q);
-				if (!endq) {
-					/* If the last char is not a quote, then we span lines */
-					size_t abuflen;
-					char *abuf;
-
-					abuf = NULL;
-					while (getline(&abuf, &abuflen, fp) != -1) {
-						buf = xrealloc(buf, buflen + abuflen);
-						endq = strchr(abuf, q);
-						if (endq)
-							*endq = '\0';
-
-						strcat(buf, abuf);
-						buflen += abuflen;
-
-						if (endq)
-							break;
-					}
-					free(abuf);
-
-					if (!endq)
-						warn("%s:%zu: %s: quote mismatch",
-								file, line, vars[i].name);
+			/* look for our desired variables and grab their value */
+			for (i = 0; vars[i].name; i++) {
+				if (buf[vars[i].name_len] != '=' &&
+						buf[vars[i].name_len] != ' ')
+					continue;
+				if (strncmp(buf, vars[i].name, vars[i].name_len))
+					continue;
 
-					s = buf + vars[i].name_len + 2;
+				/* make sure we handle spaces between the varname, the =,
+				 * and the value:
+				 * VAR=val   VAR = val   VAR="val"
+				 */
+				s = buf + vars[i].name_len;
+				if ((p = strchr(s, '=')) != NULL)
+					s = p + 1;
+				while (isspace(*s))
+					s++;
+				if (*s == '"' || *s == '\'') {
+					char *endq;
+					char q = *s;
+
+					/* make sure we handle spacing/comments after the quote */
+					endq = strchr(s + 1, q);
+					if (!endq) {
+						/* if the last char is not a quote,
+						 * then we span lines */
+						size_t abuflen;
+						char *abuf;
+
+						abuf = NULL;
+						while (getline(&abuf, &abuflen, fp) != -1) {
+							buf = xrealloc(buf, buflen + abuflen);
+							endq = strchr(abuf, q);
+							if (endq)
+								*endq = '\0';
+
+							strcat(buf, abuf);
+							buflen += abuflen;
+
+							if (endq)
+								break;
+						}
+						free(abuf);
+
+						if (!endq)
+							warn("%s:%zu: %s: quote mismatch",
+									file, line, vars[i].name);
+
+						s = buf + vars[i].name_len + 2;
+					} else {
+						*endq = '\0';
+						s++;
+					}
 				} else {
-					*endq = '\0';
-					++s;
+					/* no quotes, so chop the spacing/comments ourselves */
+					size_t off = strcspn(s, "# \t\n");
+					s[off] = '\0';
 				}
+
+				set_portage_env_var(&vars[i], s, file);
+			}
+		} else if (type == PMASK_FILE) {
+			/* trim leading space */
+			for (s = buf; isspace((int)*s); s++)
+				;
+			if (*s == '\0')
+				continue;
+			if (*s == '-') {
+				/* negation/removal, lookup and drop mask if it exists;
+				 * note that this only supports exact matches (PMS
+				 * 5.2.5) so we don't even have to parse and use
+				 * atom-compare here */
+				s++;
+				if ((p = del_set(s, masks, NULL)) != NULL)
+					free(p);
 			} else {
-				/* no quotes, so chop the spacing/comments ourselves */
-				size_t off = strcspn(s, "# \t\n");
-				s[off] = '\0';
+				p = xstrdup(file);
+				if (add_set_value(s, p, masks) != NULL)
+					free(p);
 			}
-
-			set_portage_env_var(&vars[i], s, file);
 		}
 	}
 
@@ -490,9 +518,10 @@ read_portage_env_file(const char *file, env_vars vars[])
 
 /* Helper to recursively read stacked make.defaults in profiles */
 static void
-read_portage_profile(const char *profile, env_vars vars[])
+read_portage_profile(const char *profile, env_vars vars[], set *masks)
 {
 	char profile_file[_Q_PATH_MAX * 3];
+	char rpath[_Q_PATH_MAX];
 	size_t profile_len;
 	char *s;
 	char *p;
@@ -548,7 +577,9 @@ read_portage_profile(const char *profile, env_vars vars[])
 			snprintf(profile_file + profile_len,
 					sizeof(profile_file) - profile_len, "%s", s);
 		}
-		read_portage_profile(profile_file, vars);
+		read_portage_profile(
+				realpath(profile_file, rpath) == NULL ? profile_file : rpath,
+				vars, masks);
 		/* restore original path in case we were repointed by profile */
 		if (p != NULL)
 			snprintf(profile_file, sizeof(profile_file), "%s/", profile);
@@ -557,9 +588,11 @@ read_portage_profile(const char *profile, env_vars vars[])
 
 	free(buf);
 
-	/* now consume *this* profile's make.defaults */
+	/* now consume *this* profile's make.defaults and package.mask */
 	strcpy(profile_file + profile_len, "make.defaults");
-	read_portage_env_file(profile_file, vars);
+	read_portage_file(profile_file, ENV_FILE, vars);
+	strcpy(profile_file + profile_len, "package.mask");
+	read_portage_file(profile_file, PMASK_FILE, masks);
 }
 
 static bool nocolor = 0;
@@ -598,6 +631,7 @@ env_vars vars_to_read[] = {
 
 #undef _Q_EV
 };
+set *package_masks = NULL;
 
 /* Handle a single file in the repos.conf format. */
 static void
@@ -716,10 +750,11 @@ initialize_portage_env(void)
 	const char *s;
 	env_vars *var;
 	char pathbuf[_Q_PATH_MAX];
+	char rpathbuf[_Q_PATH_MAX];
 	const char *configroot = getenv("PORTAGE_CONFIGROOT");
 	char *primary_overlay = NULL;
 
-	/* initialize all the strings with their default value */
+	/* initialize all the properties with their default value */
 	for (i = 0; vars_to_read[i].name; ++i) {
 		var = &vars_to_read[i];
 		if (var->type != _Q_BOOL)
@@ -727,6 +762,8 @@ initialize_portage_env(void)
 		var->src = xstrdup(STR_DEFAULT);
 	}
 
+	package_masks = create_set();
+
 	/* figure out where to find our config files */
 	if (!configroot)
 		configroot = CONFIG_EPREFIX;
@@ -737,7 +774,7 @@ initialize_portage_env(void)
 		i--;
 
 	/* read overlays first so we can resolve repo references in profile
-	 * parent files */
+	 * parent files (non PMS feature?) */
 	snprintf(pathbuf, sizeof(pathbuf), "%.*s", (int)i, configroot);
 	read_repos_conf(pathbuf, "/usr/share/portage/config/repos.conf",
 			&primary_overlay);
@@ -747,23 +784,41 @@ initialize_portage_env(void)
 	snprintf(pathbuf, sizeof(pathbuf),
 			"%.*s/usr/share/portage/config/make.globals",
 			(int)i, configroot);
-	read_portage_env_file(pathbuf, vars_to_read);
+	read_portage_file(pathbuf, ENV_FILE, vars_to_read);
+
+	/* start with base masks, Portage behaviour PMS 5.2.8 */
+	if (primary_overlay != NULL) {
+		char *overlay;
+		size_t n;
+		array_for_each(overlay_names, n, overlay) {
+			if (overlay == primary_overlay) {
+				snprintf(pathbuf, sizeof(pathbuf), "%s/profiles/package.mask",
+						(char *)array_get_elem(overlays, n));
+				read_portage_file(pathbuf, PMASK_FILE, package_masks);
+				break;
+			}
+		}
+	}
 
 	/* walk all the stacked profiles */
 	snprintf(pathbuf, sizeof(pathbuf), "%.*s/etc/make.profile",
 			(int)i, configroot);
-	read_portage_profile(pathbuf, vars_to_read);
+	read_portage_profile(
+			realpath(pathbuf, rpathbuf) == NULL ? pathbuf : rpathbuf,
+			vars_to_read, package_masks);
 	snprintf(pathbuf, sizeof(pathbuf), "%.*s/etc/portage/make.profile",
 			(int)i, configroot);
-	read_portage_profile(pathbuf, vars_to_read);
+	read_portage_profile(
+			realpath(pathbuf, rpathbuf) == NULL ? pathbuf : rpathbuf,
+			vars_to_read, package_masks);
 
-	/* now read all the config files */
+	/* now read all Portage's config files */
 	snprintf(pathbuf, sizeof(pathbuf), "%.*s/etc/make.conf",
 			(int)i, configroot);
-	read_portage_env_file(pathbuf, vars_to_read);
+	read_portage_file(pathbuf, ENV_FILE, vars_to_read);
 	snprintf(pathbuf, sizeof(pathbuf), "%.*s/etc/portage/make.conf",
 			(int)i, configroot);
-	read_portage_env_file(pathbuf, vars_to_read);
+	read_portage_file(pathbuf, ENV_FILE, vars_to_read);
 
 	/* finally, check the env */
 	for (i = 0; vars_to_read[i].name; i++) {

diff --git a/main.h b/main.h
index 98e5cbb..68b9795 100644
--- a/main.h
+++ b/main.h
@@ -24,6 +24,7 @@
 
 #include "colors.h"
 #include "i18n.h"
+#include "set.h"
 
 extern const char *argv0;
 
@@ -152,5 +153,6 @@ typedef struct {
 	char *src;
 } env_vars;
 extern env_vars vars_to_read[];
+extern set *package_masks;
 
 #endif

diff --git a/man/include/q.desc b/man/include/q.desc
index 7109c46..7d38ba2 100644
--- a/man/include/q.desc
+++ b/man/include/q.desc
@@ -6,3 +6,5 @@ After version 0.74 of portage-utils, the cache functionality was removed
 in favour of using various trees directly, and optionally the caches
 therein.  As such the \fB-r\fR and \fB-m\fR options were removed.  It is
 no longer necessary to initialise the cache at any time.
+.P
+After version 0.82, the \fB-m\fR flag got repurposed for listing masks.

diff --git a/man/q.1 b/man/q.1
index 886b00f..21a09b3 100644
--- a/man/q.1
+++ b/man/q.1
@@ -1,5 +1,5 @@
 .\" generated by mkman.py, please do NOT edit!
-.TH q "1" "Nov 2019" "Gentoo Foundation" "q"
+.TH q "1" "Dec 2019" "Gentoo Foundation" "q"
 .SH NAME
 q \- invoke a portage utility applet
 .SH SYNOPSIS
@@ -14,6 +14,8 @@ After version 0.74 of portage-utils, the cache functionality was removed
 in favour of using various trees directly, and optionally the caches
 therein.  As such the \fB-r\fR and \fB-m\fR options were removed.  It is
 no longer necessary to initialise the cache at any time.
+.P
+After version 0.82, the \fB-m\fR flag got repurposed for listing masks.
 .SH OPTIONS
 .TP
 \fB\-i\fR, \fB\-\-install\fR
@@ -26,6 +28,15 @@ see the source (file) where the overlay was declared.
 \fB\-e\fR, \fB\-\-envvar\fR
 Print used environment variables and found values.  Use \fI-v\fR to
 see the source (file, environment) where the variable was declared.
+Additional arguments are treated as variable names to print the
+values for.  If just one name is given, only the value is printed if
+matched.  When no arguments or more than one argument is given, the
+variable name and the value is printed as a shell-style declaration.
+.TP
+\fB\-m\fR, \fB\-\-masks\fR
+Print the masks from package.mask files found.  Use \fI-v\fR to see
+the source (file) where the mask was declared.  Additional arguments
+are treated as atom selectors which must match the masks.
 .TP
 \fB\-\-root\fR \fI<arg>\fR
 Set the ROOT env var.

diff --git a/q.c b/q.c
index f137b04..4a2fd62 100644
--- a/q.c
+++ b/q.c
@@ -19,21 +19,24 @@
 #include <libproc.h>
 #endif
 
+#include "atom.h"
 #include "basename.h"
 #include "eat_file.h"
 #include "rmspace.h"
 
-#define Q_FLAGS "ioe" COMMON_FLAGS
+#define Q_FLAGS "ioem" COMMON_FLAGS
 static struct option const q_long_opts[] = {
 	{"install",       no_argument, NULL, 'i'},
 	{"overlays",      no_argument, NULL, 'o'},
 	{"envvar",        no_argument, NULL, 'e'},
+	{"masks",         no_argument, NULL, 'm'},
 	COMMON_LONG_OPTS
 };
 static const char * const q_opts_help[] = {
 	"Install symlinks for applets",
 	"Print available overlays (read from repos.conf)",
 	"Print used variables and their found values",
+	"Print (package.)masks for the current profile",
 	COMMON_OPTS_HELP
 };
 #define q_usage(ret) usage(ret, Q_FLAGS, q_long_opts, q_opts_help, NULL, lookup_applet_idx("q"))
@@ -81,6 +84,7 @@ int q_main(int argc, char **argv)
 	bool install;
 	bool print_overlays;
 	bool print_vars;
+	bool print_masks;
 	const char *p;
 	APPLET func;
 
@@ -100,12 +104,14 @@ int q_main(int argc, char **argv)
 	install = false;
 	print_overlays = false;
 	print_vars = false;
+	print_masks = false;
 	while ((i = GETOPT_LONG(Q, q, "+")) != -1) {
 		switch (i) {
 		COMMON_GETOPTS_CASES(q)
 		case 'i': install = true;        break;
 		case 'o': print_overlays = true; break;
 		case 'e': print_vars = true;     break;
+		case 'm': print_masks = true;    break;
 		}
 	}
 
@@ -258,6 +264,54 @@ int q_main(int argc, char **argv)
 		return 0;
 	}
 
+	if (print_masks) {
+		DECLARE_ARRAY(masks);
+		DECLARE_ARRAY(files);
+		char *mask;
+		size_t n;
+		int j;
+		bool match;
+		depend_atom *atom;
+		depend_atom *qatom;
+
+		array_set(package_masks, masks);
+		values_set(package_masks, files);
+
+		array_for_each(masks, n, mask) {
+			if ((atom = atom_explode(mask)) == NULL)
+				continue;
+
+			match = true;
+			if (argc > optind) {
+				match = false;
+				for (j = optind; j < argc; j++) {
+					qatom = atom_explode(argv[j]);
+					if (qatom != NULL && atom_compare(atom, qatom) == EQUAL)
+						match = true;
+					atom_implode(qatom);
+					if (match)
+						break;
+				}
+			}
+			if (!match)
+				continue;
+
+			printf("%s", atom_format(
+						"%[pfx]%[CAT]%[PF]%[SLOT]%[SUBSLOT]%[sfx]%[USE]%[REPO]",
+						atom));
+			if (verbose)
+				printf(" [%s]\n", (char *)array_get_elem(files, n));
+			else
+				printf("\n");
+			atom_implode(atom);
+		}
+
+		xarrayfree_int(masks);
+		xarrayfree_int(files);
+
+		return 0;
+	}
+
 	if (argc == optind)
 		q_usage(EXIT_FAILURE);
 	if ((func = lookup_applet(argv[optind])) == NULL)


             reply	other threads:[~2019-12-27 16:57 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-27 16:57 Fabian Groffen [this message]
  -- strict thread matches above, loose matches on Subject: below --
2021-02-20 12:23 [gentoo-commits] proj/portage-utils:master commit in: man/include/, /, man/ Fabian Groffen
2019-06-19  7:31 Fabian Groffen
2019-04-12 18:50 Fabian Groffen
2018-04-12 19:33 Fabian Groffen
2018-04-03 15:21 Fabian Groffen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1577465758.032bd7e9200d1071b79f3a5d33906020fc805048.grobian@gentoo \
    --to=grobian@gentoo.org \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox