xbps-checkvers: rewrite shell substitution without buffer guessing

This commit is contained in:
Duncaen 2019-06-13 21:39:45 +02:00 committed by Duncan Overbruck
parent 6b3b4bc3cd
commit 204b9b630a

View File

@ -1,6 +1,7 @@
/* /*
* Copyright (c) 2014-2019 Juan Romero Pardines * Copyright (c) 2014-2019 Juan Romero Pardines
* Copyright (c) 2012-2014 Dave Elusive <davehome@redthumb.info.tm> * Copyright (c) 2012-2014 Dave Elusive <davehome@redthumb.info.tm>
* Copyright (c) 2019 Duncan Overbruck <mail@duncano.de>
* All rights reserved * All rights reserved
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -58,6 +59,17 @@ do { \
#define _dprintf(...) #define _dprintf(...)
#endif #endif
#define SBUFSZ 128
#define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1))
#define MAX(a, b) ((a) < (b) ? (b) : (a))
#define NEXTSZ(o, r) ALIGN(MAX((o) * 2, (o) + (r)), SBUFSZ)
struct sbuf {
char *mem;
size_t len;
size_t size;
};
typedef struct str_ptr_t { typedef struct str_ptr_t {
char *s; char *s;
size_t len; size_t len;
@ -90,6 +102,77 @@ typedef struct _rcv_t {
typedef int (*rcv_check_func)(rcv_t *); typedef int (*rcv_check_func)(rcv_t *);
typedef int (*rcv_proc_func)(rcv_t *, const char *, rcv_check_func); typedef int (*rcv_proc_func)(rcv_t *, const char *, rcv_check_func);
static void
sbuf_extend(struct sbuf *sb, int newsz)
{
sb->size = newsz;
sb->mem = realloc(sb->mem, newsz);
if (sb->mem == NULL) {
fprintf(stderr, "realloc: %s\n", strerror(errno));
exit(1);
}
}
static struct sbuf *
sbuf_make(void)
{
struct sbuf *sb = calloc(1, sizeof(*sb));
if (sb == NULL) {
fprintf(stderr, "calloc: %s\n", strerror(errno));
exit(1);
}
return sb;
}
static char *
sbuf_buf(struct sbuf *sb)
{
if (!sb->mem)
sbuf_extend(sb, 1);
sb->mem[sb->len] = '\0';
return sb->mem;
}
static size_t
sbuf_done(struct sbuf *sb, char **dest)
{
size_t len = sb->len;
*dest = sbuf_buf(sb);
free(sb);
return len;
}
static void
sbuf_chr(struct sbuf *sb, int c)
{
if (sb->len + 2 >= sb->size)
sbuf_extend(sb, NEXTSZ(sb->size, 1));
sb->mem[sb->len++] = c;
}
static void
sbuf_mem(struct sbuf *sb, const char *src, size_t len)
{
if (sb->len + len + 1 >= sb->size)
sbuf_extend(sb, NEXTSZ(sb->size, len + 1));
memcpy(sb->mem + sb->len, src, len);
sb->len += len;
}
static void
sbuf_str(struct sbuf *sb, const char *src)
{
sbuf_mem(sb, src, strlen(src));
}
#if 0
static void
sbuf_strn(struct sbuf *sb, const char *src, size_t n)
{
sbuf_mem(sb, src, n);
}
#endif
static map_item_t static map_item_t
map_new_item(void) map_new_item(void)
{ {
@ -291,87 +374,81 @@ rcv_load_file(rcv_t *rcv, const char *fname)
return true; return true;
} }
static char * static size_t
rcv_refs(rcv_t *rcv, const char *s, size_t len) rcv_sh_substitute(rcv_t *rcv, const char *str, size_t len, char **outp)
{ {
const char *p;
char *cmd;
struct sbuf *out = sbuf_make();
for (p = str; *p && p < str+len; p++) {
switch (*p) {
case '$':
if (p+1 < str+len) {
const char *ref;
size_t reflen;
map_item_t item; map_item_t item;
size_t i = 0, j = 0, k = 0, count = len*3; p++;
char *ref = calloc(count, sizeof(char)); if (*p == '(') {
char *buf = calloc(count, sizeof(char)); FILE *fp;
char c;
assert(rcv); for (ref = ++p; *p && p < str+len && *p != ')'; p++)
assert(s); ;
assert(ref); if (*p != ')')
assert(buf); goto err1;
cmd = strndup(ref, p-ref);
while (i < len) { if ((fp = popen(cmd, "r")) == NULL)
if (s[i] == '$' && s[i+1] != '(') { goto err2;
j = 0; while ((c = fgetc(fp)) != EOF && c != '\n')
i++; sbuf_chr(out, c);
if (s[i] == '{') { if (pclose(fp) != 0)
i++; goto err2;
} free(cmd);
while (isalpha(s[i]) || s[i] == '_') { cmd = NULL;
ref[j++] = s[i++]; continue;
} } else if (*p == '{') {
if (s[i] == '}') { for (ref = ++p; *p && p < str+len && (isalnum(*p) || *p == '_'); p++)
i++; ;
} reflen = p-ref;
ref[j++] = '\0'; switch (*p) {
item = map_find(rcv->env, ref); case '/': /* fallthrough */
if ((strncmp(ref, item.k.s, strlen(ref)) == 0)) { case '%': /* fallthrough */
buf = strcat(buf, item.v.s); case '#': /* fallthrough */
k += item.v.len; case ':':
} else { for (; *p && p < str+len && *p != '}'; p++)
buf = strcat(buf, "NULL"); ;
k += 4; if (*p != '}')
goto err1;
break;
case '}':
break;
default:
goto err1;
} }
} else { } else {
if (s[i] != '\n') for (ref = p; *p && p < str+len && (isalnum(*p) || *p == '_'); p++)
buf[k++] = s[i++]; ;
reflen = p-ref;
}
item = map_find_n(rcv->env, ref, reflen);
if ((strncmp(ref, item.k.s, reflen) == 0)) {
sbuf_str(out, item.v.s);
} else {
sbuf_str(out, "NULL");
}
break;
}
/* fallthrough */
default:
sbuf_chr(out, *p);
} }
} }
buf[k] = '\0'; return sbuf_done(out, outp);
free(ref);
return buf;
}
static char * err1:
rcv_cmd(rcv_t *rcv, const char *s, size_t len) fprintf(stderr, "syntax error: in file '%s'\n", rcv->fname);
{ exit(1);
int c, rv = 0; err2:
FILE *stream;
size_t i = 0, j = 0, k = 0, count = len*3;
char *cmd = calloc(count, sizeof(char));
char *buf = calloc(count, sizeof(char));
assert(cmd);
assert(buf);
(void)rcv;
while (i < len) {
if (s[i] == '$' && s[i+1] != '{') {
j = 0;
i++;
if (s[i] == '(') {
i++;
}
while (s[i] != ')') {
cmd[j++] = s[i++];
}
if (s[i] == ')') {
i++;
}
cmd[j++] = '\0';
if ((stream = popen(cmd, "r")) == NULL)
goto error;
while ((c = fgetc(stream)) != EOF && c != '\n') {
buf[k++] = (char)c;
}
rv = pclose(stream);
error:
if (rv > 0 || errno > 0) {
fprintf(stderr, fprintf(stderr,
"Shell cmd failed: '%s' for " "Shell cmd failed: '%s' for "
"template '%s'", "template '%s'",
@ -379,20 +456,10 @@ error:
if (errno > 0) { if (errno > 0) {
fprintf(stderr, ": %s\n", fprintf(stderr, ": %s\n",
strerror(errno)); strerror(errno));
}
putchar('\n');
exit(1);
}
} else { } else {
if (s[i] != '\n') fputc('\n', stderr);
buf[k++] = s[i++];
} }
} exit(1);
buf[k] = '\0';
free(cmd);
free(__UNCONST(s));
return buf;
} }
static void static void
@ -460,11 +527,8 @@ rcv_get_pkgver(rcv_t *rcv)
if (strchr(v, '$')) { if (strchr(v, '$')) {
assert(item); assert(item);
assert(item->v.s); assert(item->v.s);
item->v.s = rcv_refs(rcv, item->v.s, item->v.len); item->v.len = rcv_sh_substitute(rcv, item->v.s, item->v.len, &item->v.s);
item->v.len = strlen(item->v.s);
item->v.vmalloc = 1; item->v.vmalloc = 1;
item->v.s = rcv_cmd(rcv, item->v.s, item->v.len);
item->v.len = strlen(item->v.s);
} else { } else {
item->v.vmalloc = 0; item->v.vmalloc = 0;
} }