commit dd0866460d180722136dfe41b83362825c002e8e
parent 998fd4e66fcc49b732b3fac151c0271d4dc17943
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date: Sun, 5 Sep 2021 17:46:48 -0700
Quote server responses
By quoting server responses 'ok' and 'error: *' can be sent to the
client. Additionally, although I'm unsure how useful it is, raw
responses can be sent back as is.
Diffstat:
11 files changed, 108 insertions(+), 35 deletions(-)
diff --git a/TODO b/TODO
@@ -1,3 +1 @@
-- Allow 'ok' and 'error: *' items
-- Consider quoting return items (which would fix 'ok' and 'error: *' items)
- Use size_t for buffer sizes
diff --git a/aps/aps.c b/aps/aps.c
@@ -19,6 +19,7 @@
#include <sys/socket.h>
+#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <poll.h>
@@ -207,7 +208,7 @@ aps_command(struct aps *aps, int s)
buf = aps->clients[s].request;
aps_logfmt(aps, COMMAND, s, "input '%s'", buf->data);
- argv = asplit(buf->data, AP_QUOTE, AP_ARG_SEPS);
+ argv = asplit(buf->data, isspace);
if (argv == NULL)
return 1;
if (*argv == NULL) {
diff --git a/aps/asplit.c b/aps/asplit.c
@@ -23,7 +23,7 @@
#include "split.h"
char **
-asplit(char *s, char quote, char *sep)
+asplit(char *s, int (*issep)(int))
{
char **args, *copy;
unsigned int len;
@@ -31,12 +31,12 @@ asplit(char *s, char quote, char *sep)
copy = strdup(s);
if (copy == NULL)
return NULL;
- len = split(NULL, 0, copy, quote, sep);
+ len = split(NULL, 0, copy, issep);
free(copy);
args = calloc(++len, sizeof(*args));
if (args == NULL)
return NULL;
- split(args, len, s, quote, sep);
+ split(args, len, s, issep);
return args;
}
diff --git a/aps/asplit.h b/aps/asplit.h
@@ -1 +1 @@
-char **asplit(char *, char, char *);
+char **asplit(char *, int (*)(int));
diff --git a/aps/response.c b/aps/response.c
@@ -55,12 +55,25 @@ respfree(struct aps *aps, int fd)
}
int
+allquote(int c)
+{
+ return 1;
+}
+
+int
respadd(struct aps *aps, int fd, char *item)
{
- if (bufappend(RESP->buf, item, strlen(item)) ||
- bufappend(RESP->buf, "\n", 1))
+ int status;
+
+ item = apquote(item, (strcmp(item, "ok") ? NULL : allquote));
+ if (item == NULL)
return 1;
- return 0;
+
+ status = (bufappend(RESP->buf, item, strlen(item)) ||
+ bufappend(RESP->buf, "\n", 1));
+ free(item);
+
+ return status;
}
int
@@ -68,7 +81,7 @@ respfinish(struct aps *aps, int fd, char *error)
{
if (error == NULL) {
/* NOTE: Perhaps it should return an error? */
- if (respadd(aps, fd, "ok"))
+ if (bufappend(RESP->buf, "ok\n", 3))
respfinish(aps, fd, errstr);
} else {
aps_logfmt(aps, COMMAND, fd, "error '%s'", error);
diff --git a/aps/split.c b/aps/split.c
@@ -22,16 +22,33 @@
#include "util.h"
char *
-unquote(char **sp, char quote, char *delim)
+spanwhile(char *s, int (*func)(int))
+{
+ while (*s && func(*s))
+ ++s;
+ return s;
+}
+
+char *
+spanwhilenot(char *s, int (*func)(int))
+{
+ while (*s && !func(*s))
+ ++s;
+ return s;
+}
+
+/* Unquoting should probably done in only one place... */
+char *
+unquote(char **sp, int (*issep)(int))
{
char *start;
- start = *sp + strspn(*sp, delim);
+ start = spanwhile(*sp, issep);
if (*start == '\0')
return NULL;
- if (*start != quote) {
- *sp = start + strcspn(start, delim);
+ if (*start != '\'') {
+ *sp = spanwhilenot(start, issep);
if (**sp != '\0') {
**sp = 0;
++*sp;
@@ -44,8 +61,8 @@ unquote(char **sp, char quote, char *delim)
return NULL;
for (; **sp; ++*sp) {
- if (**sp == quote) {
- if ((*sp)[1] != quote) {
+ if (**sp == '\'') {
+ if ((*sp)[1] != '\'') {
**sp = 0;
++*sp;
return start;
@@ -59,12 +76,12 @@ unquote(char **sp, char quote, char *delim)
}
unsigned int
-split(char **ap, unsigned int len, char *s, char quote, char *sep)
+split(char **ap, unsigned int len, char *s, int (*sep)(int))
{
char *p;
unsigned int i;
- for (i = 0; (p = unquote(&s, quote, sep)); ++i) {
+ for (i = 0; (p = unquote(&s, 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, char *);
+unsigned int split(char **, unsigned int, char *, int (*)(int));
diff --git a/include/ap/ap.h b/include/ap/ap.h
@@ -1,6 +1,3 @@
-#define AP_QUOTE '\''
-#define AP_ARG_SEPS " \t"
-
#include "buf.h"
#include "con.h"
#include "item.h"
diff --git a/include/ap/quote.h b/include/ap/quote.h
@@ -1 +1,2 @@
-char *apquote(char *);
+char *apquote(char *, int (*)(int));
+char *apunquote(char *);
diff --git a/lib/ap/client.c b/lib/ap/client.c
@@ -77,7 +77,7 @@ apc_msg(struct apc *apc)
itm = apc->args;
do {
- arg = apquote(itm->path);
+ arg = apquote(itm->path, NULL);
if (arg == NULL || bufappend(buf, arg, strlen(arg)) ||
bufappend(buf, " ", 1)) {
free(arg);
@@ -143,7 +143,7 @@ char *
apc_read(struct apc *apc)
{
unsigned int len;
- char *buf, *end;
+ char *buf, *end, *tmp;
while ((end = memchr(apc->buf->data, '\n', apc->buf->len)) == NULL &&
apc_recv(apc) == 0)
@@ -169,5 +169,11 @@ apc_read(struct apc *apc)
apc->status = buf;
return NULL;
}
+
+ if (*buf == '\'') {
+ tmp = apunquote(buf);
+ free(buf);
+ buf = tmp;
+ }
return buf;
}
diff --git a/lib/ap/quote.c b/lib/ap/quote.c
@@ -17,20 +17,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#include <sys/socket.h>
-
+#include <ctype.h>
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
-#include <ap/ap.h>
+int
+apneedquote(char *s, int (*needquote)(int))
+{
+ if (*s == 0)
+ return 1;
+ while (!iscntrl(*s) && !isspace(*s) && *s != '\'' && (!needquote || !needquote(*s)))
+ ++s;
+ return *s;
+}
char *
-apquote(char *s)
+apquote(char *s, int (*needquote)(int))
{
char *buf, *p;
size_t i;
- if (!strchr(s, AP_QUOTE) && s[strcspn(s, AP_ARG_SEPS)] == '\0')
+ if (!apneedquote(s, needquote))
return strdup(s);
buf = malloc(strlen(s) * 2 + 3);
@@ -38,14 +46,46 @@ apquote(char *s)
return NULL;
p = buf;
- *p++ = AP_QUOTE;
+ *p++ = '\'';
for (i = 0; s[i]; ++i) {
- if (s[i] == AP_QUOTE)
- *p++ = AP_QUOTE;
+ if (s[i] == '\'')
+ *p++ = '\'';
*p++ = s[i];
}
- *p++ = AP_QUOTE;
+ *p++ = '\'';
*p++ = '\0';
return buf;
}
+
+char *
+apunquote(char *s)
+{
+ size_t i;
+ char *buf, *bufp;
+
+ if (s[0] != '\'')
+ return strdup(s);
+
+ bufp = buf = malloc(strlen(s));
+ if (buf == NULL)
+ return NULL;
+
+ for (i = 1; s[i]; ++i, ++bufp) {
+ if (s[i] == '\'') {
+ ++i;
+ if (s[i] == '\0') {
+ *bufp = '\0';
+ return buf;
+ }
+ if (s[i] != '\'')
+ break;
+ }
+ *bufp = s[i];
+
+ }
+
+ free(buf);
+ errno = EINVAL;
+ return NULL;
+}