ap

An audio player suited to my tastes
Log | Files | Refs | README | LICENSE

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:
Maps/Makefile | 4++--
Maps/aps.c | 3++-
Maps/command.c | 52++++++++++++++++++++++++++++++----------------------
Maps/find.c | 178+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Maps/find.h | 26+++++++++++++++++++++++---
Maps/main.c | 1+
Daps/match.c | 43-------------------------------------------
Daps/match.h | 1-
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(&reg, pattern, REG_EXTENDED)) - return 0; - code = regexec(&reg, s, 0, (void *)0, 0); - regfree(&reg); - 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 *);