#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "demangle_int.h"
#include "cxx.h"
#define CHUNK_SIZE (8U)
void
name_init(name_t *n, sysdem_ops_t *ops)
{
(void) memset(n, 0, sizeof (*n));
n->nm_ops = (ops != NULL) ? ops : sysdem_ops_default;
}
void
name_fini(name_t *n)
{
if (n == NULL)
return;
name_clear(n);
xfree(n->nm_ops, n->nm_items, n->nm_size);
n->nm_items = NULL;
n->nm_size = 0;
}
size_t
name_len(const name_t *n)
{
return (n->nm_len);
}
boolean_t
name_empty(const name_t *n)
{
return (name_len(n) == 0 ? B_TRUE : B_FALSE);
}
void
name_clear(name_t *n)
{
if (n == NULL)
return;
for (size_t i = 0; i < n->nm_len; i++) {
str_pair_t *sp = &n->nm_items[i];
sysdem_ops_t *ops = sp->strp_l.str_ops;
str_pair_fini(sp);
(void) str_pair_init(sp, ops);
}
n->nm_len = 0;
}
static boolean_t
name_reserve(name_t *n, size_t amt)
{
size_t newlen = n->nm_len + amt;
if (newlen == cpp_name_max_depth) {
errno = ENAMETOOLONG;
return (B_FALSE);
}
if (newlen < n->nm_size)
return (B_TRUE);
size_t newsize = roundup(newlen, CHUNK_SIZE);
if (newsize > cpp_name_max_depth)
newsize = cpp_name_max_depth;
void *temp = xrealloc(n->nm_ops, n->nm_items,
n->nm_size * sizeof (str_pair_t), newsize * sizeof (str_pair_t));
if (temp == NULL)
return (B_FALSE);
n->nm_items = temp;
n->nm_size = newsize;
return (B_TRUE);
}
boolean_t
name_add(name_t *n, const char *l, size_t l_len, const char *r, size_t r_len)
{
str_t sl = { 0 };
str_t sr = { 0 };
str_init(&sl, n->nm_ops);
str_init(&sr, n->nm_ops);
str_set(&sl, l, l_len);
str_set(&sr, r, r_len);
return (name_add_str(n, &sl, &sr));
}
boolean_t
name_add_str(name_t *n, str_t *l, str_t *r)
{
str_pair_t sp;
(void) str_pair_init(&sp, n->nm_ops);
if (!name_reserve(n, 1))
return (B_FALSE);
if (l != NULL) {
sp.strp_l = *l;
(void) memset(l, 0, sizeof (*l));
}
if (r != NULL) {
sp.strp_r = *r;
(void) memset(r, 0, sizeof (*r));
}
n->nm_items[n->nm_len++] = sp;
return (B_TRUE);
}
str_pair_t *
name_at(const name_t *n, size_t idx)
{
VERIFY(!name_empty(n));
VERIFY3U(idx, <, n->nm_len);
return (&n->nm_items[n->nm_len - idx - 1]);
}
str_pair_t *
name_top(name_t *n)
{
return (name_at(n, 0));
}
void
name_pop(name_t *n, str_pair_t *sp)
{
if (n->nm_len == 0)
return;
str_pair_t *top = name_top(n);
if (sp != NULL) {
*sp = *top;
(void) memset(top, 0, sizeof (*top));
} else {
str_pair_fini(top);
}
n->nm_len--;
}
boolean_t
name_join(name_t *n, size_t amt, const char *sep)
{
str_pair_t *sp = NULL;
str_t res = { 0 };
size_t seplen = strlen(sep);
VERIFY3U(amt, <=, n->nm_len);
if (amt == 0) {
(void) name_add(n, "", 0, "", 0);
return (B_TRUE);
}
if (amt == 1) {
VERIFY3U(name_len(n), >, 0);
return (str_pair_merge(name_top(n)));
}
(void) str_init(&res, n->nm_ops);
sp = name_at(n, amt - 1);
for (size_t i = 0; i < amt; i++) {
if (i > 0) {
if (!str_append(&res, sep, seplen))
goto error;
}
if (!str_append_str(&res, &sp->strp_l))
goto error;
if (!str_append_str(&res, &sp->strp_r))
goto error;
sp++;
}
for (size_t i = 0; i < amt; i++)
name_pop(n, NULL);
VERIFY(name_add_str(n, &res, NULL));
return (B_TRUE);
error:
str_fini(&res);
return (B_FALSE);
}
static boolean_t
name_fmt_s(name_t *n, str_t *s, const char *fmt, long *maxp)
{
const char *p;
long max = -1;
if (fmt == NULL)
return (B_TRUE);
for (p = fmt; *p != '\0'; p++) {
if (*p != '{') {
(void) str_append_c(s, *p);
continue;
}
errno = 0;
char *q = NULL;
long val = strtol(p + 1, &q, 10);
VERIFY(val != 0 || errno == 0);
VERIFY3U(val, <, n->nm_len);
str_pair_t *sp = name_at(n, val);
if (val > max)
max = val;
switch (q[0]) {
case '}':
if (!str_append_str(s, &sp->strp_l))
return (B_FALSE);
if (!str_append_str(s, &sp->strp_r))
return (B_FALSE);
p = q;
continue;
case ':':
switch (q[1]) {
case 'L':
if (!str_append_str(s, &sp->strp_l))
return (B_FALSE);
break;
case 'R':
if (!str_append_str(s, &sp->strp_r))
return (B_FALSE);
break;
}
p = q + 2;
VERIFY(*p == '}');
break;
}
}
if (*maxp < max)
*maxp = max;
return (B_TRUE);
}
boolean_t
name_fmt(name_t *n, const char *fmt_l, const char *fmt_r)
{
str_pair_t res;
long max = -1;
(void) str_pair_init(&res, n->nm_ops);
if (!name_reserve(n, 1))
return (B_FALSE);
if (!name_fmt_s(n, &res.strp_l, fmt_l, &max))
goto error;
if (!name_fmt_s(n, &res.strp_r, fmt_r, &max))
goto error;
if (max >= 0) {
for (size_t i = 0; i <= max; i++)
name_pop(n, NULL);
}
n->nm_items[n->nm_len++] = res;
return (B_TRUE);
error:
str_pair_fini(&res);
return (B_FALSE);
}
void
sub_init(sub_t *sub, sysdem_ops_t *ops)
{
(void) memset(sub, 0, sizeof (*sub));
sub->sub_ops = (ops != NULL) ? ops : sysdem_ops_default;
}
void
sub_fini(sub_t *sub)
{
if (sub == NULL)
return;
sub_clear(sub);
xfree(sub->sub_ops, sub->sub_items, sub->sub_size);
sub->sub_items = NULL;
sub->sub_size = 0;
}
void
sub_clear(sub_t *sub)
{
if (sub == NULL)
return;
for (size_t i = 0; i < sub->sub_len; i++)
name_fini(&sub->sub_items[i]);
sub->sub_len = 0;
}
boolean_t
sub_empty(const sub_t *sub)
{
return ((sub->sub_len == 0) ? B_TRUE : B_FALSE);
}
size_t
sub_len(const sub_t *sub)
{
return (sub->sub_len);
}
static boolean_t
sub_reserve(sub_t *sub, size_t amt)
{
if (sub->sub_len + amt < sub->sub_size)
return (B_TRUE);
size_t newsize = roundup(sub->sub_size + amt, CHUNK_SIZE);
void *temp = xrealloc(sub->sub_ops, sub->sub_items,
sub->sub_size * sizeof (name_t), newsize * sizeof (name_t));
if (temp == NULL)
return (B_FALSE);
sub->sub_items = temp;
sub->sub_size = newsize;
return (B_TRUE);
}
boolean_t
sub_save(sub_t *sub, const name_t *n, size_t depth)
{
if (depth == 0)
return (B_TRUE);
if (!sub_reserve(sub, 1))
return (B_FALSE);
name_t *dest = &sub->sub_items[sub->sub_len++];
name_init(dest, sub->sub_ops);
if (!name_reserve(dest, depth)) {
name_fini(dest);
sub->sub_len--;
return (B_FALSE);
}
const str_pair_t *src_sp = name_at(n, depth - 1);
for (size_t i = 0; i < depth; i++, src_sp++) {
str_pair_t copy = { 0 };
(void) str_pair_init(©, n->nm_ops);
if (!str_pair_copy(src_sp, ©)) {
str_pair_fini(©);
name_fini(dest);
return (B_FALSE);
}
VERIFY(name_add_str(dest, ©.strp_l, ©.strp_r));
}
return (B_TRUE);
}
boolean_t
sub_substitute(const sub_t *sub, size_t idx, name_t *n)
{
VERIFY3U(idx, <, sub->sub_len);
const name_t *src = &sub->sub_items[idx];
const str_pair_t *sp = src->nm_items;
size_t save = name_len(n);
for (size_t i = 0; i < src->nm_len; i++, sp++) {
str_pair_t copy = { 0 };
if (!str_pair_copy(sp, ©))
goto fail;
if (!name_add_str(n, ©.strp_l, ©.strp_r))
goto fail;
}
return (B_TRUE);
fail:
for (size_t i = 0; i < name_len(n) - save; i++)
name_pop(n, NULL);
return (B_FALSE);
}
void
sub_pop(sub_t *sub)
{
name_t *top = &sub->sub_items[--sub->sub_len];
name_fini(top);
}
static boolean_t
templ_reserve(templ_t *tpl, size_t n)
{
if (tpl->tpl_len + n < tpl->tpl_size)
return (B_TRUE);
size_t newsize = tpl->tpl_size + CHUNK_SIZE;
void *temp = xrealloc(tpl->tpl_ops, tpl->tpl_items,
tpl->tpl_size * sizeof (sub_t), newsize * sizeof (sub_t));
if (temp == NULL)
return (B_FALSE);
tpl->tpl_items = temp;
tpl->tpl_size = newsize;
return (B_TRUE);
}
void
templ_init(templ_t *tpl, sysdem_ops_t *ops)
{
(void) memset(tpl, 0, sizeof (*tpl));
tpl->tpl_ops = ops;
}
void
templ_fini(templ_t *tpl)
{
if (tpl == NULL)
return;
for (size_t i = 0; i < tpl->tpl_len; i++)
sub_fini(&tpl->tpl_items[i]);
xfree(tpl->tpl_ops, tpl->tpl_items, tpl->tpl_size * sizeof (sub_t));
sysdem_ops_t *ops = tpl->tpl_ops;
(void) memset(tpl, 0, sizeof (*tpl));
tpl->tpl_ops = ops;
}
boolean_t
templ_push(templ_t *tpl)
{
if (!templ_reserve(tpl, 1))
return (B_FALSE);
sub_t *sub = &tpl->tpl_items[tpl->tpl_len++];
sub_init(sub, tpl->tpl_ops);
return (B_TRUE);
}
void
templ_pop(templ_t *tpl)
{
VERIFY(!templ_empty(tpl));
sub_t *sub = &tpl->tpl_items[--tpl->tpl_len];
sub_fini(sub);
}
sub_t *
templ_top(templ_t *tpl)
{
if (tpl->tpl_len == 0)
return (NULL);
return (&tpl->tpl_items[tpl->tpl_len - 1]);
}
boolean_t
templ_empty(const templ_t *tpl)
{
return ((tpl->tpl_len == 0) ? B_TRUE : B_FALSE);
}
size_t
templ_top_len(const templ_t *tpl)
{
const sub_t *sub = templ_top((templ_t *)tpl);
return (sub->sub_len);
}
boolean_t
templ_sub(const templ_t *tpl, size_t idx, name_t *n)
{
const sub_t *sub = templ_top((templ_t *)tpl);
return (sub_substitute(sub, idx, n));
}
boolean_t
templ_save(const name_t *n, size_t amt, templ_t *tpl)
{
VERIFY3U(tpl->tpl_len, >, 0);
sub_t *s = templ_top(tpl);
boolean_t res = B_TRUE;
if (amt == 0) {
name_t name = { 0 };
name_init(&name, tpl->tpl_ops);
res &= name_add(&name, "", 0, "", 0);
if (res)
res &= sub_save(s, &name, 1);
name_fini(&name);
} else {
res &= sub_save(s, n, amt);
}
return (res);
}