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