ap

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

commit 2cf612b0e6892efa2b8254865833c403544e2678
parent 4d23b52f42c1843b5ef06710299d3ea4897a2f1a
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date:   Sat, 28 Aug 2021 23:12:32 -0700

Improve client library

A client struct was added which allows the library to manage arguments
and an input buffer itself. It's also not much bigger since the two
major components were taken from aps.

Some functions should be put into a unique namespace so as not to
conflict. Additionally some functions should be moved to their own
files in case they are not used.

The apc client was, of course, rewritten to use the new interface.
Additionally instead of running the command for each line of input
append each line as arguments to the command. This makes the truncate
command useful (and is also more efficient).

Diffstat:
Mapc/apc.c | 157++++++++++++++++++++++++++++---------------------------------------------------
Dapc/die.c | 27---------------------------
Dapc/die.h | 1-
Mapc/mkfile | 4++--
Maps/aps.h | 2--
Daps/buf.c | 68--------------------------------------------------------------------
Daps/buf.h | 10----------
Daps/item.c | 82-------------------------------------------------------------------------------
Maps/mkfile | 4++--
Minclude/ap/ap.h | 6+++++-
Ainclude/ap/buf.h | 10++++++++++
Minclude/ap/client.h | 17+++++++++++++++--
Raps/item.h -> include/ap/item.h | 0
Ainclude/ap/quote.h | 1+
Alib/ap/buf.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/ap/client.c | 203++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mlib/ap/con.c | 2+-
Alib/ap/item.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/ap/mkfile | 4++--
Alib/ap/quote.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
20 files changed, 412 insertions(+), 386 deletions(-)

