11f0c339eSJason King /*
21f0c339eSJason King * This file and its contents are supplied under the terms of the
31f0c339eSJason King * Common Development and Distribution License ("CDDL"), version 1.0.
41f0c339eSJason King * You may only use this file in accordance with the terms of version
51f0c339eSJason King * 1.0 of the CDDL.
61f0c339eSJason King *
71f0c339eSJason King * A full copy of the text of the CDDL should have accompanied this
81f0c339eSJason King * source. A copy of the CDDL is also available via the Internet at
91f0c339eSJason King * http://www.illumos.org/license/CDDL.
101f0c339eSJason King */
111f0c339eSJason King
121f0c339eSJason King /*
131f0c339eSJason King * String utility functions with dynamic memory management.
141f0c339eSJason King */
151f0c339eSJason King
161f0c339eSJason King /*
17*00e04c5dSJoshua M. Clulow * Copyright 2019 Joyent, Inc.
181f0c339eSJason King */
191f0c339eSJason King
201f0c339eSJason King #include <stdlib.h>
211f0c339eSJason King #include <err.h>
221f0c339eSJason King #include <errno.h>
231f0c339eSJason King #include <string.h>
241f0c339eSJason King #include <stdio.h>
251f0c339eSJason King #include <stdarg.h>
261f0c339eSJason King #include <sys/debug.h>
271f0c339eSJason King
281f0c339eSJason King #include "libcustr.h"
291f0c339eSJason King
301f0c339eSJason King typedef enum {
311f0c339eSJason King CUSTR_FIXEDBUF = 0x01
321f0c339eSJason King } custr_flags_t;
331f0c339eSJason King
341f0c339eSJason King struct custr {
351f0c339eSJason King size_t cus_strlen;
361f0c339eSJason King size_t cus_datalen;
371f0c339eSJason King char *cus_data;
381f0c339eSJason King custr_flags_t cus_flags;
391f0c339eSJason King };
401f0c339eSJason King
411f0c339eSJason King #define STRING_CHUNK_SIZE 64
421f0c339eSJason King
431f0c339eSJason King void
custr_reset(custr_t * cus)441f0c339eSJason King custr_reset(custr_t *cus)
451f0c339eSJason King {
461f0c339eSJason King if (cus->cus_data == NULL)
471f0c339eSJason King return;
481f0c339eSJason King
491f0c339eSJason King cus->cus_strlen = 0;
501f0c339eSJason King cus->cus_data[0] = '\0';
511f0c339eSJason King }
521f0c339eSJason King
531f0c339eSJason King size_t
custr_len(custr_t * cus)541f0c339eSJason King custr_len(custr_t *cus)
551f0c339eSJason King {
561f0c339eSJason King return (cus->cus_strlen);
571f0c339eSJason King }
581f0c339eSJason King
591f0c339eSJason King const char *
custr_cstr(custr_t * cus)601f0c339eSJason King custr_cstr(custr_t *cus)
611f0c339eSJason King {
621f0c339eSJason King if (cus->cus_data == NULL) {
631f0c339eSJason King VERIFY(cus->cus_strlen == 0);
641f0c339eSJason King VERIFY(cus->cus_datalen == 0);
651f0c339eSJason King
661f0c339eSJason King /*
671f0c339eSJason King * This function should never return NULL. If no buffer has
681f0c339eSJason King * been allocated, return a pointer to a zero-length string.
691f0c339eSJason King */
701f0c339eSJason King return ("");
711f0c339eSJason King }
721f0c339eSJason King return (cus->cus_data);
731f0c339eSJason King }
741f0c339eSJason King
751f0c339eSJason King int
custr_append_vprintf(custr_t * cus,const char * fmt,va_list ap)761f0c339eSJason King custr_append_vprintf(custr_t *cus, const char *fmt, va_list ap)
771f0c339eSJason King {
781f0c339eSJason King int len = vsnprintf(NULL, 0, fmt, ap);
791f0c339eSJason King size_t chunksz = STRING_CHUNK_SIZE;
801f0c339eSJason King
81*00e04c5dSJoshua M. Clulow if (len < 0) {
821f0c339eSJason King return (len);
83*00e04c5dSJoshua M. Clulow }
841f0c339eSJason King
851f0c339eSJason King while (chunksz < len) {
861f0c339eSJason King chunksz *= 2;
871f0c339eSJason King }
881f0c339eSJason King
891f0c339eSJason King if (len + cus->cus_strlen + 1 >= cus->cus_datalen) {
901f0c339eSJason King char *new_data;
911f0c339eSJason King size_t new_datalen = cus->cus_datalen + chunksz;
921f0c339eSJason King
931f0c339eSJason King if (cus->cus_flags & CUSTR_FIXEDBUF) {
941f0c339eSJason King errno = EOVERFLOW;
951f0c339eSJason King return (-1);
961f0c339eSJason King }
971f0c339eSJason King
981f0c339eSJason King /*
991f0c339eSJason King * Allocate replacement memory:
1001f0c339eSJason King */
1011f0c339eSJason King if ((new_data = malloc(new_datalen)) == NULL) {
1021f0c339eSJason King return (-1);
1031f0c339eSJason King }
1041f0c339eSJason King
1051f0c339eSJason King /*
1061f0c339eSJason King * Copy existing data into replacement memory and free
1071f0c339eSJason King * the old memory.
1081f0c339eSJason King */
1091f0c339eSJason King if (cus->cus_data != NULL) {
1101f0c339eSJason King (void) memcpy(new_data, cus->cus_data,
1111f0c339eSJason King cus->cus_strlen + 1);
1121f0c339eSJason King free(cus->cus_data);
1131f0c339eSJason King }
1141f0c339eSJason King
1151f0c339eSJason King /*
1161f0c339eSJason King * Swap in the replacement buffer:
1171f0c339eSJason King */
1181f0c339eSJason King cus->cus_data = new_data;
1191f0c339eSJason King cus->cus_datalen = new_datalen;
1201f0c339eSJason King }
1211f0c339eSJason King /*
1221f0c339eSJason King * Append new string to existing string:
1231f0c339eSJason King */
124*00e04c5dSJoshua M. Clulow if ((len = vsnprintf(cus->cus_data + cus->cus_strlen,
125*00e04c5dSJoshua M. Clulow cus->cus_datalen - cus->cus_strlen, fmt, ap)) < 0) {
1261f0c339eSJason King return (len);
127*00e04c5dSJoshua M. Clulow }
1281f0c339eSJason King cus->cus_strlen += len;
1291f0c339eSJason King
1301f0c339eSJason King return (0);
1311f0c339eSJason King }
1321f0c339eSJason King
1331f0c339eSJason King int
custr_appendc(custr_t * cus,char newc)1341f0c339eSJason King custr_appendc(custr_t *cus, char newc)
1351f0c339eSJason King {
1361f0c339eSJason King return (custr_append_printf(cus, "%c", newc));
1371f0c339eSJason King }
1381f0c339eSJason King
1391f0c339eSJason King int
custr_append_printf(custr_t * cus,const char * fmt,...)1401f0c339eSJason King custr_append_printf(custr_t *cus, const char *fmt, ...)
1411f0c339eSJason King {
1421f0c339eSJason King va_list ap;
1431f0c339eSJason King int ret;
1441f0c339eSJason King
1451f0c339eSJason King va_start(ap, fmt);
1461f0c339eSJason King ret = custr_append_vprintf(cus, fmt, ap);
1471f0c339eSJason King va_end(ap);
1481f0c339eSJason King
1491f0c339eSJason King return (ret);
1501f0c339eSJason King }
1511f0c339eSJason King
1521f0c339eSJason King int
custr_append(custr_t * cus,const char * name)1531f0c339eSJason King custr_append(custr_t *cus, const char *name)
1541f0c339eSJason King {
1551f0c339eSJason King return (custr_append_printf(cus, "%s", name));
1561f0c339eSJason King }
1571f0c339eSJason King
1581f0c339eSJason King int
custr_alloc(custr_t ** cus)1591f0c339eSJason King custr_alloc(custr_t **cus)
1601f0c339eSJason King {
1611f0c339eSJason King custr_t *t;
1621f0c339eSJason King
1631f0c339eSJason King if ((t = calloc(1, sizeof (*t))) == NULL) {
1641f0c339eSJason King *cus = NULL;
1651f0c339eSJason King return (-1);
1661f0c339eSJason King }
1671f0c339eSJason King
1681f0c339eSJason King *cus = t;
1691f0c339eSJason King return (0);
1701f0c339eSJason King }
1711f0c339eSJason King
1721f0c339eSJason King int
custr_alloc_buf(custr_t ** cus,void * buf,size_t buflen)1731f0c339eSJason King custr_alloc_buf(custr_t **cus, void *buf, size_t buflen)
1741f0c339eSJason King {
1751f0c339eSJason King int ret;
1761f0c339eSJason King
1771f0c339eSJason King if (buflen == 0 || buf == NULL) {
1781f0c339eSJason King errno = EINVAL;
1791f0c339eSJason King return (-1);
1801f0c339eSJason King }
1811f0c339eSJason King
1821f0c339eSJason King if ((ret = custr_alloc(cus)) != 0)
1831f0c339eSJason King return (ret);
1841f0c339eSJason King
1851f0c339eSJason King (*cus)->cus_data = buf;
1861f0c339eSJason King (*cus)->cus_datalen = buflen;
1871f0c339eSJason King (*cus)->cus_strlen = 0;
1881f0c339eSJason King (*cus)->cus_flags = CUSTR_FIXEDBUF;
1891f0c339eSJason King (*cus)->cus_data[0] = '\0';
1901f0c339eSJason King
1911f0c339eSJason King return (0);
1921f0c339eSJason King }
1931f0c339eSJason King
1941f0c339eSJason King void
custr_free(custr_t * cus)1951f0c339eSJason King custr_free(custr_t *cus)
1961f0c339eSJason King {
1971f0c339eSJason King if (cus == NULL)
1981f0c339eSJason King return;
1991f0c339eSJason King
2001f0c339eSJason King if ((cus->cus_flags & CUSTR_FIXEDBUF) == 0)
2011f0c339eSJason King free(cus->cus_data);
2021f0c339eSJason King free(cus);
2031f0c339eSJason King }
204