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 via email to