#ifdef _KERNEL
#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/stdbool.h>
#include <sys/varargs.h>
#include <sys/systm.h>
#else
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdarg.h>
#include <strings.h>
#endif
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/ilstr.h>
#ifdef _KERNEL
#define bmove(src, dst, len) ovbcopy(src, dst, len)
#else
#define bmove(src, dst, len) bcopy(src, dst, len)
#endif
static bool ilstr_have_space(ilstr_t *, size_t);
void
ilstr_init(ilstr_t *ils, int kmflag)
{
#ifdef _KERNEL
if (!kmem_ready) {
panic("ilstr_init() cannot be used before kmem is ready");
}
#endif
bzero(ils, sizeof (*ils));
ils->ils_kmflag = kmflag;
}
void
ilstr_init_prealloc(ilstr_t *ils, char *buf, size_t buflen)
{
bzero(ils, sizeof (*ils));
ils->ils_data = buf;
ils->ils_datalen = buflen;
ils->ils_data[0] = '\0';
ils->ils_flag |= ILSTR_FLAG_PREALLOC;
}
void
ilstr_reset(ilstr_t *ils)
{
if (ils->ils_strlen > 0) {
ils->ils_data[0] = '\0';
ils->ils_strlen = 0;
}
ils->ils_errno = ILSTR_ERROR_OK;
}
void
ilstr_fini(ilstr_t *ils)
{
if (!(ils->ils_flag & ILSTR_FLAG_PREALLOC)) {
if (ils->ils_data != NULL) {
#ifdef _KERNEL
kmem_free(ils->ils_data, ils->ils_datalen);
#else
free(ils->ils_data);
#endif
}
}
bzero(ils, sizeof (*ils));
}
void
ilstr_prepend_str(ilstr_t *ils, const char *s)
{
size_t len;
if (ils->ils_errno != ILSTR_ERROR_OK) {
return;
}
if ((len = strlen(s)) < 1) {
return;
}
if (!ilstr_have_space(ils, len)) {
return;
}
bmove(ils->ils_data, ils->ils_data + len, ils->ils_strlen + 1);
bcopy(s, ils->ils_data, len);
ils->ils_strlen += len;
}
void
ilstr_append_str(ilstr_t *ils, const char *s)
{
size_t len;
if (ils->ils_errno != ILSTR_ERROR_OK) {
return;
}
if ((len = strlen(s)) < 1) {
return;
}
if (!ilstr_have_space(ils, len)) {
return;
}
bcopy(s, ils->ils_data + ils->ils_strlen, len + 1);
ils->ils_strlen += len;
}
static bool
ilstr_have_space(ilstr_t *ils, size_t needbytes)
{
size_t chunksz = 64;
if (ils->ils_datalen > 3 * chunksz) {
chunksz = P2ROUNDUP(ils->ils_datalen / 3, 64);
}
if (needbytes >= SIZE_MAX - ils->ils_strlen - 1) {
ils->ils_errno = ILSTR_ERROR_OVERFLOW;
return (false);
}
size_t new_strlen = ils->ils_strlen + needbytes;
if (new_strlen + 1 > ils->ils_datalen) {
size_t new_datalen = ils->ils_datalen;
char *new_data;
if (ils->ils_flag & ILSTR_FLAG_PREALLOC) {
ils->ils_errno = ILSTR_ERROR_NOMEM;
return (false);
}
while (new_datalen < new_strlen + 1) {
if (chunksz >= SIZE_MAX - new_datalen) {
ils->ils_errno = ILSTR_ERROR_OVERFLOW;
return (false);
}
new_datalen += chunksz;
}
#ifdef _KERNEL
new_data = kmem_alloc(new_datalen, ils->ils_kmflag);
#else
new_data = malloc(new_datalen);
#endif
if (new_data == NULL) {
ils->ils_errno = ILSTR_ERROR_NOMEM;
return (false);
}
if (ils->ils_data != NULL) {
bcopy(ils->ils_data, new_data, ils->ils_strlen + 1);
#ifdef _KERNEL
kmem_free(ils->ils_data, ils->ils_datalen);
#else
free(ils->ils_data);
#endif
} else {
new_data[0] = '\0';
}
ils->ils_data = new_data;
ils->ils_datalen = new_datalen;
}
return (true);
}
void
ilstr_aprintf(ilstr_t *ils, const char *fmt, ...)
{
va_list ap;
if (ils->ils_errno != ILSTR_ERROR_OK) {
return;
}
va_start(ap, fmt);
ilstr_vaprintf(ils, fmt, ap);
va_end(ap);
}
void
ilstr_vaprintf(ilstr_t *ils, const char *fmt, va_list ap)
{
if (ils->ils_errno != ILSTR_ERROR_OK) {
return;
}
va_list tap;
va_copy(tap, ap);
#ifdef _KERNEL
size_t len;
#else
int len;
#endif
len = vsnprintf(NULL, 0, fmt, tap);
#ifndef _KERNEL
if (len < 0) {
ils->ils_errno = ILSTR_ERROR_PRINTF;
return;
}
#endif
if (!ilstr_have_space(ils, len)) {
return;
}
len = vsnprintf(ils->ils_data + ils->ils_strlen, len + 1, fmt, ap);
#ifndef _KERNEL
if (len < 0) {
ils->ils_errno = ILSTR_ERROR_PRINTF;
return;
}
#endif
ils->ils_strlen += len;
}
void
ilstr_append_char(ilstr_t *ils, char c)
{
char buf[2];
if (ils->ils_errno != ILSTR_ERROR_OK) {
return;
}
buf[0] = c;
buf[1] = '\0';
ilstr_append_str(ils, buf);
}
void
ilstr_prepend_char(ilstr_t *ils, char c)
{
char buf[2];
if (ils->ils_errno != ILSTR_ERROR_OK) {
return;
}
buf[0] = c;
buf[1] = '\0';
ilstr_prepend_str(ils, buf);
}
ilstr_errno_t
ilstr_errno(ilstr_t *ils)
{
return (ils->ils_errno);
}
const char *
ilstr_cstr(ilstr_t *ils)
{
if (ils->ils_data == NULL) {
VERIFY3U(ils->ils_datalen, ==, 0);
VERIFY3U(ils->ils_strlen, ==, 0);
return ("");
}
return (ils->ils_data);
}
size_t
ilstr_len(ilstr_t *ils)
{
return (ils->ils_strlen);
}
bool
ilstr_is_empty(ilstr_t *ils)
{
return (ilstr_len(ils) == 0);
}
const char *
ilstr_errstr(ilstr_t *ils)
{
switch (ils->ils_errno) {
case ILSTR_ERROR_OK:
return ("ok");
case ILSTR_ERROR_NOMEM:
return ("could not allocate memory");
case ILSTR_ERROR_OVERFLOW:
return ("tried to construct too large a string");
case ILSTR_ERROR_PRINTF:
return ("invalid printf arguments");
default:
return ("unknown error");
}
}