commit:     da9cb1c25e743601521274130e023f82dab1621e
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Thu May  2 08:27:49 2019 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Thu May  2 08:27:49 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=da9cb1c2

quse: rewrite using libc/cache

- dropped -K/KEYWORDS search, this should be done using qkeywords (qcache)
- unified colouring/naming packages
- added listing per package mode (-v)

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

 TODO.md                       |   1 +
 man/include/quse.desc         |   2 +-
 man/include/quse.optdesc.yaml |   9 +-
 man/quse.1                    |  21 +-
 quse.c                        | 820 ++++++++++++++++++++++++------------------
 tests/quse/dotest             |  55 +--
 tests/quse/list01.good        |   4 +-
 7 files changed, 498 insertions(+), 414 deletions(-)

diff --git a/TODO.md b/TODO.md
index 38778b0..3a3c26a 100644
--- a/TODO.md
+++ b/TODO.md
@@ -98,3 +98,4 @@
 
 # quse
 - make -v faster by calling searcg funcs once per package match
+- make -v only print requested USE-flag when flags given

diff --git a/man/include/quse.desc b/man/include/quse.desc
index 7492774..84a4ba2 100644
--- a/man/include/quse.desc
+++ b/man/include/quse.desc
@@ -1,2 +1,2 @@
-\fIquse\fR searches in ebuilds for a match in IUSE, KEYWORDS or LICENSE.
+\fIquse\fR searches in ebuilds for a match in IUSE or LICENSE.
 It can also search for USE-flags and show their descriptions.

diff --git a/man/include/quse.optdesc.yaml b/man/include/quse.optdesc.yaml
index 2a3675d..79a98fa 100644
--- a/man/include/quse.optdesc.yaml
+++ b/man/include/quse.optdesc.yaml
@@ -1,11 +1,6 @@
 exact: Search for exact string, e.g.\ do not use regular expression matching.
-format: |
-    Advanced option to manually override the variable searched for in
-    ebuilds.  By default, the search is \fIIUSE=\fR, the \fB\-K\fR and
-    \fB\-L\fR override that to \fIKEYWORDS=\fR and \fILICENSE=\fR
-    respectively.  This option, sets the search to any variable.  Note
-    that the equals sign is part of the search, and needs to be set.
 verbose: |
-    Show problems encountered during parsing.  These are mostly
+    Show descriptions for USE-flags for packages that match the search.
+    Also shows problems encountered during parsing.  These are mostly
     diagnostic and indicate possible incorrectness in the results.
 quiet: Ignored for compatibility with other qapplets.

diff --git a/man/quse.1 b/man/quse.1
index 6292f8c..bb02306 100644
--- a/man/quse.1
+++ b/man/quse.1
@@ -1,12 +1,12 @@
 .\" generated by mkman.py, please do NOT edit!
-.TH quse "1" "Apr 2019" "Gentoo Foundation" "quse"
+.TH quse "1" "May 2019" "Gentoo Foundation" "quse"
 .SH NAME
 quse \- find pkgs using useflags
 .SH SYNOPSIS
 .B quse
 \fI[opts] <useflag>\fR
 .SH DESCRIPTION
-\fIquse\fR searches in ebuilds for a match in IUSE, KEYWORDS or LICENSE.
+\fIquse\fR searches in ebuilds for a match in IUSE or LICENSE.
 It can also search for USE-flags and show their descriptions.
 .SH OPTIONS
 .TP
@@ -16,30 +16,21 @@ Search for exact string, e.g.\ do not use regular 
expression matching.
 \fB\-a\fR, \fB\-\-all\fR
 List all ebuilds, don't match anything.
 .TP
-\fB\-K\fR, \fB\-\-keywords\fR
-Use the KEYWORDS vs IUSE.
-.TP
 \fB\-L\fR, \fB\-\-license\fR
 Use the LICENSE vs IUSE.
 .TP
 \fB\-D\fR, \fB\-\-describe\fR
 Describe the USE flag.
 .TP
-\fB\-F\fR \fI<arg>\fR, \fB\-\-format\fR \fI<arg>\fR
-Advanced option to manually override the variable searched for in
-ebuilds.  By default, the search is \fIIUSE=\fR, the \fB\-K\fR and
-\fB\-L\fR override that to \fIKEYWORDS=\fR and \fILICENSE=\fR
-respectively.  This option, sets the search to any variable.  Note
-that the equals sign is part of the search, and needs to be set.
-.TP
-\fB\-N\fR, \fB\-\-name\-only\fR
-Only show package name.
+\fB\-p\fR \fI<arg>\fR, \fB\-\-package\fR \fI<arg>\fR
+Restrict matching to package or category.
 .TP
 \fB\-\-root\fR \fI<arg>\fR
 Set the ROOT env var.
 .TP
 \fB\-v\fR, \fB\-\-verbose\fR
-Show problems encountered during parsing.  These are mostly
+Show descriptions for USE-flags for packages that match the search.
+Also shows problems encountered during parsing.  These are mostly
 diagnostic and indicate possible incorrectness in the results.
 .TP
 \fB\-q\fR, \fB\-\-quiet\fR

diff --git a/quse.c b/quse.c
index c5f44ed..fbf61cf 100644
--- a/quse.c
+++ b/quse.c
@@ -18,6 +18,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <dirent.h>
+#include <ctype.h>
 #include <assert.h>
 
 #include "cache.h"
