commit 761b1a1df2fe8384a75c56910a4b67a0c764942c
parent f76829ecbe17f4e1f094d1fd51ac107d33d9c22f
Author: Jacob R. Edwards <n/a>
Date: Sun, 11 Sep 2022 14:01:20 -0700
Make easy and efficient queue searching functions
These new searching functions keep a state which allows for many
more possibilities, like the now revived numeric patterns. It's
also more efficient as the pattern string is only parsed once.
Diffstat:
8 files changed, 224 insertions(+), 84 deletions(-)
diff --git a/aps/Makefile b/aps/Makefile
@@ -1,6 +1,6 @@
name = aps
-src = aps.c arg.c asplit.c bug.c command.c find.c log.c main.c match.c \
- player.c queue.c response.c split.c
+src = aps.c arg.c asplit.c bug.c command.c find.c log.c main.c player.c \
+ queue.c response.c split.c
inc = ${src:.c=.h} util.h
ldlibs = ../lib/ap/libap.a
cflags = -I../lib
diff --git a/aps/aps.c b/aps/aps.c
@@ -23,6 +23,7 @@
#include <errno.h>
#include <limits.h>
#include <poll.h>
+#include <regex.h>
#include <stdlib.h>
#include <string.h>
@@ -261,7 +262,7 @@ aps_seek(struct aps *aps, struct item *(*incr)(struct item *), char *pattern)
{
struct item *item;
- item = find(aps->queue, aps->queue, incr, pattern);
+ item = find(pattern, aps->queue, NULL);
if (item == NULL)
return 1;
queue_set(aps, item);
diff --git a/aps/command.c b/aps/command.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <limits.h>
#include <poll.h>
+#include <regex.h>
#include <stdlib.h>
#include <string.h>
@@ -31,7 +32,6 @@
#include "command.h"
#include "find.h"
#include "log.h"
-#include "match.h"
#include "queue.h"
#include "util.h"
@@ -51,26 +51,28 @@ char *
com_remove(struct aps *aps, int fd, int argc, char **argv)
{
struct item *item;
+ struct search search;
+ char *err;
if (!argc) {
queue_remove(aps, aps->queue);
return NULL;
}
- item = aps->queue;
- while (item) {
- if (match(item->path, *argv)) {
- if (item == aps->queue) {
- item = queue_remove(aps, item);
- } else {
- item = queue_remove(aps, item);
- if (item == aps->queue)
- item = NULL;
- }
- } else if (item->next != aps->queue)
- item = item->next;
- else
- item = NULL;
+ err = prepsearch(&search, aps->queue, *argv);
+ if (err)
+ return err;
+
+ while ((item = findnext(&search))) {
+ if (item == aps->queue) {
+ item = queue_remove(aps, item);
+ } else {
+ item = queue_remove(aps, item);
+ if (item == aps->queue)
+ item = NULL;
+ }
+ if (item == NULL)
+ break;
}
return NULL;
@@ -80,15 +82,21 @@ char *
com_list(struct aps *aps, int fd, int argc, char **argv)
{
struct item *item;
+ struct search search;
+ char *err;
- if ((item = aps->queue) == NULL)
+ err = prepsearch(&search, aps->queue, argc ? *argv : "//");
+ if (err)
+ return err;
+
+ if (aps->queue == NULL)
return NULL;
- do {
- if (!argc || match(item->path, *argv))
- if (respadd(aps, fd, item->path))
- return errstr;
- } while ((item = item->next) != aps->queue);
+ while ((item = findnext(&search))) {
+ if (respadd(aps, fd, item->path))
+ return errstr;
+ }
+ stopsearch(&search);
return NULL;
}
@@ -98,7 +106,7 @@ com_seek(struct aps *aps, int fd, int argc, char **argv)
{
if (!argc)
return "No pattern";
- if (aps_seek(aps, findnext, *argv))
+ if (aps_seek(aps, nextitem, *argv))
return errstr;
return NULL;
}
diff --git a/aps/find.c b/aps/find.c
@@ -19,33 +19,187 @@
#include <ap/item.h>
-#include <stddef.h>
+#include <errno.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bug.h"
#include "find.h"
-#include "match.h"
+#include "util.h"
struct item *
-findnext(struct item *item)
+nextitem(struct item *item)
{
return item->next;
}
+
struct item *
-findprev(struct item *item)
+previtem(struct item *item)
{
return item->prev;
}
-struct item *
-find(struct item *start, struct item *stop, struct item *(*incr)(struct item *), char *pattern)
+char *
+findnum(char *str, int *i, char **ep)
+{
+ long n;
+
+ errno = 0;
+ n = strtol(str, ep, 10);
+ if (errno)
+ return errstr;
+ if (n < INT_MIN || n > INT_MAX)
+ return "Number too large";
+ *i = n;
+ return NULL;
+}
+
+void
+renewsearch(struct search *state, struct item *start)
+{
+ state->cur = start;
+ state->end = start;
+ state->fin = 0;
+ state->new = 1;
+ if (!state->reg) {
+ state->u.num.ind = 0;
+ state->u.num.rev = (state->u.num.min < 0);
+ }
+}
+
+char *
+prepsearch(struct search *state, struct item *start, char *pattern)
{
- struct item *i;
+ static char err[128];
+ char reg[1024];
+ int rcode;
+ char *p;
+ char *ep;
+ char c;
- for (i = incr(start);; i = incr(i)) {
- if (!pattern || match(i->path, pattern))
- return i;
- if (i == stop)
- break;
+ if (!pattern || !pattern[0]) {
+ return "Empty pattern";
+ } else if (*pattern == '/' || *pattern == '?') {
+ c = *pattern;
+ pattern += 1;
+ p = strrchr(pattern, c);
+ if (!p || p[1])
+ return "Unterminated regular expression";
+ if (p - pattern >= sizeof(reg))
+ return "Regular expression too long";
+ memcpy(reg, pattern, p - pattern);
+ reg[p - pattern] = 0;
+ if (!reg[0])
+ strcpy(reg, "^");
+ if ((rcode = regcomp(&state->u.reg, reg, REG_EXTENDED))) {
+ regerror(rcode, &state->u.reg, err, sizeof(err));
+ return err;
+ }
+ state->inc = (c == '/') ? nextitem : previtem;
+ state->reg = 1;
+ } else if (pattern[0]) {
+ if ((ep = findnum(pattern, &state->u.num.min, &p)))
+ return ep;
+ if (p[0] == ',') {
+ if ((ep = findnum(p + 1, &state->u.num.max, &p)))
+ return ep;
+ if (p[0])
+ return "Invalid character in numeric pattern";
+ if (state->u.num.max < state->u.num.min)
+ return "Range end is smaller than beginning";
+ } else if (p[0])
+ return "Invalid character in numeric pattern";
+ else
+ state->u.num.max = state->u.num.min;
+ if (state->u.num.min < 0 && state->u.num.max > 0) {
+ /* Since I'm not sure how I want these to
+ * behave yet, they're not allowed. The
+ * question is how duplicates should be
+ * handled?
+ */
+ return "Ranges spanning from negative to positive indexes not yet implemented";
+ }
+ state->inc = nextitem;
+ state->reg = 0;
}
+
+ renewsearch(state, start);
return NULL;
}
+
+void
+stopsearch(struct search *state)
+{
+ if (state->reg)
+ regfree(&state->u.reg);
+}
+
+struct item *
+findnext(struct search *state)
+{
+ struct item *match;
+
+ if (state->fin)
+ return NULL;
+
+ if (!state->reg && state->u.num.rev) {
+ while (state->u.num.ind > state->u.num.min) {
+ state->cur = previtem(state->cur);
+ --state->u.num.ind;
+ if (state->cur == state->end)
+ break;
+ }
+ state->u.num.rev = 0;
+ state->end = state->cur;
+ }
+
+ /* NOTE: state->inc cannot return NULL */
+ for (match = NULL; !match && !state->fin; state->cur = state->inc(state->cur)) {
+ if (!state->new && state->cur == state->end) {
+ state->fin = 1;
+ } else if (state->reg) { /* One could do something like "match = state->match(state->cur);" instead. */
+ switch (regexec(&state->u.reg, state->cur->path, 0, NULL, 0)) {
+ case 0:
+ match = state->cur;
+ break;
+ case REG_NOMATCH:
+ break;
+ default:
+ bug("Regex failed to execute");
+ }
+ } else {
+ if (state->u.num.ind > state->u.num.max)
+ state->fin = 1;
+ else if (state->u.num.ind >= state->u.num.min)
+ match = state->cur;
+ /* Placement determines if indexing starts from 0 or 1 */
+ state->u.num.ind += 1;
+ }
+
+ if (state->new)
+ state->new = 0;
+ }
+
+ return match;
+}
+
+struct item *
+find(char *pattern, struct item *start, char **ep)
+{
+ struct item *match;
+ struct search search;
+ char *err;
+
+ err = prepsearch(&search, start, pattern);
+ if (err) {
+ if (ep)
+ *ep = err;
+ return NULL;
+ }
+ match = findnext(&search);
+ stopsearch(&search);
+ return match;
+}
diff --git a/aps/find.h b/aps/find.h
@@ -1,3 +1,23 @@
-struct item *findnext(struct item *);
-struct item *findprev(struct item *);
-struct item *find(struct item *, struct item *, struct item *(*)(struct item *), char *);
+struct search {
+ int fin;
+ int new;
+ int reg;
+ struct item *(*inc)(struct item *);
+ struct item *cur, *end;
+ union {
+ regex_t reg;
+ struct {
+ int ind;
+ int min, max;
+ int rev, ign;
+ } num;
+ } u;
+};
+
+struct item *nextitem(struct item *);
+struct item *previtem(struct item *);
+void renewsearch(struct search *, struct item *);
+char *prepsearch(struct search *, struct item *, char *);
+void stopsearch(struct search *);
+struct item *findnext(struct search *);
+struct item *find(char *, struct item *, char **);
diff --git a/aps/main.c b/aps/main.c
@@ -23,6 +23,7 @@
#include <errno.h>
#include <limits.h>
#include <poll.h>
+#include <regex.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/aps/match.c b/aps/match.c
@@ -1,43 +0,0 @@
-/*
- * Copyright 2021 Jacob R. Edwards
- *
- * ap -- audio player
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#include <regex.h>
-
-int
-regmatch(char *s, char *pattern)
-{
- int code;
- regex_t reg;
-
- if (regcomp(®, pattern, REG_EXTENDED))
- return 0;
- code = regexec(®, s, 0, (void *)0, 0);
- regfree(®);
- return code == 0;
-}
-
-int
-match(char *s, char *pattern)
-{
- if (pattern[0] == '!')
- return !regmatch(s, pattern + 1);
- if (pattern[0] == '\\' && pattern[1] != '\\')
- ++pattern;
- return regmatch(s, pattern);
-}
diff --git a/aps/match.h b/aps/match.h
@@ -1 +0,0 @@
-int match(char *, char *);