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:
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;