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)