diff --git a/apc/apc.c b/apc/apc.c @@ -18,141 +18,94 @@ #include <sys/socket.h> -#include <errno.h> -#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef __OpenBSD__ #include <unistd.h> +#else +#define pledge(a,b) (0) #endif -#include <ap.h> - -#include "die.h" +#include <ap/ap.h> static char subchr = '^'; -int -mksend(int sock, char **argv, int argc) +void +die(char *s) { - char buf[ARG_MAX]; - unsigned int len; - - len = apc_genbuf(buf, sizeof(buf), argv, argc); - if (len == 0) { - errno = E2BIG; - return 0; - } - return send(sock, buf, len, 0) != len; + perror(s); + exit(1); } int -run(FILE *bfd, char **argv, int argc) +main(int argc, char *argv[]) { + struct apc *apc; + int insert; + int i; char *buf; - char *status; - size_t size; ssize_t len; - - if (argc == 0) { - errno = EINVAL; - return 1; - } - - if (mksend(fileno(bfd), argv, argc)) - return 1; - - buf = NULL; - size = 0; - while ((len = getline(&buf, &size, bfd)) != -1) { - if (len && buf[len - 1] == '\n') - buf[--len] = 0; - if ((status = apc_status(buf))) { - if (strcmp(status, "ok") != 0) { - errno = 0; - fprintf(stderr, "aps: %s\n", status); - free(buf); - return 1; - } - free(buf); - return 0; - } else { - if (puts(buf) < 0) { - free(buf); - return 1; - } - } - } - - free(buf); - return ferror(bfd); -} - -int -main(int argc, char **argv) -{ - FILE *bfd; - char *buf; - char *path; - int i; - int sock; - int status; - int sub; size_t size; - ssize_t len; - - if (argc <= 1) - die("no command"); - path = apfile(NULL); - if (path == NULL) - die("apfile"); + apc = apc_new(NULL); + if (apc == NULL) + die("create client"); - sock = sopen(path, connect, SOCK_STREAM); - if (sock == -1) - die(path); - free(path); - -#ifdef __OpenBSD__ if (pledge("stdio", NULL)) die("pledge"); -#endif - bfd = fdopen(sock, "w+"); - if (bfd == NULL) - die("fdopen"); - - sub = 0; - for (i = 1; !sub && i < argc; ++i) { - if (argv[i][0] == subchr) { - if (argv[i][1] == '\0') - sub = i; - else if (argv[i][1] == subchr) - ++argv[i]; + --argc; + ++argv; + + if (argc) { + insert = 0; + if (argv[argc - 1][0] == subchr) { + if (argv[argc - 1][1] == '\0') { + insert = 1; + --argc; + } else if (argv[argc - 1][1] == subchr) + ++argv[argc - 1]; } } - if (!sub) { - status = run(bfd, argv + 1, argc - 1); - } else { + for (i = 0; i < argc; ++i) { + if (apc_add(apc, argv[i])) + die("add argument"); + } + + if (insert) { buf = NULL; size = 0; - status = 0; - while (status == 0 && - (len = getline(&buf, &size, stdin)) != -1) { + while ((len = getline(&buf, &size, stdin)) >= 0) { if (len && buf[len - 1] == '\n') buf[--len] = 0; - argv[sub] = buf; - status = run(bfd, argv + 1, argc - 1); + if (apc_add(apc, buf)) + die("add argument"); } free(buf); + if (ferror(stdin)) + die("stdin"); + } + + if (apc_write(apc)) + die("write"); + + while ((buf = apc_read(apc))) { + puts(buf); + free(buf); + } + if (apc->status == NULL) + die("read"); + + if (strcmp(apc->status, "ok") != 0) { + fprintf(stderr, "aps: %s\n", apc->status); + exit(1); } - if (shutdown(fileno(bfd), SHUT_RDWR) || fclose(bfd)) - die("unable to close connection"); - if (status && errno) - die(NULL); - return status; + apc_free(apc); + + return 0; } + diff --git a/apc/die.c b/apc/die.c @@ -1,27 +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 <stdio.h> -#include <stdlib.h> - -void -die(char *s) -{ - perror(s); - exit(1); -} diff --git a/apc/die.h b/apc/die.h @@ -1 +0,0 @@ -void die(char *); diff --git a/apc/mkfile b/apc/mkfile @@ -1,12 +1,12 @@ # Copyright 2021 Jacob R. Edwards name = apc -src = apc.c die.c +src = apc.c obj = ${src:%.c=%.o} lib = ap mk = ../mk -cppflags = ${lib:%=-I../include/%} +cppflags = -I../include/ ldflags = ${lib:%=-L../lib/%} ldlibs = ${lib:%=-l%} diff --git a/aps/aps.h b/aps/aps.h @@ -2,8 +2,6 @@ struct aps; -#include "buf.h" -#include "item.h" #include "player.h" #include "response.h" diff --git a/aps/buf.c b/aps/buf.c @@ -1,68 +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 <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "buf.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) { - errno = EINVAL; - return 1; - } - - 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 @@ -1,10 +0,0 @@ -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/item.c b/aps/item.c @@ -1,82 +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 <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "item.h" -#include "util.h" - -struct item * -item_new(char *path) -{ - struct item *item; - - item = calloc(1, sizeof(*item)); - if (item == NULL) - return NULL; - - item->path = strdup(path); - if (item->path == NULL) { - free(item); - return NULL; - } - - return item; -} - -void -item_free(struct item *item) -{ - if (item) { - free(item->path); - free(item); - } -} - -int -item_insert(struct item *list, struct item *item) -{ - if (list == NULL || item == NULL || item->next || item->prev) { - errno = EINVAL; - return 1; - } - - if (list->prev == NULL) { - ASSERT(list->next == NULL); - list->next = list->prev = item; - item->next = item->prev = list; - } else { - list->prev->next = item; - item->prev = list->prev; - item->next = list; - list->prev = item; - } - - return 0; -} - -void -item_unlink(struct item *item) -{ - if (item->prev) - item->prev->next = item->next; - if (item->next) - item->next->prev = item->prev; -} diff --git a/aps/mkfile b/aps/mkfile @@ -1,8 +1,8 @@ # Copyright 2021 Jacob R. Edwards name = aps -src = aps.c arg.c asplit.c buf.c bug.c command.c find.c item.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\ + match.c player.c queue.c response.c split.c obj = ${src:%.c=%.o} lib = ap mk = ../mk diff --git a/include/ap/ap.h b/include/ap/ap.h @@ -1,6 +1,10 @@ #define AP_QUOTE '\'' #define AP_ARG_SEPS " \t" -#include "client.h" +#include "buf.h" #include "con.h" +#include "item.h" +#include "quote.h" #include "sock.h" + +#include "client.h" diff --git a/include/ap/buf.h b/include/ap/buf.h @@ -0,0 +1,10 @@ +struct buf { + unsigned int len; + unsigned int size; + char *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/include/ap/client.h b/include/ap/client.h @@ -1,2 +1,15 @@ -size_t apc_genbuf(void *, size_t, char **, unsigned int); -char * apc_status(char *); +struct apc { + char *status; + struct apcon *con; + struct buf *buf; + struct item *args; +}; + +void apc_free(struct apc *); +struct apc *apc_new(char *); +int apc_add(struct apc *, char *); +char *apc_msg(struct apc *); +int apc_write(struct apc *); +void apc_clear(struct apc *); +int apc_recv(struct apc *); +char *apc_read(struct apc *); diff --git a/aps/item.h b/include/ap/item.h diff --git a/include/ap/quote.h b/include/ap/quote.h @@ -0,0 +1 @@ +char *apquote(char *); diff --git a/lib/ap/buf.c b/lib/ap/buf.c @@ -0,0 +1,68 @@ +/* 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 <ap/buf.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) { + errno = EINVAL; + return 1; + } + + 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/lib/ap/client.c b/lib/ap/client.c @@ -18,120 +18,155 @@ #include <sys/socket.h> -#include <errno.h> -#include <limits.h> -#include <stdint.h> #include <stdlib.h> #include <string.h> -#include <ap.h> +#include <ap/ap.h> -size_t -apc_memcat(char *dst, size_t dstsize, size_t dstlen, char *src, size_t srclen) +void +apc_free(struct apc *apc) { - if (dstlen > SIZE_MAX - srclen) { - errno = EOVERFLOW; - return 0; - } - if (dstlen + srclen > dstsize) { - errno = ENOBUFS; - return 0; - } - memcpy(dst + dstlen, src, srclen); - return dstlen + srclen; + if (apc == NULL) + return; + + apc_clear(apc); + free(apc->status); + apclose(apc->con); + buffree(apc->buf); + free(apc); } -size_t -apc_strjoin(char *buf, size_t size, char *fs, char **strings, unsigned int nstrings) +struct apc * +apc_new(char *path) { - size_t len; - unsigned int i; - size_t fslen; - - len = 0; - fslen = strlen(fs); - for (i = 0; i < nstrings; ++i) { - if (i > 0) { - len = apc_memcat(buf, size, len, fs, fslen); - if (len == 0) - return 0; - } - len = apc_memcat(buf, size, len, strings[i], strlen(strings[i])); - if (len == 0) - return 0; + struct apc *apc; + + if ((apc = calloc(1, sizeof(*apc))) == NULL || (apc->buf = bufnew()) == NULL || + (apc->con = apopen(path, connect, 0)) == NULL) { + apc_free(apc); + return NULL; } + return apc; +} - return apc_memcat(buf, size, len, "", 1); +int +apc_add(struct apc *apc, char *arg) +{ + if (apc->args == NULL) { + apc->args = item_new(arg); + return apc->args == NULL; + } + return item_insert(apc->args, item_new(arg)); } char * -apc_quote(char *s) +apc_msg(struct apc *apc) { - char *buf, *p; - size_t i; + char *msg; + char *arg; + struct buf *buf; + struct item *itm; - if (!strchr(s, AP_QUOTE) && s[strcspn(s, AP_ARG_SEPS)] == '\0') - return strdup(s); + if (apc->args == NULL) + return NULL; - buf = malloc(strlen(s) * 2 + 3); + buf = bufnew(); if (buf == NULL) return NULL; - p = buf; - *p++ = AP_QUOTE; - for (i = 0; s[i]; ++i) { - if (s[i] == AP_QUOTE) - *p++ = AP_QUOTE; - *p++ = s[i]; - } - *p++ = AP_QUOTE; - *p++ = '\0'; + itm = apc->args; + do { + arg = apquote(itm->path); + if (arg == NULL || bufappend(buf, arg, strlen(arg)) || + bufappend(buf, " ", 1)) { + free(arg); + buffree(buf); + return NULL; + } + free(arg); + } while ((itm = itm->next) != apc->args && itm); - return buf; + if (buf->len) + buf->data[buf->len - 1] = '\n'; + buf->data[buf->len] = '\0'; + + msg = buf->data; + buf->data = NULL; + buffree(buf); + return msg; } -size_t -apc_genbuf(void *buf, size_t size, char **argv, unsigned int argc) +int +apc_write(struct apc *apc) { - char *newargv[argc]; - char sep[2]; - size_t len; - unsigned int i; - - for (i = 0; i < argc; ++i) { - newargv[i] = apc_quote(argv[i]); - if (newargv[i] == NULL) { - len = 0; - goto free; - } - } + char *buf; + int status; + ssize_t len; - strlcpy(sep, AP_ARG_SEPS, sizeof(sep)); - len = apc_strjoin(buf, size, sep, newargv, argc); + buf = apc_msg(apc); + if (buf == NULL) + return 1; -free: - while (i > 0) - free(newargv[--i]); - if (len == 0) - return 0; + len = strlen(buf); + status = send(apc->con->sock, buf, len, 0) != len; + free(buf); + return status; +} + +void +apc_clear(struct apc *apc) +{ + struct item *next; - ((char *)buf)[len - 1] = '\n'; - return len; + while (apc->args) { + next = ((apc->args == apc->args->next) ? NULL : apc->args->next); + item_unlink(apc->args); + item_free(apc->args); + apc->args = next; + } } -char * -apc_status(char *s) +int +apc_recv(struct apc *apc) { - char *ep; - int eplen; + char buf[4096]; + int len; - if (strcmp(s, "ok") == 0) - return s; + len = recv(apc->con->sock, buf, sizeof(buf), 0); + if (len <= 0) + return 1; + return bufappend(apc->buf, buf, len); +} - ep = "error: "; - eplen = strlen(ep); - if (strncmp(s, ep, eplen) == 0) - return s + eplen; +char * +apc_read(struct apc *apc) +{ + unsigned int len; + char *buf, *end; - return NULL; + while ((end = memchr(apc->buf->data, '\n', apc->buf->len)) == NULL && + apc_recv(apc) == 0) + ; + if (end == NULL) + return NULL; + + len = end - apc->buf->data + 1; + buf = malloc(len); + if (buf == NULL) + return NULL; + memcpy(buf, apc->buf->data, len); + buf[len - 1] = 0; + + memmove(apc->buf->data, apc->buf->data + len, apc->buf->len - len); + apc->buf->len -= len; + + if (strcmp(buf, "ok") == 0 || strncmp(buf, "error: ", 7) == 0) { + if (*buf == 'e') + memmove(buf, buf + 7, len - 7); + if (apc->status) + free(apc->status); + apc->status = buf; + return NULL; + } + return buf; } diff --git a/lib/ap/con.c b/lib/ap/con.c @@ -22,7 +22,7 @@ #include <string.h> #include <unistd.h> -#include <ap.h> +#include <ap/ap.h> char * apfile(char *path) diff --git a/lib/ap/item.c b/lib/ap/item.c @@ -0,0 +1,82 @@ +/* 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 <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <ap/item.h> + +struct item * +item_new(char *path) +{ + struct item *item; + + item = calloc(1, sizeof(*item)); + if (item == NULL) + return NULL; + + item->path = strdup(path); + if (item->path == NULL) { + free(item); + return NULL; + } + + return item; +} + +void +item_free(struct item *item) +{ + if (item) { + free(item->path); + free(item); + } +} + +int +item_insert(struct item *list, struct item *item) +{ + if (list == NULL || item == NULL || item->next || item->prev) { + errno = EINVAL; + return 1; + } + + if (list->prev == NULL) { + assert(list->next == NULL); + list->next = list->prev = item; + item->next = item->prev = list; + } else { + list->prev->next = item; + item->prev = list->prev; + item->next = list; + list->prev = item; + } + + return 0; +} + +void +item_unlink(struct item *item) +{ + if (item->prev) + item->prev->next = item->next; + if (item->next) + item->next->prev = item->prev; +} diff --git a/lib/ap/mkfile b/lib/ap/mkfile @@ -1,9 +1,9 @@ # Copyright 2021 Jacob R. Edwards name = libap.a -src = client.c con.c sock.c +src = buf.c client.c con.c item.c quote.c sock.c obj = ${src:%.c=%.o} -hdir = ../../include/ap +hdir = ../../include/ mk = ../../mk cppflags = -I$hdir diff --git a/lib/ap/quote.c b/lib/ap/quote.c @@ -0,0 +1,50 @@ +/* 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 <sys/socket.h> + +#include <stdlib.h> +#include <string.h> + +#include <ap/ap.h> + +char * +apquote(char *s) +{ + char *buf, *p; + size_t i; + + if (!strchr(s, AP_QUOTE) && s[strcspn(s, AP_ARG_SEPS)] == '\0') + return strdup(s); + + buf = malloc(strlen(s) * 2 + 3); + if (buf == NULL) + return NULL; + + p = buf; + *p++ = AP_QUOTE; + for (i = 0; s[i]; ++i) { + if (s[i] == AP_QUOTE) + *p++ = AP_QUOTE; + *p++ = s[i]; + } + *p++ = AP_QUOTE; + *p++ = '\0'; + + return buf; +}