@@ -25,434 +26,565 @@
 #include "xarray.h"
 #include "xregex.h"
 
-/*
- quse -CKe -- '-*' {'~',-,}{alpha,amd64,hppa,ia64,ppc,ppc64,sparc,x86}
- quse -Ke --  nls
-*/
-
-#define QUSE_FLAGS "eavKLDF:N" COMMON_FLAGS
+#define QUSE_FLAGS "eaLDp:" COMMON_FLAGS
 static struct option const quse_long_opts[] = {
        {"exact",     no_argument, NULL, 'e'},
        {"all",       no_argument, NULL, 'a'},
-       {"keywords",  no_argument, NULL, 'K'},
        {"license",   no_argument, NULL, 'L'},
        {"describe",  no_argument, NULL, 'D'},
-       {"format",     a_argument, NULL, 'F'},
-       {"name-only", no_argument, NULL, 'N'},
+       {"package",    a_argument, NULL, 'p'},
        COMMON_LONG_OPTS
 };
 static const char * const quse_opts_help[] = {
        "Show exact non regexp matching using strcmp",
        "List all ebuilds, don't match anything",
-       "Use the KEYWORDS vs IUSE",
        "Use the LICENSE vs IUSE",
        "Describe the USE flag",
-       "Use your own variable formats. -F NAME=",
-       "Only show package name",
+       "Restrict matching to package or category",
        COMMON_OPTS_HELP
 };
 #define quse_usage(ret) usage(ret, QUSE_FLAGS, quse_long_opts, quse_opts_help, 
NULL, lookup_applet_idx("quse"))
 
-char quse_name_only = 0;
+struct quse_state {
+       int argc;
+       char **argv;
+       const char *overlay;
+       bool do_all:1;
+       bool do_regex:1;
+       bool do_describe:1;
+       bool do_licence:1;
+       bool do_list:1;
+       depend_atom *match;
+       regex_t *pregv;
+};
 
-static void
-print_highlighted_use_flags(char *string, int ind, int argc, char **argv)
+static char *_quse_getline_buf = NULL;
+static size_t _quse_getline_buflen = 0;
+#define GETLINE(FD, BUF, LEN) \
+       LEN = getline(&_quse_getline_buf, &_quse_getline_buflen, FD); \
+       BUF = _quse_getline_buf
+
+static bool
+quse_search_use_local_desc(int portdirfd, struct quse_state *state)
 {
-       char *str, *p;
-       char buf[BUFSIZ];
-       size_t pos, len;
-       short highlight = 0;
+       int fd;
+       FILE *f;
+       ssize_t linelen;
+       char *buf;
+       char *p;
+       char *q;
        int i;
+       bool match = false;
+       bool ret = false;
+       depend_atom *atom;
+
+       fd = openat(portdirfd, "profiles/use.local.desc", O_RDONLY | O_CLOEXEC);
+       if (fd == -1)
+               return false;
+
+       f = fdopen(fd, "r");
+       if (f == NULL) {
+               close(fd);
+               return false;
+       }
 
-       if (quse_name_only)
-               return;
+       /* use.local.desc: <pkg>:<use> - <desc> */
+       do {
+               GETLINE(f, buf, linelen);
+               if (linelen < 0)
+                       break;
+
+               rmspace_len(buf, (size_t)linelen);
+               if (buf[0] == '#' || buf[0] == '\0')
+                       continue;
 
-       snprintf(buf, sizeof(buf), "%.*s", (int)sizeof(buf) - 1, string);
-       str = buf;
-       remove_extra_space(str);
-       rmspace(str);
+               if ((p = strchr(buf, ':')) == NULL)
+                       continue;
+               *p++ = '\0';
 
-       if (*WHITE == '\0') {
-               printf("%s", str);
-               return;
-       }
+               q = strchr(p, ' ');
+               if (q == NULL || q[1] != '-')
+                       continue;
+               *q = '\0';
+               q += 3; /* " - " */
 
-       len = strlen(str);
-       for (pos = 0; pos < len; pos++) {
-               highlight = 0;
-               if ((p = strchr(str, ' ')) != NULL)
-                       *p = 0;
-               pos += strlen(str);
-               for (i = ind; i < argc; ++i)
-                       if (strcmp(str, argv[i]) == 0)
-                               highlight = 1;
-               if (highlight)
-                       printf("%s%s%s ", BOLD, str, NORM);
-               else
-                       printf("%s%s%s%s ", NORM, MAGENTA, str, NORM);
-               if (p != NULL)
-                       str = p + 1;
-       }
+               match = false;
+               for (i = 0; i < state->argc; i++) {
+                       if (state->do_regex) {
+                               if (regexec(&state->pregv[i], p, 0, NULL, 0) != 
0)
+                                       continue;
+                       } else {
+                               if (strcmp(p, state->argv[i]) != 0)
+                                       continue;
+                       }
+                       match = true;
+                       break;
+               }
+
+               if (match) {
+                       if ((atom = atom_explode(buf)) == NULL)
+                               continue;
+
+                       if (state->match == NULL ||
+                                       atom_compare(atom, state->match) == 
EQUAL)
+                       {
+                               if (state->do_list)
+                                       printf("  %s%s%s  %s\n", MAGENTA, p, 
NORM, q);
+                               else
+                                       printf("%s%s/%s%s%s[%s%s%s] %s\n",
+                                                       BOLD, atom->CATEGORY,
+                                                       BLUE, atom->PN, NORM,
+                                                       MAGENTA, p, NORM, q);
+                       }
+
+                       atom_implode(atom);
+                       ret = true;
+               }
+       } while (1);
+
+       fclose(f);
+       return ret;
 }
 
