File: //home/ubuntu/neovim/.deps/build/src/unibilium/unibilium.c
/*
Copyright 2008, 2010, 2012, 2013, 2015 Lukas Mai.
This file is part of unibilium.
Unibilium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Unibilium 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with unibilium. If not, see <http://www.gnu.org/licenses/>.
*/
#include "unibilium.h"
#include <errno.h>
#include <limits.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#define ASSERT_RETURN(COND, VAL) do { \
assert(COND); \
if (!(COND)) return VAL; \
} while (0)
#define ASSERT_RETURN_(COND) ASSERT_RETURN(COND, )
#define COUNTOF(a) (sizeof (a) / sizeof *(a))
#define NCONTAINERS(n, csize) (((n) - 1) / (csize) + 1u)
#define SIZE_ERR ((size_t)-1)
#define MAX15BITS 0x7fff
#define MAX31BITS 0x7fffffff
#if INT_MAX < MAX31BITS
#error "int must be at least 32 bits wide"
#endif
#define DYNARR(W, X) DynArr_ ## W ## _ ## X
#define DYNARR_T(W) DYNARR(W, t)
#define DEFDYNARRAY(T, W) \
typedef struct { T (*data); size_t used, size; } DYNARR_T(W); \
static void DYNARR(W, init)(DYNARR_T(W) *const d) { \
d->data = NULL; \
d->used = d->size = 0; \
} \
static void DYNARR(W, free)(DYNARR_T(W) *const d) { \
free(d->data); \
DYNARR(W, init)(d); \
} \
static int DYNARR(W, ensure_slots)(DYNARR_T(W) *const d, const size_t n) { \
size_t k = d->size; \
while (d->used + n > k) { \
k = next_alloc(k); \
} \
if (k > d->size) { \
T (*const p) = realloc(d->data, k * sizeof *p); \
if (!p) { \
return 0; \
} \
d->data = p; \
d->size = k; \
} \
return 1; \
} \
static int DYNARR(W, ensure_slot)(DYNARR_T(W) *const d) { \
return DYNARR(W, ensure_slots)(d, 1); \
} \
static void DYNARR(W, init)(DYNARR_T(W) *)
static size_t next_alloc(size_t n) {
return n * 3 / 2 + 5;
}
DEFDYNARRAY(unsigned char, bool);
DEFDYNARRAY(int, num);
DEFDYNARRAY(const char *, str);
enum {
MAGIC_16BIT = 00432,
MAGIC_32BIT = 01036
};
struct unibi_term {
const char *name;
const char **aliases;
unsigned char bools[NCONTAINERS(unibi_boolean_end_ - unibi_boolean_begin_ - 1, CHAR_BIT)];
int nums[unibi_numeric_end_ - unibi_numeric_begin_ - 1];
const char *strs[unibi_string_end_ - unibi_string_begin_ - 1];
char *alloc;
DYNARR_T(bool) ext_bools;
DYNARR_T(num) ext_nums;
DYNARR_T(str) ext_strs;
DYNARR_T(str) ext_names;
char *ext_alloc;
};
#define ASSERT_EXT_NAMES(X) assert((X)->ext_names.used == (X)->ext_bools.used + (X)->ext_nums.used + (X)->ext_strs.used)
static unsigned short get_ushort16(const char *p) {
const unsigned char *q = (const unsigned char *)p;
return q[0] + q[1] * 256;
}
static short get_short16(const char *p) {
unsigned short n = get_ushort16(p);
return n <= MAX15BITS ? n : -1;
}
static unsigned int get_uint32(const char *p) {
const unsigned char *q = (const unsigned char *)p;
return q[0] + q[1] * 256u + q[2] * 256u * 256u + q[3] * 256u * 256u * 256u;
}
static int get_int32(const char *p) {
unsigned int n = get_uint32(p);
return n <= MAX31BITS ? (int)n : -1;
}
static void fill_1(int *p, size_t n) {
while (n--) {
*p++ = -1;
}
}
static void fill_null(const char **p, size_t n) {
while (n--) {
*p++ = NULL;
}
}
static const char *off_of(const char *p, size_t n, short i) {
return i < 0 || (size_t)i >= n ? NULL : p + i;
}
unibi_term *unibi_dummy(void) {
unibi_term *t;
void *mem;
if (!(t = malloc(sizeof *t))) {
return NULL;
}
if (!(mem = malloc(2 * sizeof *t->aliases))) {
free(t);
return NULL;
}
t->alloc = mem;
t->aliases = mem;
t->name = "unibilium dummy terminal";
t->aliases[0] = "null";
t->aliases[1] = NULL;
memset(t->bools, '\0', sizeof t->bools);
fill_1(t->nums, COUNTOF(t->nums));
fill_null(t->strs, COUNTOF(t->strs));
DYNARR(bool, init)(&t->ext_bools);
DYNARR(num, init)(&t->ext_nums);
DYNARR(str, init)(&t->ext_strs);
DYNARR(str, init)(&t->ext_names);
t->ext_alloc = NULL;
ASSERT_EXT_NAMES(t);
return t;
}
static size_t mcount(const char *p, size_t n, char c) {
size_t r = 0;
while (n--) {
if (*p++ == c) {
r++;
}
}
return r;
}
static size_t size_max(size_t a, size_t b) {
return a >= b ? a : b;
}
#define FAIL_IF_(c, e, f) do { if (c) { f; errno = (e); return NULL; } } while (0)
#define FAIL_IF(c, e) FAIL_IF_(c, e, (void)0)
#define DEL_FAIL_IF(c, e, x) FAIL_IF_(c, e, unibi_destroy(x))
unibi_term *unibi_from_mem(const char *p, size_t n) {
unibi_term *t = NULL;
size_t numsize;
unsigned short magic, namlen, boollen, numlen, strslen, tablsz;
char *strp, *namp;
size_t namco;
size_t i;
FAIL_IF(n < 12, EFAULT);
magic = get_ushort16(p + 0);
FAIL_IF(magic != MAGIC_16BIT && magic != MAGIC_32BIT, EINVAL);
numsize = magic == MAGIC_16BIT ? 2 : 4;
namlen = get_ushort16(p + 2);
boollen = get_ushort16(p + 4);
numlen = get_ushort16(p + 6);
strslen = get_ushort16(p + 8);
tablsz = get_ushort16(p + 10);
p += 12;
n -= 12;
FAIL_IF(n < namlen, EFAULT);
namco = mcount(p, namlen, '|') + 1;
if (!(t = malloc(sizeof *t))) {
return NULL;
}
{
void *mem;
if (!(mem = malloc(namco * sizeof *t->aliases + tablsz + namlen + 1))) {
free(t);
return NULL;
}
t->alloc = mem;
t->aliases = mem;
}
strp = t->alloc + namco * sizeof *t->aliases;
namp = strp + tablsz;
memcpy(namp, p, namlen);
namp[namlen] = '\0';
p += namlen;
n -= namlen;
{
size_t k = 0;
char *a, *z;
a = namp;
while ((z = strchr(a, '|'))) {
*z = '\0';
t->aliases[k++] = a;
a = z + 1;
}
assert(k < namco);
t->aliases[k] = NULL;
t->name = a;
}
DYNARR(bool, init)(&t->ext_bools);
DYNARR(num, init)(&t->ext_nums);
DYNARR(str, init)(&t->ext_strs);
DYNARR(str, init)(&t->ext_names);
t->ext_alloc = NULL;
DEL_FAIL_IF(n < boollen, EFAULT, t);
memset(t->bools, '\0', sizeof t->bools);
for (i = 0; i < boollen && i / CHAR_BIT < COUNTOF(t->bools); i++) {
if (p[i]) {
t->bools[i / CHAR_BIT] |= 1 << i % CHAR_BIT;
}
}
p += boollen;
n -= boollen;
if ((namlen + boollen) % 2 && n > 0) {
p += 1;
n -= 1;
}
DEL_FAIL_IF(n < numlen * numsize, EFAULT, t);
for (i = 0; i < numlen && i < COUNTOF(t->nums); i++) {
if (numsize == 2) {
t->nums[i] = get_short16(p + i * 2);
} else {
t->nums[i] = get_int32(p + i * 4);
}
}
fill_1(t->nums + i, COUNTOF(t->nums) - i);
p += numlen * numsize;
n -= numlen * numsize;
DEL_FAIL_IF(n < strslen * 2u, EFAULT, t);
for (i = 0; i < strslen && i < COUNTOF(t->strs); i++) {
t->strs[i] = off_of(strp, tablsz, get_short16(p + i * 2));
}
fill_null(t->strs + i, COUNTOF(t->strs) - i);
p += strslen * 2;
n -= strslen * 2;
DEL_FAIL_IF(n < tablsz, EFAULT, t);
memcpy(strp, p, tablsz);
if (tablsz) {
strp[tablsz - 1] = '\0';
}
p += tablsz;
n -= tablsz;
if (tablsz % 2 && n > 0) {
p += 1;
n -= 1;
}
if (n >= 10) {
unsigned short extboollen, extnumlen, extstrslen, extofflen, exttablsz;
size_t extalllen;
extboollen = get_ushort16(p + 0);
extnumlen = get_ushort16(p + 2);
extstrslen = get_ushort16(p + 4);
extofflen = get_ushort16(p + 6);
exttablsz = get_ushort16(p + 8);
if (
extboollen <= MAX15BITS &&
extnumlen <= MAX15BITS &&
extstrslen <= MAX15BITS &&
extofflen <= MAX15BITS &&
exttablsz <= MAX15BITS
) {
p += 10;
n -= 10;
extalllen = 0;
extalllen += extboollen;
extalllen += extnumlen;
extalllen += extstrslen;
DEL_FAIL_IF(
n <
extboollen +
extboollen % 2 +
extnumlen * numsize +
extstrslen * 2 +
extalllen * 2 +
exttablsz,
EFAULT,
t
);
DEL_FAIL_IF(
!DYNARR(bool, ensure_slots)(&t->ext_bools, extboollen) ||
!DYNARR(num, ensure_slots)(&t->ext_nums, extnumlen) ||
!DYNARR(str, ensure_slots)(&t->ext_strs, extstrslen) ||
!DYNARR(str, ensure_slots)(&t->ext_names, extalllen) ||
(exttablsz && !(t->ext_alloc = malloc(exttablsz))),
ENOMEM,
t
);
for (i = 0; i < extboollen; i++) {
t->ext_bools.data[i] = !!p[i];
}
t->ext_bools.used = extboollen;
p += extboollen;
n -= extboollen;
if (extboollen % 2) {
p += 1;
n -= 1;
}
for (i = 0; i < extnumlen; i++) {
if (numsize == 2) {
t->ext_nums.data[i] = get_short16(p + i * 2);
} else {
t->ext_nums.data[i] = get_int32(p + i * 4);
}
}
t->ext_nums.used = extnumlen;
p += extnumlen * numsize;
n -= extnumlen * numsize;
{
char *ext_alloc2;
size_t tblsz2;
const char *const tbl1 = p + extstrslen * 2 + extalllen * 2;
size_t s_max = 0, s_sum = 0;
for (i = 0; i < extstrslen; i++) {
const short v = get_short16(p + i * 2);
if (v < 0 || (unsigned short)v >= exttablsz) {
t->ext_strs.data[i] = NULL;
} else {
const char *start = tbl1 + v;
const char *end = memchr(start, '\0', exttablsz - v);
if (end) {
end++;
} else {
end = tbl1 + exttablsz;
}
s_sum += end - start;
s_max = size_max(s_max, end - tbl1);
t->ext_strs.data[i] = t->ext_alloc + v;
}
}
t->ext_strs.used = extstrslen;
p += extstrslen * 2;
n -= extstrslen * 2;
DEL_FAIL_IF(s_max != s_sum, EINVAL, t);
ext_alloc2 = t->ext_alloc + s_sum;
tblsz2 = exttablsz - s_sum;
for (i = 0; i < extalllen; i++) {
const short v = get_short16(p + i * 2);
DEL_FAIL_IF(v < 0 || (unsigned short)v >= tblsz2, EINVAL, t);
t->ext_names.data[i] = ext_alloc2 + v;
}
t->ext_names.used = extalllen;
p += extalllen * 2;
n -= extalllen * 2;
assert(p == tbl1);
if (exttablsz) {
memcpy(t->ext_alloc, p, exttablsz);
t->ext_alloc[exttablsz - 1] = '\0';
}
}
}
}
ASSERT_EXT_NAMES(t);
return t;
}
#undef FAIL_IF
#undef FAIL_IF_
#undef DEL_FAIL_IF
void unibi_destroy(unibi_term *t) {
DYNARR(bool, free)(&t->ext_bools);
DYNARR(num, free)(&t->ext_nums);
DYNARR(str, free)(&t->ext_strs);
DYNARR(str, free)(&t->ext_names);
free(t->ext_alloc);
t->ext_alloc = (char *)">_>";
t->aliases = NULL;
free(t->alloc);
t->alloc = (char *)":-O";
free(t);
}
static void put_ushort16(char *p, unsigned short n) {
unsigned char *q = (unsigned char *)p;
q[0] = n % 256;
q[1] = n / 256 % 256;
}
static void put_short16(char *p, short n) {
#if MAX15BITS < SHRT_MAX
assert(n <= MAX15BITS);
#endif
put_ushort16(
p,
n < 0
#if MAX15BITS < SHRT_MAX
|| n > MAX15BITS
#endif
? 0xffffU
: (unsigned short)n
);
}
static void put_uint32(char *p, unsigned int n) {
unsigned char *q = (unsigned char *)p;
q[0] = n % 256u;
q[1] = n / 256u % 256u;
q[2] = n / (256u * 256u) % 256u;
q[3] = n / (256u * 256u * 256u) % 256u;
}
static void put_int32(char *p, int n) {
#if MAX31BITS < INT_MAX
assert(n <= MAX31BITS);
#endif
put_uint32(
p,
n < 0
#if MAX31BITS < INT_MAX
|| n > MAX31BITS
#endif
? 0xffffffffU
: (unsigned int)n
);
}
#define FAIL_INVAL_IF(c) if (c) { errno = EINVAL; return SIZE_ERR; } else (void)0
size_t unibi_dump(const unibi_term *t, char *ptr, size_t n) {
size_t req, i;
size_t namlen, boollen, numlen, strslen, tablsz;
size_t ext_count, ext_tablsz1, ext_tablsz2;
char *p;
ASSERT_EXT_NAMES(t);
p = ptr;
req = 2 + 5 * 2;
namlen = strlen(t->name) + 1;
for (i = 0; t->aliases[i]; i++) {
namlen += strlen(t->aliases[i]) + 1;
}
req += namlen;
for (i = unibi_boolean_end_ - unibi_boolean_begin_ - 1; i--; ) {
if (t->bools[i / CHAR_BIT] >> i % CHAR_BIT & 1) {
break;
}
}
i++;
boollen = i;
req += boollen;
if (req % 2) {
req += 1;
}
size_t numsize = 2;
for (i = COUNTOF(t->nums); i--; ) {
if (t->nums[i] >= 0) {
break;
}
}
i++;
numlen = i;
while (i--) {
if (t->nums[i] > MAX15BITS) {
FAIL_INVAL_IF(t->nums[i] > MAX31BITS);
numsize = 4;
}
}
req += numlen * numsize;
for (i = COUNTOF(t->strs); i--; ) {
if (t->strs[i]) {
break;
}
}
i++;
strslen = i;
req += strslen * 2;
tablsz = 0;
while (i--) {
if (t->strs[i]) {
tablsz += strlen(t->strs[i]) + 1;
}
}
req += tablsz;
FAIL_INVAL_IF(tablsz > MAX15BITS);
FAIL_INVAL_IF(t->ext_bools.used > MAX15BITS);
FAIL_INVAL_IF(t->ext_nums.used > MAX15BITS);
FAIL_INVAL_IF(t->ext_strs.used > MAX15BITS);
ext_tablsz1 = ext_tablsz2 = 0;
ext_count = t->ext_bools.used + t->ext_nums.used + t->ext_strs.used;
assert(ext_count == t->ext_names.used);
if (ext_count) {
if (req % 2) {
req += 1;
}
req += 5 * 2;
req += t->ext_bools.used;
if (req % 2) {
req += 1;
}
if (numsize == 2) {
for (i = 0; i < t->ext_nums.used; i++) {
if (t->ext_nums.data[i] > MAX15BITS) {
FAIL_INVAL_IF(t->ext_nums.data[i] > MAX31BITS);
numsize = 4;
}
}
if (numsize == 4) {
req += numlen * 2;
}
}
req += t->ext_nums.used * numsize;
req += t->ext_strs.used * 2;
req += ext_count * 2;
for (i = 0; i < t->ext_strs.used; i++) {
if (t->ext_strs.data[i]) {
ext_tablsz1 += strlen(t->ext_strs.data[i]) + 1;
}
}
FAIL_INVAL_IF(ext_tablsz1 > MAX15BITS);
req += ext_tablsz1;
for (i = 0; i < t->ext_names.used; i++) {
ext_tablsz2 += strlen(t->ext_names.data[i]) + 1;
}
FAIL_INVAL_IF(ext_tablsz2 > MAX15BITS);
req += ext_tablsz2;
FAIL_INVAL_IF(ext_tablsz1 + ext_tablsz2 > MAX15BITS);
}
if (req > n) {
errno = EFAULT;
return req;
}
put_ushort16(p + 0, numsize == 2 ? MAGIC_16BIT : MAGIC_32BIT);
put_ushort16(p + 2, namlen);
put_ushort16(p + 4, boollen);
put_ushort16(p + 6, numlen);
put_ushort16(p + 8, strslen);
put_ushort16(p + 10, tablsz);
p += 12;
for (i = 0; t->aliases[i]; i++) {
size_t k = strlen(t->aliases[i]);
memcpy(p, t->aliases[i], k);
p += k;
*p++ = '|';
}
{
size_t k = strlen(t->name) + 1;
memcpy(p, t->name, k);
p += k;
}
for (i = 0; i < boollen; i++) {
*p++ = t->bools[i / CHAR_BIT] >> i % CHAR_BIT & 1;
}
if ((namlen + boollen) % 2) {
*p++ = '\0';
}
for (i = 0; i < numlen; i++) {
if (numsize == 2) {
put_short16(p, t->nums[i]);
p += 2;
} else {
put_int32(p, t->nums[i]);
p += 4;
}
}
{
char *const tbl = p + strslen * 2;
size_t off = 0;
for (i = 0; i < strslen; i++) {
if (!t->strs[i]) {
put_short16(p, -1);
p += 2;
} else {
size_t k = strlen(t->strs[i]) + 1;
assert(off < MAX15BITS);
put_short16(p, (short)off);
p += 2;
memcpy(tbl + off, t->strs[i], k);
off += k;
}
}
assert(off == tablsz);
assert(p == tbl);
p += off;
}
if (ext_count) {
if ((p - ptr) % 2) {
*p++ = '\0';
}
put_ushort16(p + 0, t->ext_bools.used);
put_ushort16(p + 2, t->ext_nums.used);
put_ushort16(p + 4, t->ext_strs.used);
put_ushort16(p + 6, t->ext_strs.used + ext_count);
put_ushort16(p + 8, ext_tablsz1 + ext_tablsz2);
p += 10;
if (t->ext_bools.used) {
memcpy(p, t->ext_bools.data, t->ext_bools.used);
p += t->ext_bools.used;
}
if (t->ext_bools.used % 2) {
*p++ = '\0';
}
for (i = 0; i < t->ext_nums.used; i++) {
if (numsize == 2) {
put_short16(p, t->ext_nums.data[i]);
p += 2;
} else {
put_int32(p, t->ext_nums.data[i]);
p += 4;
}
}
{
char *const tbl1 = p + (t->ext_strs.used + ext_count) * 2;
char *const tbl2 = tbl1 + ext_tablsz1;
size_t off = 0;
for (i = 0; i < t->ext_strs.used; i++) {
const char *const s = t->ext_strs.data[i];
if (!s) {
put_short16(p, -1);
} else {
const size_t k = strlen(s) + 1;
assert(off < MAX15BITS);
put_ushort16(p, off);
memcpy(tbl1 + off, s, k);
off += k;
}
p += 2;
}
assert(off == ext_tablsz1);
assert(p + ext_count * 2 == tbl1);
off = 0;
for (i = 0; i < t->ext_names.used; i++) {
const char *const s = t->ext_names.data[i];
const size_t k = strlen(s) + 1;
assert(off < MAX15BITS);
put_ushort16(p, off);
p += 2;
memcpy(tbl2 + off, s, k);
off += k;
}
assert(off == ext_tablsz2);
assert(p == tbl1);
p += ext_tablsz1 + ext_tablsz2;
}
}
assert((size_t)(p - ptr) == req);
return req;
}
const char *unibi_get_name(const unibi_term *t) {
return t->name;
}
void unibi_set_name(unibi_term *t, const char *s) {
t->name = s;
}
const char **unibi_get_aliases(const unibi_term *t) {
return t->aliases;
}
void unibi_set_aliases(unibi_term *t, const char **a) {
t->aliases = a;
}
int unibi_get_bool(const unibi_term *t, enum unibi_boolean v) {
size_t i;
ASSERT_RETURN(v > unibi_boolean_begin_ && v < unibi_boolean_end_, -1);
i = v - unibi_boolean_begin_ - 1;
return t->bools[i / CHAR_BIT] >> i % CHAR_BIT & 1;
}
void unibi_set_bool(unibi_term *t, enum unibi_boolean v, int x) {
size_t i;
ASSERT_RETURN_(v > unibi_boolean_begin_ && v < unibi_boolean_end_);
i = v - unibi_boolean_begin_ - 1;
if (x) {
t->bools[i / CHAR_BIT] |= 1 << i % CHAR_BIT;
} else {
t->bools[i / CHAR_BIT] &= ~(1 << i % CHAR_BIT);
}
}
int unibi_get_num(const unibi_term *t, enum unibi_numeric v) {
size_t i;
ASSERT_RETURN(v > unibi_numeric_begin_ && v < unibi_numeric_end_, -2);
i = v - unibi_numeric_begin_ - 1;
return t->nums[i];
}
void unibi_set_num(unibi_term *t, enum unibi_numeric v, int x) {
size_t i;
ASSERT_RETURN_(v > unibi_numeric_begin_ && v < unibi_numeric_end_);
i = v - unibi_numeric_begin_ - 1;
t->nums[i] = x;
}
const char *unibi_get_str(const unibi_term *t, enum unibi_string v) {
size_t i;
ASSERT_RETURN(v > unibi_string_begin_ && v < unibi_string_end_, NULL);
i = v - unibi_string_begin_ - 1;
return t->strs[i];
}
void unibi_set_str(unibi_term *t, enum unibi_string v, const char *x) {
size_t i;
ASSERT_RETURN_(v > unibi_string_begin_ && v < unibi_string_end_);
i = v - unibi_string_begin_ - 1;
t->strs[i] = x;
}
size_t unibi_count_ext_bool(const unibi_term *t) {
return t->ext_bools.used;
}
size_t unibi_count_ext_num(const unibi_term *t) {
return t->ext_nums.used;
}
size_t unibi_count_ext_str(const unibi_term *t) {
return t->ext_strs.used;
}
int unibi_get_ext_bool(const unibi_term *t, size_t i) {
ASSERT_RETURN(i < t->ext_bools.used, -1);
return t->ext_bools.data[i] ? 1 : 0;
}
const char *unibi_get_ext_bool_name(const unibi_term *t, size_t i) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN(i < t->ext_bools.used, NULL);
return t->ext_names.data[i];
}
int unibi_get_ext_num(const unibi_term *t, size_t i) {
ASSERT_RETURN(i < t->ext_nums.used, -2);
return t->ext_nums.data[i];
}
const char *unibi_get_ext_num_name(const unibi_term *t, size_t i) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN(i < t->ext_nums.used, NULL);
return t->ext_names.data[t->ext_bools.used + i];
}
const char *unibi_get_ext_str(const unibi_term *t, size_t i) {
ASSERT_RETURN(i < t->ext_strs.used, NULL);
return t->ext_strs.data[i];
}
const char *unibi_get_ext_str_name(const unibi_term *t, size_t i) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN(i < t->ext_strs.used, NULL);
return t->ext_names.data[t->ext_bools.used + t->ext_nums.used + i];
}
void unibi_set_ext_bool(unibi_term *t, size_t i, int v) {
ASSERT_RETURN_(i < t->ext_bools.used);
t->ext_bools.data[i] = !!v;
}
void unibi_set_ext_bool_name(unibi_term *t, size_t i, const char *c) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN_(i < t->ext_bools.used);
t->ext_names.data[i] = c;
}
void unibi_set_ext_num(unibi_term *t, size_t i, int v) {
ASSERT_RETURN_(i < t->ext_nums.used);
t->ext_nums.data[i] = v;
}
void unibi_set_ext_num_name(unibi_term *t, size_t i, const char *c) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN_(i < t->ext_nums.used);
t->ext_names.data[t->ext_bools.used + i] = c;
}
void unibi_set_ext_str(unibi_term *t, size_t i, const char *v) {
ASSERT_RETURN_(i < t->ext_strs.used);
t->ext_strs.data[i] = v;
}
void unibi_set_ext_str_name(unibi_term *t, size_t i, const char *c) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN_(i < t->ext_strs.used);
t->ext_names.data[t->ext_bools.used + t->ext_nums.used + i] = c;
}
size_t unibi_add_ext_bool(unibi_term *t, const char *c, int v) {
size_t r;
ASSERT_EXT_NAMES(t);
if (
!DYNARR(bool, ensure_slot)(&t->ext_bools) ||
!DYNARR(str, ensure_slot)(&t->ext_names)
) {
return SIZE_ERR;
}
{
const char **const p = t->ext_names.data + t->ext_bools.used;
memmove(p + 1, p, (t->ext_names.used - t->ext_bools.used) * sizeof *t->ext_names.data);
*p = c;
t->ext_names.used++;
}
r = t->ext_bools.used++;
t->ext_bools.data[r] = !!v;
return r;
}
size_t unibi_add_ext_num(unibi_term *t, const char *c, int v) {
size_t r;
ASSERT_EXT_NAMES(t);
if (
!DYNARR(num, ensure_slot)(&t->ext_nums) ||
!DYNARR(str, ensure_slot)(&t->ext_names)
) {
return SIZE_ERR;
}
{
const char **const p = t->ext_names.data + t->ext_bools.used + t->ext_nums.used;
memmove(p + 1, p, (t->ext_names.used - t->ext_bools.used - t->ext_nums.used) * sizeof *t->ext_names.data);
*p = c;
t->ext_names.used++;
}
r = t->ext_nums.used++;
t->ext_nums.data[r] = v;
return r;
}
size_t unibi_add_ext_str(unibi_term *t, const char *c, const char *v) {
size_t r;
ASSERT_EXT_NAMES(t);
if (
!DYNARR(str, ensure_slot)(&t->ext_strs) ||
!DYNARR(str, ensure_slot)(&t->ext_names)
) {
return SIZE_ERR;
}
t->ext_names.data[t->ext_names.used++] = c;
r = t->ext_strs.used++;
t->ext_strs.data[r] = v;
return r;
}
void unibi_del_ext_bool(unibi_term *t, size_t i) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN_(i < t->ext_bools.used);
{
unsigned char *const p = t->ext_bools.data + i;
memmove(p, p + 1, (t->ext_bools.used - i - 1) * sizeof *t->ext_bools.data);
t->ext_bools.used--;
}
{
const char **const p = t->ext_names.data + i;
memmove(p, p + 1, (t->ext_names.used - i - 1) * sizeof *t->ext_names.data);
t->ext_names.used--;
}
}
void unibi_del_ext_num(unibi_term *t, size_t i) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN_(i < t->ext_nums.used);
{
int *const p = t->ext_nums.data + i;
memmove(p, p + 1, (t->ext_nums.used - i - 1) * sizeof *t->ext_nums.data);
t->ext_nums.used--;
}
{
const char **const p = t->ext_names.data + t->ext_bools.used + i;
memmove(p, p + 1, (t->ext_names.used - i - 1) * sizeof *t->ext_names.data);
t->ext_names.used--;
}
}
void unibi_del_ext_str(unibi_term *t, size_t i) {
ASSERT_EXT_NAMES(t);
ASSERT_RETURN_(i < t->ext_strs.used);
{
const char **const p = t->ext_strs.data + i;
memmove(p, p + 1, (t->ext_strs.used - i - 1) * sizeof *t->ext_strs.data);
t->ext_strs.used--;
}
{
const char **const p = t->ext_names.data + t->ext_bools.used + t->ext_nums.used + i;
memmove(p, p + 1, (t->ext_names.used - i - 1) * sizeof *t->ext_names.data);
t->ext_names.used--;
}
}
unibi_var_t unibi_var_from_num(int i) {
unibi_var_t v;
v.p_ = NULL;
v.i_ = i;
return v;
}
unibi_var_t unibi_var_from_str(char *p) {
unibi_var_t v;
assert(p != NULL);
v.i_ = INT_MIN;
v.p_ = p;
return v;
}
int unibi_num_from_var(unibi_var_t v) {
return v.p_ ? INT_MIN : v.i_;
}
const char *unibi_str_from_var(unibi_var_t v) {
return v.p_ ? v.p_ : "";
}
static void dput(
char t,
const char *fmt,
int w,
int p,
unibi_var_t x,
void (*out)(void *, const char *, size_t),
void *ctx
) {
char buf[512];
buf[0] = '\0';
#define BITTY(A, B, C) (!!(A) << 0 | !!(B) << 1 | !!(C) << 2)
switch (BITTY(t == 's', w != -1, p != -1)) {
case BITTY(0, 0, 0): snprintf(buf, sizeof buf, fmt, unibi_num_from_var(x)); break;
case BITTY(0, 0, 1): snprintf(buf, sizeof buf, fmt, p, unibi_num_from_var(x)); break;
case BITTY(0, 1, 0): snprintf(buf, sizeof buf, fmt, w, unibi_num_from_var(x)); break;
case BITTY(0, 1, 1): snprintf(buf, sizeof buf, fmt, w, p, unibi_num_from_var(x)); break;
case BITTY(1, 0, 0): snprintf(buf, sizeof buf, fmt, unibi_str_from_var(x)); break;
case BITTY(1, 0, 1): snprintf(buf, sizeof buf, fmt, p, unibi_str_from_var(x)); break;
case BITTY(1, 1, 0): snprintf(buf, sizeof buf, fmt, w, unibi_str_from_var(x)); break;
case BITTY(1, 1, 1): snprintf(buf, sizeof buf, fmt, w, p, unibi_str_from_var(x)); break;
}
#undef BITTY
out(ctx, buf, strlen(buf));
}
static long cstrtol(const char *s, const char **pp) {
long r;
char *tmp;
r = strtol(s, &tmp, 10);
*pp = tmp;
return r;
}
void unibi_format(
unibi_var_t var_dyn[26],
unibi_var_t var_static[26],
const char *fmt,
unibi_var_t param[9],
void (*out)(void *, const char *, size_t),
void *ctx1,
void (*pad)(void *, size_t, int, int),
void *ctx2
) {
const unibi_var_t zero = {0};
unibi_var_t stack[123] = {{0}};
size_t sp = 0;
#define UC(F, C) (F((unsigned char)(C)))
#define POP() (sp ? stack[--sp] : zero)
#define PUSH(X) do { if (sp < COUNTOF(stack)) { stack[sp++] = (X); } } while (0)
#define PUSHi(N) do { unibi_var_t tmp_ = unibi_var_from_num(N); PUSH(tmp_); } while (0)
while (*fmt) {
{
size_t r = strcspn(fmt, "%$");
if (r) {
out(ctx1, fmt, r);
fmt += r;
if (!*fmt) {
break;
}
}
}
if (*fmt == '$') {
++fmt;
if (*fmt == '<' && UC(isdigit, fmt[1])) {
int scale = 0, force = 0;
const char *v = fmt + 1;
size_t n = cstrtol(v, &v);
n *= 10;
if (*v == '.') {
++v;
}
if (UC(isdigit, *v)) {
n += *v++ - '0';
}
if (*v == '/') {
++v;
force = 1;
if (*v == '*') {
++v;
scale = 1;
}
} else if (*v == '*') {
++v;
scale = 1;
if (*v == '/') {
++v;
force = 1;
}
}
if (*v == '>') {
fmt = v + 1;
if (pad) {
pad(ctx2, n, scale, force);
}
} else {
out(ctx1, fmt - 1, 1);
}
} else {
out(ctx1, fmt - 1, 1);
}
continue;
}
assert(*fmt == '%');
++fmt;
if (UC(isdigit, *fmt) || (*fmt && strchr(":# .doxX", *fmt))) {
enum {
FlagAlt = 1,
FlagSpc = 2,
FlagSgn = 4,
FlagLft = 8,
FlagZro = 16
};
int flags = 0, width = -1, prec = -1;
const char *v = fmt;
if (*v == ':') {
++v;
}
while (1) {
switch (*v++) {
case '#': flags |= FlagAlt; continue;
case ' ': flags |= FlagSpc; continue;
case '0': flags |= FlagZro; continue;
case '+': flags |= FlagSgn; continue;
case '-': flags |= FlagLft; continue;
}
--v;
break;
}
if (UC(isdigit, *v)) {
width = cstrtol(v, &v);
}
if (*v == '.' && UC(isdigit, v[1])) {
++v;
prec = cstrtol(v, &v);
}
if (*v && strchr("doxXs", *v)) {
char gen[sizeof "%# +-0*.*d"], *g = gen;
*g++ = '%';
if (flags & FlagAlt) { *g++ = '#'; }
if (flags & FlagSpc) { *g++ = ' '; }
if (flags & FlagSgn) { *g++ = '+'; }
if (flags & FlagLft) { *g++ = '-'; }
if (flags & FlagZro) { *g++ = '0'; }
if (width != -1) { *g++ = '*'; }
if (prec != -1) { *g++ = '.'; *g++ = '*'; }
*g++ = *v;
*g = '\0';
dput(*v, gen, width, prec, POP(), out, ctx1);
fmt = v;
} else {
out(ctx1, fmt - 1, 2);
}
++fmt;
continue;
}
switch (*fmt++) {
default:
out(ctx1, fmt - 2, 2);
break;
case '\0':
--fmt;
out(ctx1, "%", 1);
break;
case '%':
out(ctx1, "%", 1);
break;
case 'c': {
unsigned char c;
c = unibi_num_from_var(POP());
out(ctx1, (const char *)&c, 1);
break;
}
case 's': {
const char *s;
s = unibi_str_from_var(POP());
out(ctx1, s, strlen(s));
break;
}
case 'p':
if (*fmt >= '1' && *fmt <= '9') {
size_t n = *fmt++ - '1';
PUSH(param[n]);
} else {
out(ctx1, fmt - 2, 2);
}
break;
case 'P':
if (*fmt >= 'a' && *fmt <= 'z') {
var_dyn[*fmt - 'a'] = POP();
fmt++;
} else if (*fmt >= 'A' && *fmt <= 'Z') {
var_static[*fmt - 'A'] = POP();
fmt++;
} else {
out(ctx1, fmt - 2, 2);
}
break;
case 'g':
if (*fmt >= 'a' && *fmt <= 'z') {
PUSH(var_dyn[*fmt - 'a']);
fmt++;
} else if (*fmt >= 'A' && *fmt <= 'Z') {
PUSH(var_static[*fmt - 'A']);
fmt++;
} else {
out(ctx1, fmt - 2, 2);
}
break;
case '\'':
if (*fmt && fmt[1] == '\'') {
PUSHi((unsigned char)*fmt);
fmt += 2;
} else {
out(ctx1, fmt - 2, 2);
}
break;
case '{': {
size_t r = strspn(fmt, "0123456789");
if (r && fmt[r] == '}') {
PUSHi(atoi(fmt));
fmt += r + 1;
} else {
out(ctx1, fmt - 2, 2);
}
break;
}
case 'l':
PUSHi(strlen(unibi_str_from_var(POP())));
break;
case 'i':
param[0] = unibi_var_from_num(unibi_num_from_var(param[0]) + 1);
param[1] = unibi_var_from_num(unibi_num_from_var(param[1]) + 1);
break;
case '?':
break;
case 't': {
int c = unibi_num_from_var(POP());
if (!c) {
size_t nesting = 0;
for (; *fmt; ++fmt) {
if (*fmt == '%') {
++fmt;
if (*fmt == '?') {
++nesting;
} else if (*fmt == ';') {
if (!nesting) {
++fmt;
break;
}
--nesting;
} else if (*fmt == 'e' && !nesting) {
++fmt;
break;
} else if (!*fmt) {
break;
}
}
}
}
break;
}
case 'e': {
size_t nesting = 0;
for (; *fmt; ++fmt) {
if (*fmt == '%') {
++fmt;
if (*fmt == '?') {
++nesting;
} else if (*fmt == ';') {
if (!nesting) {
++fmt;
break;
}
--nesting;
} else if (!*fmt) {
break;
}
}
}
break;
}
case ';':
break;
#define ARITH2(C, O) \
case (C): { \
unibi_var_t x, y; \
y = POP(); \
x = POP(); \
PUSHi(unibi_num_from_var(x) O unibi_num_from_var(y)); \
} break
ARITH2('+', +);
ARITH2('-', -);
ARITH2('*', *);
ARITH2('/', /);
ARITH2('m', %);
ARITH2('&', &);
ARITH2('|', |);
ARITH2('^', ^);
ARITH2('=', ==);
ARITH2('<', <);
ARITH2('>', >);
ARITH2('A', &&);
ARITH2('O', ||);
#undef ARITH2
#define ARITH1(C, O) \
case (C): \
PUSHi(O unibi_num_from_var(POP())); \
break
ARITH1('!', !);
ARITH1('~', ~);
#undef ARITH1
}
}
#undef PUSHi
#undef PUSH
#undef POP
#undef UC
}
typedef struct {
char *p;
size_t n, w;
} run_ctx_t;
static size_t xmin(size_t a, size_t b) {
return a < b ? a : b;
}
static void out(void *vctx, const char *p, size_t n) {
run_ctx_t *ctx = vctx;
size_t k = xmin(n, ctx->n);
ctx->w += n;
memcpy(ctx->p, p, k);
ctx->p += k;
ctx->n -= k;
}
size_t unibi_run(const char *fmt, unibi_var_t param[9], char *p, size_t n) {
unibi_var_t vars[26 + 26] = {{0}};
run_ctx_t ctx;
ctx.p = p;
ctx.n = n;
ctx.w = 0;
unibi_format(vars, vars + 26, fmt, param, out, &ctx, NULL, NULL);
return ctx.w;
}