ap

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

commit 494b7acad7b09b0dadcc5a7241920e1f9a41916a
parent e16156d22e3a0acecb586ebc69c3375a821216b8
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date:   Tue, 29 Jun 2021 22:04:15 -0700

Use common buffer code for request and response structures

Diffstat:
Maps/aps.c | 26+++++++++++++++-----------
Maps/aps.h | 10+++++-----
Aaps/buf.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aaps/buf.h | 10++++++++++
Maps/command.c | 97++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Maps/mkfile | 2+-
Maps/response.c | 88++++++++++++++++++++++++-------------------------------------------------------
Maps/response.h | 11++++-------
Maps/util.h | 1+
9 files changed, 185 insertions(+), 127 deletions(-)

diff --git a/aps/aps.c b/aps/aps.c @@ -64,6 +64,16 @@ sigchld(int sig) } int +aps_drop(struct aps *aps, int fd) +{ + --aps->nfds; + aps->pfds[fd].fd = -1; + respfree(aps, fd); + buffree(aps->clients[fd].request); + return sclose(fd); +} + +int aps_accept(struct aps *aps) { int s; @@ -78,23 +88,17 @@ aps_accept(struct aps *aps) return r; while ((s = accept4(aps->con.s, NULL, NULL, 0)) >= 0) { - aps->pfds[s].fd = s; ++aps->nfds; + aps->pfds[s].fd = s; + if ((aps->clients[s].request = bufnew()) == NULL || + respnew(aps, s)) + aps_drop(aps, s); } return errno != EWOULDBLOCK; } int -aps_drop(struct aps *aps, int fd) -{ - aps->pfds[fd].fd = -1; - resp_free(aps, fd); - --aps->nfds; - return sclose(fd); -} - -int aps_handle(struct aps *aps, int fd) { if (aps->pfds[fd].revents & (POLLERR | POLLHUP | POLLNVAL)) { @@ -103,7 +107,7 @@ aps_handle(struct aps *aps, int fd) if (aps_command(aps, fd)) return 1; } else if (aps->pfds[fd].revents & POLLOUT) { - if (aps->clients[fd].response.lock && resp_send(aps, fd)) + if (aps->clients[fd].resp->lock && respond(aps, fd)) return 1; } else { die("unhandled poll revent"); diff --git a/aps/aps.h b/aps/aps.h @@ -1,5 +1,8 @@ #include <ap.h> +struct aps; + +#include "buf.h" #include "player.h" #include "queue.h" #include "response.h" @@ -12,11 +15,8 @@ struct aps { int playing; struct apcon con; struct client { - struct request { - char buf[AP_LINE_MAX]; - int len; - } request; - struct response response; + struct buf *request; + struct response *resp; } clients[OPEN_MAX]; struct player player; struct pollfd pfds[OPEN_MAX]; diff --git a/aps/buf.c b/aps/buf.c @@ -0,0 +1,67 @@ +/* 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 <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "buf.h" +#include "util.h" + +struct buf * +bufnew(void) +{ + return calloc(1, sizeof(struct buf)); +} + +void +buffree(struct buf *buf) +{ + if (buf == NULL) + return; + free(buf->data); + free(buf); +} + +int +bufresize(struct buf *buf, unsigned int newsize) +{ + char *tmp; + + if (buf == NULL || newsize < buf->len) + ERET(EINVAL); + + tmp = realloc(buf->data, newsize); + if (tmp == NULL) + return 1; + + buf->data = tmp; + buf->size = newsize; + return 0; +} + +int +bufappend(struct buf *buf, void *data, unsigned int len) +{ + if ((buf == NULL || buf->size - buf->len < len) && + bufresize(buf, (buf->size * 2) + len) != 0) + return 1; + memcpy(buf->data + buf->len, data, len); + buf->len += len; + return 0; +} diff --git a/aps/buf.h b/aps/buf.h @@ -0,0 +1,10 @@ +struct buf { + unsigned int len; + unsigned int size; + void *data; +}; + +struct buf *bufnew(void); +void buffree(struct buf *); +int bufresize(struct buf *, unsigned int); +int bufappend(struct buf *, void *, unsigned int); diff --git a/aps/command.c b/aps/command.c @@ -35,10 +35,9 @@ static char *earg = "Invalid number of arguments"; static char *enotfound = "Not found"; static char *eposition = "No position in queue"; -static char *etoofew = "Too few arguments"; int -resp_finish(struct aps *aps, int s, char *error) +finish(struct aps *aps, int s, char *error) { char buf[AP_LINE_MAX]; int len; @@ -52,14 +51,15 @@ resp_finish(struct aps *aps, int s, char *error) if (len >= sizeof(buf)) ERET(EMSGSIZE); - if (resp_add(aps, s, buf, len)) + if (bufappend(aps->clients[s].resp->buf, buf, len)) return 1; - resp_end(aps, s); + + aps->clients[s].resp->lock = 1; return 0; } int -resp_additem(struct aps *aps, int s, char *item) +additem(struct aps *aps, int s, char *item) { char buf[AP_LINE_MAX]; int n; @@ -67,7 +67,7 @@ resp_additem(struct aps *aps, int s, char *item) n = snprintf(buf, sizeof(buf), "%s\n", item); if (n >= sizeof(buf)) ERET(EMSGSIZE); - return resp_add(aps, s, buf, n); + return bufappend(aps->clients[s].resp->buf, buf, n); } int @@ -132,7 +132,7 @@ aps_list(struct aps *aps, int s, int argc, char **argv) item = aps->queue.head; while ((item = nextmatch(item_next, item, argv + 1, argc - 1, 1))) { - if (resp_additem(aps, s, item->path)) + if (additem(aps, s, item->path)) return strerror(errno); item = item->next; } @@ -164,7 +164,6 @@ findpat(struct item *(*incr)(struct item *), struct item *item, struct item * find(struct aps *aps, int argc, char **argv, char **err) { - char *e; struct item *(*incr)(struct item *); struct item *item; @@ -267,7 +266,7 @@ aps_name(struct aps *aps, int s, int argc, char **argv) if (item == NULL) return err; - if (resp_additem(aps, s, item->path)) + if (additem(aps, s, item->path)) return strerror(errno); return NULL; } @@ -292,13 +291,40 @@ aps_next(struct aps *aps, int s, int argc, char **argv) return aps_play(aps, -1, aps->nextlen, aps->next); } +char * +aps_read(struct aps *aps, int s) +{ + char *end; + int len; + struct buf *buf; + + buf = aps->clients[s].request; + if (buf->len == buf->size && bufresize(buf, MAX(buf->size, 128) * 2)) { + buf->len = 0; + /* TODO: handle error */ + finish(aps, s, strerror(errno)); + return NULL; + } + + len = recv(s, buf->data + buf->len, buf->size - buf->len, 0); + if (len == -1) + return NULL; + buf->len += len; + + end = memchr(buf->data + (buf->len - len), '\n', len); + if (end == NULL) + return NULL; /* not a full command yet */ + *end = 0; + + return end + 1; +} + int aps_command(struct aps *aps, int s) { - char *end; - char buf[AP_LINE_MAX]; - int argc; - struct client *c; + char *next; + char bufbuf[AP_LINE_MAX]; + struct buf *buf; char *argv[AP_ARGV_MAX]; int i; int len; @@ -318,38 +344,27 @@ aps_command(struct aps *aps, int s) { "toggle", aps_toggle } }; - c = &aps->clients[s]; - if (c->request.len == sizeof(c->request.buf)) { - c->request.len = 0; - return resp_finish(aps, s, strerror(EMSGSIZE)); - } + if ((next = aps_read(aps, s)) == NULL) + return 0; - len = recv(s, c->request.buf + c->request.len, LEN(c->request.buf) - c->request.len, 0); - if (len == -1) - return 1; - - end = memchr(c->request.buf + c->request.len, '\n', len); - c->request.len += len; - if (end == NULL) - return 0; /* not a full command yet */ + buf = aps->clients[s].request; + len = next - (char *)buf->data; + memcpy(bufbuf, buf->data, len); + memmove(buf->data, buf->data + len, buf->len - len); + buf->len -= len; - *end = 0; - len = end - c->request.buf + 1; - memcpy(buf, c->request.buf, len); - memmove(c->request.buf, c->request.buf + len, c->request.len - len); - c->request.len -= len; - - argc = split(argv, LEN(argv), buf, AP_ARG_SEPS); - if (!argc) - return resp_finish(aps, s, "No command"); - if (argc < 0) - return resp_finish(aps, s, strerror(E2BIG)); + len = split(argv, LEN(argv), bufbuf, AP_ARG_SEPS); + if (len == 0) + return finish(aps, s, "No command"); + if (len < 0) + return finish(aps, s, earg); for (i = 0; i < LEN(commands); ++i) { - if (strcmp(*argv, commands[i].name) == 0) - return resp_finish(aps, s, - commands[i].func(aps, s, argc, argv)); + if (strcmp(*argv, commands[i].name) == 0) { + return finish(aps, s, + commands[i].func(aps, s, len, argv)); + } } - return resp_finish(aps, s, "Command not found"); + return finish(aps, s, "Command not found"); } diff --git a/aps/mkfile b/aps/mkfile @@ -1,7 +1,7 @@ # Copyright 2021 Jacob R. Edwards name = aps -src = aps.c command.c player.c queue.c response.c split.c +src = aps.c buf.c command.c player.c queue.c response.c split.c obj = ${src:%.c=%.o} cppflags = -I../lib diff --git a/aps/response.c b/aps/response.c @@ -21,91 +21,55 @@ #include <errno.h> #include <limits.h> #include <poll.h> -#include <stdio.h> #include <stdlib.h> -#include <string.h> #include "aps.h" #include "util.h" +#define RESP (aps->clients[fd].resp) + int -resp_enlarge(struct aps *aps, int fd, unsigned int more) +respnew(struct aps *aps, int fd) { - struct response *resp; - unsigned int newsiz; - void *tmp; - - resp = &aps->clients[fd].response; - if (resp->len + more < resp->len) - ERET(EOVERFLOW); - - newsiz = (resp->siz ? resp->siz * 2 : BUFSIZ); - while (newsiz < resp->len + more) { - if (newsiz *= 2 < resp->siz) - ERET(EOVERFLOW); - } - - tmp = realloc(resp->buf, newsiz); - if (tmp == NULL) { - resp_free(aps, fd); + if ((RESP = calloc(1, sizeof(struct response))) == NULL) + return 1; + if ((RESP->buf = bufnew()) == NULL) { + free(RESP); return 1; } - - resp->buf = tmp; - resp->siz = newsiz; return 0; } -int -resp_add(struct aps *aps, int fd, void *data, unsigned int len) +void +respfree(struct aps *aps, int fd) { - struct response *resp; - - resp = &aps->clients[fd].response; - if (resp->len + len > resp->siz && resp_enlarge(aps, fd, len)) - return 1; - - memcpy(resp->buf + resp->len, data, len); - resp->len += len; - return 0; + if (RESP == NULL) + return; + buffree(RESP->buf); + free(RESP); + RESP = NULL; } int -resp_send(struct aps *aps, int fd) +respond(struct aps *aps, int fd) { ssize_t n; struct response *resp; - resp = &aps->clients[fd].response; - if (!resp->lock || resp->len - resp->sent == 0) + resp = RESP; + if (!resp->lock || resp->buf->len - resp->sent == 0) return 1; - - n = send(fd, resp->buf + resp->sent, resp->len - resp->sent, + n = send(fd, resp->buf->data + resp->sent, resp->buf->len - resp->sent, MSG_NOSIGNAL); - switch (n) { - case -1: + + if (n < 0) return 1; - case 0: + if (n == 0) ERET(ECONNRESET); - default: - if (resp->sent += n == resp->len) - resp_free(aps, fd); - return 0; + if (resp->sent += n == resp->buf->len) { + resp->buf->len = 0; + resp->sent = 0; + resp->lock = 0; } -} - -void -resp_end(struct aps *aps, int fd) -{ - aps->clients[fd].response.lock = 1; -} - -void -resp_free(struct aps *aps, int fd) -{ - struct response *resp; - - resp = &aps->clients[fd].response; - free(resp->buf); - memset(resp, 0, sizeof(*resp)); + return 0; } diff --git a/aps/response.h b/aps/response.h @@ -1,12 +1,9 @@ struct response { int lock; - unsigned int len; + struct buf *buf; unsigned int sent; - unsigned int siz; - void *buf; }; -int resp_add(struct aps *, int, void *, unsigned int); -int resp_send(struct aps *, int); -void resp_end(struct aps *, int); -void resp_free(struct aps *, int); +int respnew(struct aps *, int); +void respfree(struct aps *, int); +int respond(struct aps *, int); diff --git a/aps/util.h b/aps/util.h @@ -1,3 +1,4 @@ #define ERET(E) { errno = E; return 1; } #define ERROR(S) (write(2, S, sizeof(S) - 1) != sizeof(S) - 1) #define LEN(X) (sizeof(X) / sizeof(*X)) +#define MAX(A,B) (A > B ? A : B)