-static void
-quse_describe_flag(const char *overlay, unsigned int ind, unsigned int argc, 
char **argv)
+static bool
+quse_search_use_desc(int portdirfd, struct quse_state *state)
 {
-#define NUM_SEARCH_FILES ARRAY_SIZE(search_files)
-       int linelen;
-       size_t buflen;
-       char *buf, *p;
-       unsigned int i, f;
-       size_t s;
-       const char * const search_files[] = {
-               "use.desc",
-               "use.local.desc",
-               "arch.list"
-       };
-       FILE *fp[NUM_SEARCH_FILES];
-       int dfd, fd;
-       DIR *d;
-       struct dirent *de;
+       int fd;
+       FILE *f;
+       ssize_t linelen;
+       char *buf;
+       char *p;
+       int i;
+       bool match = false;
+       bool ret = false;
 
-       /* pick 1000 arbitrarily long enough for all files under desc/ */
-       buflen = strlen(overlay) + 1000;
-       buf = xmalloc(buflen);
+       fd = openat(portdirfd, "profiles/use.desc", O_RDONLY | O_CLOEXEC);
+       if (fd == -1)
+               return false;
 
-       for (i = 0; i < NUM_SEARCH_FILES; ++i) {
-               snprintf(buf, buflen, "%s/profiles/%s", overlay, 
search_files[i]);
-               fp[i] = fopen(buf, "r");
-               if (verbose && fp[i] == NULL)
-                       warnp("skipping %s", buf);
+       f = fdopen(fd, "r");
+       if (f == NULL) {
+               close(fd);
+               return false;
        }
 
-       for (i = ind; i < argc; i++) {
-               s = strlen(argv[i]);
+       /* use.desc: <use> - <desc> */
+       do {
+               GETLINE(f, buf, linelen);
+               if (linelen < 0)
+                       break;
 
-               for (f = 0; f < NUM_SEARCH_FILES; ++f) {
-                       if (fp[f] == NULL)
-                               continue;
+               rmspace_len(buf, (size_t)linelen);
+               if (buf[0] == '#' || buf[0] == '\0')
+                       continue;
+
+               p = strchr(buf, ' ');
+               if (p == NULL || p[1] != '-')
+                       continue;
+               *p = '\0';
+               p += 3; /* " - " */
 
-                       while ((linelen = getline(&buf, &buflen, fp[f])) >= 0) {
-                               rmspace_len(buf, (size_t)linelen);
-                               if (buf[0] == '#' || buf[0] == '\0')
+               match = false;
+               for (i = 0; i < state->argc; i++) {
+                       if (state->do_regex) {
+                               if (regexec(&state->pregv[i], buf, 0, NULL, 0) 
!= 0)
                                        continue;
+                       } else {
+                               if (strcmp(buf, state->argv[i]) != 0)
+                                       continue;
+                       }
+                       match = true;
+                       break;
+               }
 
-                               switch (f) {
-                                       case 0: /* Global use.desc */
-                                               if (!strncmp(buf, argv[i], s))
-                                                       if (buf[s] == ' ' && 
buf[s + 1] == '-') {
-                                                               printf(" 
%sglobal%s:%s%s%s: %s\n",
-                                                                               
BOLD, NORM, BLUE, argv[i], NORM,
-                                                                               
buf + s + 3);
-                                                               goto skip_file;
-                                                       }
-                                               break;
+               if (match) {
+                       if (state->do_list)
+                               printf("  %s%s%s  %s\n", MAGENTA, buf, NORM, p);
+                       else
+                               printf("%sglobal%s[%s%s%s] %s\n",
+                                               BOLD, NORM, MAGENTA, buf, NORM, 
p);
 
-                                       case 1: /* Local use.local.desc */
-                                               if ((p = strchr(buf, ':')) == 
NULL)
-                                                       break;
-                                               ++p;
-                                               if (!strncmp(p, argv[i], s)) {
-                                                       if (p[s] == ' ' && p[s 
+ 1] == '-') {
-                                                               *p = '\0';
-                                                               printf(" 
%slocal%s:%s%s%s:%s%s%s %s\n",
-                                                                               
BOLD, NORM, BLUE, argv[i], NORM,
-                                                                               
BOLD, buf, NORM, p + s + 3);
-                                                       }
-                                               }
-                                               break;
+                       ret = true;
+               }
+       } while (1);
 
-                                       case 2: /* Architectures arch.list */
-                                               if (!strcmp(buf, argv[i])) {
-                                                       printf(" 
%sarch%s:%s%s%s: %s architecture\n",
-                                                                       BOLD, 
NORM, BLUE, argv[i], NORM, argv[i]);
-                                                       goto skip_file;
-                                               }
-                                               break;
-                               }
-                       }
+       fclose(f);
+       return ret;
+}
 
- skip_file:
-                       rewind(fp[f]);
-               }
-       }
+static bool
+quse_search_profiles_desc(
+               int portdirfd,
+               struct quse_state *state)
+{
+       int fd;
+       FILE *f;
+       ssize_t linelen;
+       char *buf;
+       char *p;
+       int i;
+       bool match = false;
+       bool ret = false;
+       size_t namelen;
+       size_t arglen;
+       char ubuf[_Q_PATH_MAX];
+       struct dirent *de;
+       DIR *d;
+       int dfd;
 
-       for (f = 0; f < NUM_SEARCH_FILES; ++f)
-               if (fp[f] != NULL)
-                       fclose(fp[f]);
-
-       /* now scan the desc dir */
-       snprintf(buf, buflen, "%s/profiles/desc/", overlay);
-       dfd = open(buf, O_RDONLY|O_CLOEXEC);
-       if (dfd == -1) {
-               if (verbose)
-                       warnp("skipping %s", buf);
-               goto done;
-       }
-       d = fdopendir(dfd);
+       fd = openat(portdirfd, "profiles/desc", O_RDONLY|O_CLOEXEC);
+       if (fd == -1)
+               return false;
+       d = fdopendir(fd);
        if (!d) {
-               close(dfd);
-               goto done;
+               close(fd);
+               return false;
        }
 
-       while ((de = readdir(d)) != NULL) {
-               s = strlen(de->d_name);
-               if (s < 6)
-                       continue;
-               p = de->d_name + s - 5;
-               if (strcmp(p, ".desc"))
-                       continue;
+       if (_quse_getline_buf == NULL) {
+               _quse_getline_buflen = _Q_PATH_MAX;
+               _quse_getline_buf = xmalloc(sizeof(char) * 
_quse_getline_buflen);
+       }
 
-               fd = openat(dfd, de->d_name, O_RDONLY|O_CLOEXEC);
-               if (fd == -1) {
-                       if (verbose)
-                               warnp("skipping %s/profiles/desc/%s", overlay, 
de->d_name);
+       while ((de = readdir(d))) {
+               if (de->d_name[0] == '.')
                        continue;
-               }
-               fp[0] = fdopen(fd, "r");
-               if (!fp[0]) {
+
+               namelen = strlen(de->d_name);
+               if (namelen <= 5 || strcmp(de->d_name + namelen - 5, ".desc") 
!= 0)
+                       return false;
+
+               snprintf(_quse_getline_buf, _quse_getline_buflen,
+                               "profiles/desc/%s", de->d_name);
+               dfd = openat(portdirfd, _quse_getline_buf, O_RDONLY | 
O_CLOEXEC);
+               if (dfd == -1)
+                       return false;
+
+               f = fdopen(dfd, "r");
+               if (f == NULL) {
                        close(fd);
-                       continue;
+                       return false;
                }
 
-               /* Chop the trailing .desc for better display */
-               *p = '\0';
+               /* remove trailing .desc */
+               namelen -= 5;
+
+               /* use.desc: <use> - <desc> */
+               do {
+                       GETLINE(f, buf, linelen);
+                       if (linelen < 0)
+                               break;
 
-               while ((linelen = getline(&buf, &buflen, fp[0])) >= 0) {
                        rmspace_len(buf, (size_t)linelen);
                        if (buf[0] == '#' || buf[0] == '\0')
                                continue;
 
-                       if ((p = strchr(buf, '-')) == NULL) {
- invalid_line:
-                               warn("Invalid line in '%s': %s", de->d_name, 
buf);
+                       p = strchr(buf, ' ');
+                       if (p == NULL || p[1] != '-')
                                continue;
+                       *p = '\0';
+                       p += 3; /* " - " */
+
+                       match = false;
+                       for (i = 0; i < state->argc; i++) {
+                               arglen = strlen(state->argv[i]);
+                               if (arglen > namelen) {
+                                       /* nginx_modules_http_lua = 
NGINX_MODULES_HTTP[lua] */
+                                       match = strncmp(state->argv[i], 
de->d_name, namelen) == 0;
+                                       if (match && state->argv[i][namelen] == 
'_') {
+                                               match = 
strcmp(&state->argv[i][namelen + 1], buf) == 0;
+                                       } else {
+                                               match = false;
+                                       }
+                                       if (match)
+                                               break;
+                               }
+
+                               if (state->do_regex) {
+                                       if (regexec(&state->pregv[i], buf, 0, 
NULL, 0) != 0)
+                                               continue;
+                               } else {
+                                       if (strcmp(buf, state->argv[i]) != 0)
+                                               continue;
+                               }
+                               match = true;
+                               break;
                        }
-                       while (p[-1] != ' ' && p[1] != ' ') {
-                               /* maybe the flag has a '-' in it ... */
-                               if ((p = strchr(p + 1, '-')) == NULL)
-                                       goto invalid_line;
+
+                       if (match) {
+                               const char *r = de->d_name;
+                               char *s = ubuf;
+                               do {
+                                       *s++ = (char)toupper((int)*r);
+                               } while (++r < (de->d_name + namelen));
+                               *s = '\0';
+                               if (state->do_list)
+                                       printf("  %s=%s%s%s  %s\n", ubuf, 
MAGENTA, buf, NORM, p);
+                               else
+                                       printf("%s%s%s[%s%s%s] %s\n",
+                                                       BOLD, ubuf, NORM, 
MAGENTA, buf, NORM, p);
+
+                               ret = true;
                        }
-                       p[-1] = '\0';
-                       p += 2;
+               } while (1);
 
-                       for (i = ind; i < argc; i++)
-                               if (!strcmp(argv[i], buf))
-                                       printf(" %s%s%s:%s%s%s: %s\n",
-                                                       BOLD, de->d_name, NORM, 
BLUE, argv[i], NORM, p);
-               }
-               fclose(fp[0]);
+               fclose(f);
        }
        closedir(d);
 
- done:
-       free(buf);
+       return ret;
 }
 
-int quse_main(int argc, char **argv)
+static void
+quse_describe_flag(const char *root, const char *overlay,
+               struct quse_state *state)
 {
-       FILE *fp;
-       const char *cache_file = NULL;
-       char *p;
+       char buf[_Q_PATH_MAX];
+       int portdirfd;
 
-       char buf0[_Q_PATH_MAX];
-       char buf1[_Q_PATH_MAX];
-       char buf2[_Q_PATH_MAX];
+       snprintf(buf, sizeof(buf), "%s/%s", root, overlay);
+       portdirfd = open(buf, O_RDONLY|O_CLOEXEC|O_PATH);
+       if (portdirfd == -1)
+               return;
 
-       int linelen;
-       size_t ebuildlen;
-       char *ebuild;
+       quse_search_use_desc(portdirfd, state);
+       quse_search_use_local_desc(portdirfd, state);
+       quse_search_profiles_desc(portdirfd, state);
 
-       const char *search_var = NULL;
-       const char *search_vars[] = {
-               "IUSE=",
-               "KEYWORDS=",
-               "LICENSE=",
-               search_var
-       };
-       short quse_all = 0;
-       int regexp_matching = 1, i, idx = 0;
-       size_t search_len;
-       size_t n;
-       const char *overlay;
+       close(portdirfd);
+}
 
-       while ((i = GETOPT_LONG(QUSE, quse, "")) != -1) {
-               switch (i) {
-               case 'e': regexp_matching = 0; break;
-               case 'a': quse_all = 1; break;
-               case 'K': idx = 1; break;
-               case 'L': idx = 2; break;
-               case 'D': idx = -1; break;
-               case 'F': idx = 3, search_vars[idx] = xstrdup(optarg); break;
-               case 'N': quse_name_only = 1; break;
-               COMMON_GETOPTS_CASES(quse)
+static int
+quse_results_cb(cache_pkg_ctx *pkg_ctx, void *priv)
+{
+       struct quse_state *state = (struct quse_state *)priv;
+       depend_atom *atom = NULL;  /* pacify compiler */
+       char buf[8192];
+       cache_pkg_meta *meta;
+       bool match;
+       char *p;
+       char *q;
+       char *s;
+       char *v;
+       char *w;
+       int i;
+       int len;
+       int portdirfd = -1;  /* pacify compiler */
+
+       if (state->match || verbose) {
+               snprintf(buf, sizeof(buf), "%s/%s",
+                               pkg_ctx->cat_ctx->name, pkg_ctx->name);
+               atom = atom_explode(buf);
+               if (atom == NULL)
+                       return 0;
+
+               if (state->match) {
+                       match = atom_compare(atom, state->match) == EQUAL;
+                       if (!verbose || !match)
+                               atom_implode(atom);
+
+                       if (!match)
+                               return 0;
                }
        }
-       if (argc == optind && !quse_all && idx >= 0)
-               quse_usage(EXIT_FAILURE);
 
-       if (idx == -1) {
-               array_for_each(overlays, n, overlay)
-                       quse_describe_flag(overlay, optind, argc, argv);
+       meta = cache_pkg_read(pkg_ctx);
+       if (meta == NULL)
                return 0;
-       }
 
-       if (quse_all) optind = argc;
+       if (meta->IUSE == NULL)
+               return 0;
 
-       search_len = strlen(search_vars[idx]);
-       assert(search_len < sizeof(buf0));
+       if (verbose) {
+               portdirfd = openat(pkg_ctx->cat_ctx->ctx->portroot_fd,
+                               state->overlay, O_RDONLY | O_CLOEXEC | O_PATH);
+               if (portdirfd == -1)
+                       return 0;
+       }
 
-       array_for_each(overlays, n, overlay) {
-               int overlay_fd;
+       match = false;
+       q = p = state->do_licence ? meta->LICENSE : meta->IUSE;
+       buf[0] = '\0';
+       v = buf;
+       w = buf + sizeof(buf);
+
+       if (state->do_all) {
+               match = true;
+               v = q;
+       } else {
+               do {
+                       if (*p == ' ' || *p == '\0') {
+                               /* skip over consequtive whitespace */
+                               if (p == q) {
+                                       q++;
+                                       continue;
+                               }
 
-               /* FIXME: use libq/cache here */ if (1 == 1) {
-                       warnp("could not read cache: %s", cache_file);
-                       continue;
-               }
+                               s = q;
+                               if (*q == '-' || *q == '+' || *q == '@')
+                                       q++;
+                               if (state->do_regex) {
+                                       char r;
+                                       for (i = 0; i < state->argc; i++) {
+                                               r = *p;
+                                               *p = '\0';
+                                               if (regexec(&state->pregv[i], 
q, 0, NULL, 0) == 0) {
+                                                       *p = r;
+                                                       v += snprintf(v, w - v, 
"%s%.*s%s%c",
+                                                                       RED, 
(int)(p - s), s, NORM, *p);
+                                                       match = true;
+                                                       break;
+                                               }
+                                               *p = r;
+                                       }
+                               } else {
+                                       for (i = 0; i < state->argc; i++) {
+                                               len = strlen(state->argv[i]);
+                                               if (len == (int)(p - q) &&
+                                                               strncmp(q, 
state->argv[i], len) == 0)
+                                               {
+                                                       v += snprintf(v, w - v, 
"%s%.*s%s%c",
+                                                                       RED, 
(int)(p - s), s, NORM, *p);
+                                                       match = true;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (i == state->argc)
+                                       v += snprintf(v, w - v, "%.*s%c", 
(int)(p - s), s, *p);
+                               q = p + 1;
+                       }
+               } while (*p++ != '\0' && v < w);
+               v = buf;
+       }
 
-               overlay_fd = open(overlay, O_RDONLY|O_CLOEXEC|O_PATH);
+       if (match) {
+               if (quiet) {
+                       printf("%s%s/%s%s%s\n", BOLD, pkg_ctx->cat_ctx->name,
+                                       BLUE, pkg_ctx->name, NORM);
+               } else if (verbose) {
+                       /* multi-line result, printing USE-flags with their 
descs */
+                       struct quse_state us = {
+                               .do_regex = false,
+                               .do_describe = false,
+                               .do_list = true,
+                               .match = atom,
+                               .argc = 1,
+                               .argv = NULL,
+                               .overlay = NULL,
+                       };
+
+                       printf("%s%s/%s%s%s:\n", BOLD, pkg_ctx->cat_ctx->name,
+                                       BLUE, pkg_ctx->name, NORM);
+
+                       q = p = state->do_licence ? meta->LICENSE : meta->IUSE;
+                       buf[0] = '\0';
+                       do {
+                               if (*p == ' ' || *p == '\0') {
+                                       s = q;
+                                       if (*q == '-' || *q == '+' || *q == '@')
+                                               q++;
+
+                                       snprintf(buf, sizeof(buf), "%.*s", 
(int)(p - q), q);
+                                       v = buf;
+                                       us.argv = &v;
+
+                                       /* print at most one match for each 
flag, this is
+                                        * why we can't setup all flags in 
argc/argv,
+                                        * because then we either print way to 
few, or way
+                                        * too many, possible opt: when argv 
would be
+                                        * modified by search funcs so they 
remove what they
+                                        * matched */
+                                       if 
(!quse_search_use_local_desc(portdirfd, &us))
+                                               if 
(!quse_search_use_desc(portdirfd, &us))
+                                                       
quse_search_profiles_desc(portdirfd, &us);
+
+                                       q = p + 1;
+                               }
+                       } while (*p++ != '\0');
+               } else {
+                       printf("%s%s/%s%s%s: %s\n", BOLD, 
pkg_ctx->cat_ctx->name,
+                                       BLUE, pkg_ctx->name, NORM, v);
+               }
+       }
 
-               ebuild = NULL;
-               while ((linelen = getline(&ebuild, &ebuildlen, fp)) >= 0) {
-                       FILE *newfp;
-                       int fd;
+       cache_close_meta(meta);
+       if (state->match || verbose)
+               atom_implode(atom);
+       if (verbose)
+               close(portdirfd);
 
-                       rmspace_len(ebuild, (size_t)linelen);
+       return EXIT_SUCCESS;
+}
 
-                       fd = openat(overlay_fd, ebuild, O_RDONLY|O_CLOEXEC);
-                       if (fd < 0)
-                               continue;
-                       newfp = fdopen(fd, "r");
-                       if (newfp != NULL) {
-                               unsigned int lineno = 0;
+int quse_main(int argc, char **argv)
+{
+       int i;
+       size_t n;
+       const char *overlay;
+       char *match = NULL;
+       struct quse_state state = {
+               .do_all = false,
+               .do_regex = true,
+               .do_describe = false,
+               .do_licence = false,
+               .match = NULL,
+               .overlay = NULL,
+       };
 
-                               while (fgets(buf0, sizeof(buf0), newfp) != 
NULL) {
-                                       int ok = 0;
-                                       char warned = 0;
-                                       lineno++;
+       while ((i = GETOPT_LONG(QUSE, quse, "")) != -1) {
+               switch (i) {
+               case 'e': state.do_regex = false;   break;
+               case 'a': state.do_all = true;      break;
+               case 'L': state.do_licence = true;  break;
+               case 'D': state.do_describe = true; break;
+               case 'p': match = optarg;           break;
+               COMMON_GETOPTS_CASES(quse)
+               }
+       }
+       if (argc == optind && !state.do_all) {
+               if (match != NULL) {
+                       /* default to printing everything if just package is 
given */
+                       state.do_all = true;
+               } else {
+                       quse_usage(EXIT_FAILURE);
+               }
+       }
 
-                                       if (strncmp(buf0, search_vars[idx], 
search_len) != 0)
-                                               continue;
+       state.argc = argc - optind;
+       state.argv = &argv[optind];
 
-                                       if ((p = strchr(buf0, '\n')) != NULL)
-                                               *p = 0;
-                                       if ((p = strchr(buf0, '#')) != NULL) {
-                                               if (buf0 != p && p[-1] == ' ')
-                                                       p[-1] = 0;
-                                               else
-                                                       *p = 0;
-                                       }
-                                       if (verbose > 1) {
-                                               if ((strchr(buf0, '\t') != NULL)
-                                                   || (strchr(buf0, '$') != 
NULL)
-                                                   || (strchr(buf0, '\\') != 
NULL)
-                                                   || (strchr(buf0, '\'') != 
NULL)
-                                                   || (strstr(buf0, "  ") != 
NULL)) {
-                                                       warned = 1;
-                                                       warn("# Line %d of %s 
has an annoying %s",
-                                                               lineno, ebuild, 
buf0);
-                                               }
-                                       }
-#ifdef THIS_SUCKS
-                                       if ((p = strrchr(&buf0[search_len + 1], 
'\\')) != NULL) {
-
-                                       multiline:
-                                               *p = ' ';
-
-                                               if (fgets(buf1, sizeof(buf1), 
newfp) == NULL)
-                                                       continue;
-                                               lineno++;
-
-                                               if ((p = strchr(buf1, '\n')) != 
NULL)
-                                                       *p = 0;
-                                               snprintf(buf2, sizeof(buf2), 
"%s %s", buf0, buf1);
-                                               remove_extra_space(buf2);
-                                               strcpy(buf0, buf2);
-                                               if ((p = strrchr(buf1, '\\')) 
!= NULL)
-                                                       goto multiline;
-                                       }
-#else
-                                       remove_extra_space(buf0);
-#endif
-                                       while ((p = strrchr(&buf0[search_len + 
1], '"')) != NULL)  *p = 0;
-                                       while ((p = strrchr(&buf0[search_len + 
1], '\'')) != NULL) *p = 0;
-                                       while ((p = strrchr(&buf0[search_len + 
1], '\\')) != NULL) *p = ' ';
-
-                                       if (verbose && warned == 0) {
-                                               if ((strchr(buf0, '$') != NULL) 
|| (strchr(buf0, '\\') != NULL)) {
-                                                       warned = 1;
-                                                       warn("# Line %d of %s 
has an annoying %s",
-                                                               lineno, ebuild, 
buf0);
-                                               }
-                                       }
+       if (match != NULL) {
+               state.match = atom_explode(match);
+               if (state.match == NULL)
+                       errf("invalid atom: %s", match);
+       }
 
-                                       if (strlen(buf0) < search_len + 1) {
-                                               /* warnf("err '%s'/%zu <= %zu; 
line %u\n", buf0, strlen(buf0), search_len + 1, lineno); */
-                                               continue;
-                                       }
+       if (state.do_regex) {
+               state.pregv = xmalloc(sizeof(state.pregv[0]) * state.argc);
+               for (i = 0; i < state.argc; i++)
+                       xregcomp(&state.pregv[i], state.argv[i], REG_EXTENDED | 
REG_NOSUB);
+       }
 
-                                       if ((argc == optind) || (quse_all)) {
-                                               ok = 1;
-                                       } else {
-                                               ok = 0;
-                                               if (regexp_matching) {
-                                                       for (i = optind; i < 
argc; ++i) {
-                                                               if 
(rematch(argv[i], &buf0[search_len + 1], REG_NOSUB) == 0) {
-                                                                       ok = 1;
-                                                                       break;
-                                                               }
-                                                       }
-                                               } else {
-                                                       
remove_extra_space(buf0);
-                                                       strcpy(buf1, 
&buf0[search_len + 1]);
-
-                                                       for (i = (size_t) 
optind; i < argc && argv[i] != NULL; i++) {
-                                                               if 
(strcmp(buf1, argv[i]) == 0) {
-                                                                       ok = 1;
-                                                                       break;
-                                                               }
-                                                       }
-                                                       if (ok == 0) while ((p 
= strchr(buf1, ' ')) != NULL) {
-                                                               *p = 0;
-                                                               for (i = 
(size_t) optind; i < argc && argv[i] != NULL; i++) {
-                                                                       if 
(strcmp(buf1, argv[i]) == 0) {
-                                                                               
ok = 1;
-                                                                               
break;
-                                                                       }
-                                                               }
-                                                               strcpy(buf2, p 
+ 1);
-                                                               strcpy(buf1, 
buf2);
-                                                               if 
(strchr(buf1, ' ') == NULL)
-                                                                       for (i 
= (size_t) optind; i < argc && argv[i] != NULL; i++) {
-                                                                               
if (strcmp(buf1, argv[i]) == 0)
-                                                                               
        ok = 1;
-                                                                       }
-                                                       }
-                                               }
-                                       }
-                                       if (ok) {
-                                               printf("%s%s%s ", CYAN, ebuild, 
NORM);
-                                               
print_highlighted_use_flags(&buf0[search_len + 1],
-                                                               optind, argc, 
argv);
-                                               puts(NORM);
-                                               if (verbose > 1) {
-                                                       char **ARGV;
-                                                       int ARGC;
-                                                       
makeargv(&buf0[search_len + 1], &ARGC, &ARGV);
-                                                       
quse_describe_flag(overlay, 1, ARGC, ARGV);
-                                                       freeargv(ARGC, ARGV);
-                                               }
-                                       }
-                                       break;
-                               }
-                               fclose(newfp);
-                       } else {
-                               warnf("missing cache, please (re)generate");
-                       }
+       if (state.do_describe) {
+               array_for_each(overlays, n, overlay)
+                       quse_describe_flag(portroot, overlay, &state);
+       } else {
+               array_for_each(overlays, n, overlay) {
+                       state.overlay = overlay;
+                       cache_foreach_pkg_sorted(portroot, overlay,
+                                       quse_results_cb, &state, NULL, NULL);
                }
-               fclose(fp);
-               close(overlay_fd);
        }
 
+       if (state.do_regex) {
+               for (i = 0; i < state.argc; i++)
+                       regfree(&state.pregv[i]);
+               free(state.pregv);
+       }
+
+       if (state.match != NULL)
+               atom_implode(state.match);
+
        return EXIT_SUCCESS;
 }

diff --git a/tests/quse/dotest b/tests/quse/dotest
index 0dadd8f..ed0511d 100755
--- a/tests/quse/dotest
+++ b/tests/quse/dotest
@@ -13,30 +13,16 @@ entries() {
        sed -e 's:#.*::' -e '/^$/d' "$1"
 }
 
-# check arch.list
-f="$d/arch.list"
-all=$(entries "$f")
-for x in ${all} ; do
-       quse -CD $x > x
-       echo " arch:$x: $x architecture" > good
-       cat good >> all.good
-       diff -u x good
-done
-quse -CD ${all} > x
-diff -u x all.good
-rm x good all.good
-tpass "arch.list"
-
 # check use.desc
 f="$d/use.desc"
 all=$(entries "$f" | awk '{print $1}')
 for x in ${all} ; do
-       quse -CD $x > x
-       sed -n -e "/^$x - /{s|^[^ ]* - | global:$x: |;p}" "$f" > good
+       quse -eCD $x > x
+       sed -n -e "/^$x - /{s|^[^ ]* - |global[$x] |;p}" "$f" > good
        cat good >> all.good
        diff -u x good
 done
-quse -CD ${all} > x
+quse -eCD ${all} > x
 diff -u x all.good
 rm x good all.good
 tpass "use.desc"
@@ -45,12 +31,12 @@ tpass "use.desc"
 f="$d/use.local.desc"
 all=$(entries "$f" | awk '{print $1}' | cut -f2 -d:)
 for x in ${all} ; do
-       quse -CD $x > x
-       sed -n -e "/^[^:]*:$x - /{s|^\([^:]*\):[^ ]* - | local:$x:\1: |;p}" 
"$f" > good
+       quse -eCD $x > x
+       sed -n -e "/^[^:]*:$x - /{s|^\([^:]*\):[^ ]* - |\1[$x] |;p}" "$f" > good
        cat good >> all.good
        diff -u x good
 done
-quse -CD ${all} > x
+quse -eCD ${all} > x
 diff -u x all.good
 rm x good all.good
 tpass "use.local.desc"
@@ -59,14 +45,15 @@ tpass "use.local.desc"
 f="$d/desc/elibc.desc"
 all=$(entries "$f" | awk '{print $1}')
 for x in ${all} ; do
-       quse -CD $x > x
+       quse -eCD $x > x
        dispf=${f##*/}
        dispf=${dispf%.desc}
-       sed -n -e "/^$x - /{s|^[^ ]* - | ${dispf}:$x: |;p}" "$f" > good
+       dispf=${dispf^^}
+       sed -n -e "/^$x - /{s|^[^ ]* - |${dispf}[$x] |;p}" "$f" > good
        cat good >> all.good
        diff -u x good
 done
-quse -CD ${all} > x
+quse -eCD ${all} > x
 diff -u x all.good
 rm x good all.good
 tpass "desc/elibc.desc"
@@ -82,25 +69,3 @@ tpass "multi file match"
 
 cleantmpdir
 
-###### faster test needed.########
-end
-##################################
-type -p mksquashfs || exit 0
-
-export PORTDIR=$(portageq envvar PORTDIR) ;
-
-arches="x86 amd64 ppc"
-
-for arch in $arches; do
-       mkdir -p PORTDIR-${arch}
-       cd PORTDIR-${arch} || exit 1
-       [[ ! -e arch.${arch} ]] && quse -CKe ${arch} > arch.${arch} ;
-       awk '{print $1}' < arch.${arch} | cut -d / -f 1-2 | uniq | while read 
cpn ; do mkdir -p $cpn ; done ; 
-       for ebuild in $(awk '{print $1}' < arch.${arch}) ; do cp 
$PORTDIR/$ebuild ./$ebuild; done ;
-       quse -CKe ${arch} | awk '{print $1}' | cut -d / -f 1-2 | uniq | while 
read cpn ; do cp -a $PORTDIR/$cpn/files ./$cpn/; done ;
-       cp -a $PORTDIR/{eclass,profiles,licences} . ; sync ; 
-       mksquashfs ./ ../PORTDIR-${arch}.squashfs -noappend -e arch.${arch} ; 
-       rm -rf *-*
-       cd -
-done
-ls -lh PORTDIR-*.squashfs

diff --git a/tests/quse/list01.good b/tests/quse/list01.good
index 5a2172e..ae16f87 100644
--- a/tests/quse/list01.good
+++ b/tests/quse/list01.good
@@ -1,2 +1,2 @@
- one:abc: cow
- two:abc: pig
+ONE[abc] cow
+TWO[abc] pig

Reply via email to