ap

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

commit 80c0a5305057b7fac0249f9e487ddcffedc4b499
parent 4b6abcd3fc0b22e07992f2a381fe4017251cb3d0
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date:   Wed, 25 Aug 2021 18:34:49 -0700

Add rc(1)-style quoting

For clients using the library nothing changes but for raw interaction
it's much more pleasant.

The server gained an ugly, bulky new function to separate and
un-quote strings which simply replaces the strsep call in split.

Diffstat:
Maps/TODO | 3+--
Maps/aps.c | 2+-
Maps/asplit.c | 6+++---
Maps/asplit.h | 2+-
Maps/split.c | 47+++++++++++++++++++++++++++++++++++++++++++++--
Maps/split.h | 2+-
Minclude/ap/ap.h | 3++-
Mlib/ap/client.c | 45+++++++++++++++++++++++++++++++++++++++++++--
8 files changed, 97 insertions(+), 13 deletions(-)

diff --git a/aps/TODO b/aps/TODO @@ -1,4 +1,3 @@ - Use size_t for buffer sizes - Allow 'ok' and 'error: *' items -- Consider separating commands with whitespace, this would require - quoting and escaping + diff --git a/aps/aps.c b/aps/aps.c @@ -204,7 +204,7 @@ aps_command(struct aps *aps, int s) buf = aps->clients[s].request; aps_log(aps, COMMAND, s, buf->data); - argv = asplit(buf->data, AP_ARG_SEPS); + argv = asplit(buf->data, AP_QUOTE, AP_ARG_SEPS); if (argv == NULL) return 1; if (*argv == NULL) { diff --git a/aps/asplit.c b/aps/asplit.c @@ -22,7 +22,7 @@ #include "split.h" char ** -asplit(char *s, char *sep) +asplit(char *s, char quote, char *sep) { char **args, *copy; unsigned int len; @@ -30,12 +30,12 @@ asplit(char *s, char *sep) copy = strdup(s); if (copy == NULL) return NULL; - len = split(NULL, 0, copy, sep); + len = split(NULL, 0, copy, quote, sep); free(copy); args = calloc(++len, sizeof(*args)); if (args == NULL) return NULL; - split(args, len, s, sep); + split(args, len, s, quote, sep); return args; } diff --git a/aps/asplit.h b/aps/asplit.h @@ -1 +1 @@ -char **asplit(char *, char *); +char **asplit(char *, char, char *); diff --git a/aps/split.c b/aps/split.c @@ -20,13 +20,56 @@ #include "util.h" +char * +unquote(char **sp, char quote, char *delim) +{ + char *start; + int quoted; + + while (**sp && strchr(delim, **sp)) + ++*sp; + if (!**sp) + return NULL; + + if (**sp != quote) { + quoted = 0; + } else { + quoted = 1; + ++*sp; + } + start = *sp; + + if (!quoted) { + *sp += strcspn(*sp, delim); + if (*sp) { + **sp = 0; + ++*sp; + } + return start; + } + + for (; **sp; ++*sp) { + if (**sp == quote) { + if ((*sp)[1] != quote) { + **sp = 0; + ++*sp; + return start; + } + /* NOTE: only up to the next argument MUST be moved */ + memmove(*sp, *sp + 1, strlen(*sp)); + } + } + + return start; +} + unsigned int -split(char **ap, unsigned int len, char *s, char *sep) +split(char **ap, unsigned int len, char *s, char quote, char *sep) { char *p; unsigned int i; - for (i = 0; (p = strsep(&s, sep)); ++i) { + for (i = 0; (p = unquote(&s, quote, sep)); ++i) { if (i < len) ap[i] = p; } diff --git a/aps/split.h b/aps/split.h @@ -1 +1 @@ -unsigned int split(char **, unsigned int, char *, char *); +unsigned int split(char **, unsigned int, char *, char, char *); diff --git a/include/ap/ap.h b/include/ap/ap.h @@ -1,4 +1,5 @@ -#define AP_ARG_SEPS "\t" +#define AP_QUOTE '\'' +#define AP_ARG_SEPS " \t" #include "client.h" #include "con.h" diff --git a/lib/ap/client.c b/lib/ap/client.c @@ -21,6 +21,7 @@ #include <errno.h> #include <limits.h> #include <stdint.h> +#include <stdlib.h> #include <string.h> #include <ap.h> @@ -63,16 +64,56 @@ apc_strjoin(char *buf, size_t size, char *fs, char **strings, unsigned int nstri return apc_memcat(buf, size, len, "", 1); } +char * +apc_quote(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; +} + size_t apc_genbuf(void *buf, size_t size, char **argv, unsigned int argc) { + 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; + } + } strlcpy(sep, AP_ARG_SEPS, sizeof(sep)); - len = apc_strjoin(buf, size, sep, argv, argc); + len = apc_strjoin(buf, size, sep, newargv, argc); + +free: + while (i > 0) + free(newargv[--i]); if (len == 0) - return 1; + return 0; ((char *)buf)[len - 1] = '\n'; return len;