1196c7f05SJoshua M. Clulow /* 2196c7f05SJoshua M. Clulow * This file and its contents are supplied under the terms of the 3196c7f05SJoshua M. Clulow * Common Development and Distribution License ("CDDL"), version 1.0. 4196c7f05SJoshua M. Clulow * You may only use this file in accordance with the terms of version 5196c7f05SJoshua M. Clulow * 1.0 of the CDDL. 6196c7f05SJoshua M. Clulow * 7196c7f05SJoshua M. Clulow * A full copy of the text of the CDDL should have accompanied this 8196c7f05SJoshua M. Clulow * source. A copy of the CDDL is also available via the Internet at 9196c7f05SJoshua M. Clulow * http://www.illumos.org/license/CDDL. 10196c7f05SJoshua M. Clulow */ 11196c7f05SJoshua M. Clulow 12196c7f05SJoshua M. Clulow /* 13196c7f05SJoshua M. Clulow * String utility functions with dynamic memory management. 14196c7f05SJoshua M. Clulow */ 15196c7f05SJoshua M. Clulow 16196c7f05SJoshua M. Clulow /* 1709cb6b0fSJoshua M. Clulow * Copyright 2019 Joyent, Inc. 18196c7f05SJoshua M. Clulow */ 19196c7f05SJoshua M. Clulow 20196c7f05SJoshua M. Clulow #include <stdlib.h> 21196c7f05SJoshua M. Clulow #include <err.h> 22500cf85bSJason King #include <errno.h> 23196c7f05SJoshua M. Clulow #include <string.h> 24fc2512cfSRobert Mustacchi #include <stdio.h> 25fc2512cfSRobert Mustacchi #include <stdarg.h> 26fc2512cfSRobert Mustacchi #include <sys/debug.h> 27196c7f05SJoshua M. Clulow 28500cf85bSJason King #include "libcustr.h" 29196c7f05SJoshua M. Clulow 30*773dbec3SJason King /* 31*773dbec3SJason King * libcustr is used by some things in usr/src/tools. If we are building 32*773dbec3SJason King * on an older platform, __unused might not be defined on the build host. 33*773dbec3SJason King * We define it here if needed. 34*773dbec3SJason King */ 35*773dbec3SJason King #ifndef __unused 36*773dbec3SJason King #if __GNUC_VERSION >= 20700 37*773dbec3SJason King #define __unused __attribute__((_unused__)) 38*773dbec3SJason King #else 39*773dbec3SJason King #define __unused 40*773dbec3SJason King #endif /* __GNUC_VERSION */ 41*773dbec3SJason King #endif /* __unused */ 42*773dbec3SJason King 43fc2512cfSRobert Mustacchi typedef enum { 44fc2512cfSRobert Mustacchi CUSTR_FIXEDBUF = 0x01 45fc2512cfSRobert Mustacchi } custr_flags_t; 46fc2512cfSRobert Mustacchi 47196c7f05SJoshua M. Clulow struct custr { 48196c7f05SJoshua M. Clulow size_t cus_strlen; 49196c7f05SJoshua M. Clulow size_t cus_datalen; 50196c7f05SJoshua M. Clulow char *cus_data; 51fc2512cfSRobert Mustacchi custr_flags_t cus_flags; 52*773dbec3SJason King custr_alloc_t *cus_alloc; 53196c7f05SJoshua M. Clulow }; 54*773dbec3SJason King #define CUSTR_ALLOC(_cus, _len) \ 55*773dbec3SJason King (_cus)->cus_alloc->cua_ops->custr_ao_alloc((_cus)->cus_alloc, (_len)) 56*773dbec3SJason King #define CUSTR_FREE(_cus, _p, _len) \ 57*773dbec3SJason King (_cus)->cus_alloc->cua_ops->custr_ao_free((_cus)->cus_alloc, \ 58*773dbec3SJason King (_p), (_len)) 59196c7f05SJoshua M. Clulow 60196c7f05SJoshua M. Clulow #define STRING_CHUNK_SIZE 64 61196c7f05SJoshua M. Clulow 62*773dbec3SJason King static void *custr_def_alloc(custr_alloc_t *, size_t); 63*773dbec3SJason King static void custr_def_free(custr_alloc_t *, void *, size_t); 64*773dbec3SJason King 65*773dbec3SJason King static custr_alloc_ops_t custr_alloc_ops_default = { 66*773dbec3SJason King NULL, /* custr_ao_init */ 67*773dbec3SJason King NULL, /* custr_ao_fini */ 68*773dbec3SJason King custr_def_alloc, /* custr_ao_alloc */ 69*773dbec3SJason King custr_def_free /* custr_ao_free */ 70*773dbec3SJason King }; 71*773dbec3SJason King 72*773dbec3SJason King static custr_alloc_t custr_alloc_default = { 73*773dbec3SJason King CUSTR_VERSION, /* cua_version */ 74*773dbec3SJason King &custr_alloc_ops_default, /* cua_ops */ 75*773dbec3SJason King NULL /* cua_arg */ 76*773dbec3SJason King }; 77*773dbec3SJason King 78196c7f05SJoshua M. Clulow void 79196c7f05SJoshua M. Clulow custr_reset(custr_t *cus) 80196c7f05SJoshua M. Clulow { 81196c7f05SJoshua M. Clulow if (cus->cus_data == NULL) 82196c7f05SJoshua M. Clulow return; 83196c7f05SJoshua M. Clulow 84196c7f05SJoshua M. Clulow cus->cus_strlen = 0; 85196c7f05SJoshua M. Clulow cus->cus_data[0] = '\0'; 86196c7f05SJoshua M. Clulow } 87196c7f05SJoshua M. Clulow 88196c7f05SJoshua M. Clulow size_t 89196c7f05SJoshua M. Clulow custr_len(custr_t *cus) 90196c7f05SJoshua M. Clulow { 91196c7f05SJoshua M. Clulow return (cus->cus_strlen); 92196c7f05SJoshua M. Clulow } 93196c7f05SJoshua M. Clulow 94196c7f05SJoshua M. Clulow const char * 95196c7f05SJoshua M. Clulow custr_cstr(custr_t *cus) 96196c7f05SJoshua M. Clulow { 97fc2512cfSRobert Mustacchi if (cus->cus_data == NULL) { 98fc2512cfSRobert Mustacchi VERIFY(cus->cus_strlen == 0); 99fc2512cfSRobert Mustacchi VERIFY(cus->cus_datalen == 0); 100196c7f05SJoshua M. Clulow 101fc2512cfSRobert Mustacchi /* 102fc2512cfSRobert Mustacchi * This function should never return NULL. If no buffer has 103fc2512cfSRobert Mustacchi * been allocated, return a pointer to a zero-length string. 104fc2512cfSRobert Mustacchi */ 105fc2512cfSRobert Mustacchi return (""); 106fc2512cfSRobert Mustacchi } 107fc2512cfSRobert Mustacchi return (cus->cus_data); 108196c7f05SJoshua M. Clulow } 109196c7f05SJoshua M. Clulow 110196c7f05SJoshua M. Clulow int 111fc2512cfSRobert Mustacchi custr_append_vprintf(custr_t *cus, const char *fmt, va_list ap) 112196c7f05SJoshua M. Clulow { 113fc2512cfSRobert Mustacchi int len = vsnprintf(NULL, 0, fmt, ap); 114196c7f05SJoshua M. Clulow size_t chunksz = STRING_CHUNK_SIZE; 115196c7f05SJoshua M. Clulow 11609cb6b0fSJoshua M. Clulow if (len < 0) { 117fc2512cfSRobert Mustacchi return (len); 11809cb6b0fSJoshua M. Clulow } 119fc2512cfSRobert Mustacchi 120196c7f05SJoshua M. Clulow while (chunksz < len) { 121196c7f05SJoshua M. Clulow chunksz *= 2; 122196c7f05SJoshua M. Clulow } 123196c7f05SJoshua M. Clulow 124196c7f05SJoshua M. Clulow if (len + cus->cus_strlen + 1 >= cus->cus_datalen) { 125196c7f05SJoshua M. Clulow char *new_data; 126196c7f05SJoshua M. Clulow size_t new_datalen = cus->cus_datalen + chunksz; 127196c7f05SJoshua M. Clulow 128fc2512cfSRobert Mustacchi if (cus->cus_flags & CUSTR_FIXEDBUF) { 129fc2512cfSRobert Mustacchi errno = EOVERFLOW; 130fc2512cfSRobert Mustacchi return (-1); 131fc2512cfSRobert Mustacchi } 132fc2512cfSRobert Mustacchi 133196c7f05SJoshua M. Clulow /* 134196c7f05SJoshua M. Clulow * Allocate replacement memory: 135196c7f05SJoshua M. Clulow */ 136*773dbec3SJason King if ((new_data = CUSTR_ALLOC(cus, new_datalen)) == NULL) { 137196c7f05SJoshua M. Clulow return (-1); 138196c7f05SJoshua M. Clulow } 139196c7f05SJoshua M. Clulow 140196c7f05SJoshua M. Clulow /* 141196c7f05SJoshua M. Clulow * Copy existing data into replacement memory and free 142196c7f05SJoshua M. Clulow * the old memory. 143196c7f05SJoshua M. Clulow */ 144196c7f05SJoshua M. Clulow if (cus->cus_data != NULL) { 145196c7f05SJoshua M. Clulow (void) memcpy(new_data, cus->cus_data, 146196c7f05SJoshua M. Clulow cus->cus_strlen + 1); 147*773dbec3SJason King CUSTR_FREE(cus, cus->cus_data, cus->cus_datalen); 148196c7f05SJoshua M. Clulow } 149196c7f05SJoshua M. Clulow 150196c7f05SJoshua M. Clulow /* 151196c7f05SJoshua M. Clulow * Swap in the replacement buffer: 152196c7f05SJoshua M. Clulow */ 153196c7f05SJoshua M. Clulow cus->cus_data = new_data; 154196c7f05SJoshua M. Clulow cus->cus_datalen = new_datalen; 155196c7f05SJoshua M. Clulow } 156196c7f05SJoshua M. Clulow /* 157196c7f05SJoshua M. Clulow * Append new string to existing string: 158196c7f05SJoshua M. Clulow */ 15909cb6b0fSJoshua M. Clulow if ((len = vsnprintf(cus->cus_data + cus->cus_strlen, 16009cb6b0fSJoshua M. Clulow cus->cus_datalen - cus->cus_strlen, fmt, ap)) < 0) { 161fc2512cfSRobert Mustacchi return (len); 16209cb6b0fSJoshua M. Clulow } 163196c7f05SJoshua M. Clulow cus->cus_strlen += len; 164196c7f05SJoshua M. Clulow 165196c7f05SJoshua M. Clulow return (0); 166196c7f05SJoshua M. Clulow } 167196c7f05SJoshua M. Clulow 168fc2512cfSRobert Mustacchi int 169fc2512cfSRobert Mustacchi custr_appendc(custr_t *cus, char newc) 170fc2512cfSRobert Mustacchi { 171fc2512cfSRobert Mustacchi return (custr_append_printf(cus, "%c", newc)); 172fc2512cfSRobert Mustacchi } 173fc2512cfSRobert Mustacchi 174fc2512cfSRobert Mustacchi int 175fc2512cfSRobert Mustacchi custr_append_printf(custr_t *cus, const char *fmt, ...) 176fc2512cfSRobert Mustacchi { 177fc2512cfSRobert Mustacchi va_list ap; 178fc2512cfSRobert Mustacchi int ret; 179fc2512cfSRobert Mustacchi 180fc2512cfSRobert Mustacchi va_start(ap, fmt); 181fc2512cfSRobert Mustacchi ret = custr_append_vprintf(cus, fmt, ap); 182fc2512cfSRobert Mustacchi va_end(ap); 183fc2512cfSRobert Mustacchi 184fc2512cfSRobert Mustacchi return (ret); 185fc2512cfSRobert Mustacchi } 186fc2512cfSRobert Mustacchi 187fc2512cfSRobert Mustacchi int 188fc2512cfSRobert Mustacchi custr_append(custr_t *cus, const char *name) 189fc2512cfSRobert Mustacchi { 190fc2512cfSRobert Mustacchi return (custr_append_printf(cus, "%s", name)); 191fc2512cfSRobert Mustacchi } 192fc2512cfSRobert Mustacchi 193196c7f05SJoshua M. Clulow int 194*773dbec3SJason King custr_alloc_init(custr_alloc_t *cua, const custr_alloc_ops_t *ops, ...) 195*773dbec3SJason King { 196*773dbec3SJason King int ret = 0; 197*773dbec3SJason King 198*773dbec3SJason King if (cua->cua_version != CUSTR_VERSION || ops->custr_ao_alloc == NULL || 199*773dbec3SJason King ops->custr_ao_free == NULL) { 200*773dbec3SJason King errno = EINVAL; 201*773dbec3SJason King return (-1); 202*773dbec3SJason King } 203*773dbec3SJason King 204*773dbec3SJason King cua->cua_ops = ops; 205*773dbec3SJason King cua->cua_arg = NULL; 206*773dbec3SJason King 207*773dbec3SJason King if (ops->custr_ao_init != NULL) { 208*773dbec3SJason King va_list ap; 209*773dbec3SJason King 210*773dbec3SJason King va_start(ap, ops); 211*773dbec3SJason King ret = ops->custr_ao_init(cua, ap); 212*773dbec3SJason King va_end(ap); 213*773dbec3SJason King } 214*773dbec3SJason King 215*773dbec3SJason King return ((ret == 0) ? 0 : -1); 216*773dbec3SJason King } 217*773dbec3SJason King 218*773dbec3SJason King void 219*773dbec3SJason King custr_alloc_fini(custr_alloc_t *cua) 220*773dbec3SJason King { 221*773dbec3SJason King if (cua->cua_ops->custr_ao_fini != NULL) 222*773dbec3SJason King cua->cua_ops->custr_ao_fini(cua); 223*773dbec3SJason King } 224*773dbec3SJason King 225*773dbec3SJason King int 226*773dbec3SJason King custr_xalloc(custr_t **cus, custr_alloc_t *cao) 227196c7f05SJoshua M. Clulow { 228196c7f05SJoshua M. Clulow custr_t *t; 229196c7f05SJoshua M. Clulow 230*773dbec3SJason King if (cao == NULL) 231*773dbec3SJason King cao = &custr_alloc_default; 232*773dbec3SJason King 233*773dbec3SJason King if ((t = cao->cua_ops->custr_ao_alloc(cao, sizeof (*t))) == NULL) { 234196c7f05SJoshua M. Clulow *cus = NULL; 235196c7f05SJoshua M. Clulow return (-1); 236196c7f05SJoshua M. Clulow } 237*773dbec3SJason King (void) memset(t, 0, sizeof (*t)); 238196c7f05SJoshua M. Clulow 239*773dbec3SJason King t->cus_alloc = cao; 240196c7f05SJoshua M. Clulow *cus = t; 241196c7f05SJoshua M. Clulow return (0); 242196c7f05SJoshua M. Clulow } 243196c7f05SJoshua M. Clulow 244fc2512cfSRobert Mustacchi int 245*773dbec3SJason King custr_alloc(custr_t **cus) 246*773dbec3SJason King { 247*773dbec3SJason King return (custr_xalloc(cus, NULL)); 248*773dbec3SJason King } 249*773dbec3SJason King 250*773dbec3SJason King int 251*773dbec3SJason King custr_xalloc_buf(custr_t **cus, void *buf, size_t buflen, custr_alloc_t *cao) 252fc2512cfSRobert Mustacchi { 253fc2512cfSRobert Mustacchi int ret; 254fc2512cfSRobert Mustacchi 255fc2512cfSRobert Mustacchi if (buflen == 0 || buf == NULL) { 256fc2512cfSRobert Mustacchi errno = EINVAL; 257fc2512cfSRobert Mustacchi return (-1); 258fc2512cfSRobert Mustacchi } 259fc2512cfSRobert Mustacchi 260*773dbec3SJason King if ((ret = custr_xalloc(cus, cao)) != 0) 261fc2512cfSRobert Mustacchi return (ret); 262fc2512cfSRobert Mustacchi 263fc2512cfSRobert Mustacchi (*cus)->cus_data = buf; 264fc2512cfSRobert Mustacchi (*cus)->cus_datalen = buflen; 265fc2512cfSRobert Mustacchi (*cus)->cus_strlen = 0; 266fc2512cfSRobert Mustacchi (*cus)->cus_flags = CUSTR_FIXEDBUF; 267fc2512cfSRobert Mustacchi (*cus)->cus_data[0] = '\0'; 268fc2512cfSRobert Mustacchi 269fc2512cfSRobert Mustacchi return (0); 270fc2512cfSRobert Mustacchi } 271fc2512cfSRobert Mustacchi 272*773dbec3SJason King int 273*773dbec3SJason King custr_alloc_buf(custr_t **cus, void *buf, size_t buflen) 274*773dbec3SJason King { 275*773dbec3SJason King return (custr_xalloc_buf(cus, buf, buflen, NULL)); 276*773dbec3SJason King } 277*773dbec3SJason King 278196c7f05SJoshua M. Clulow void 279196c7f05SJoshua M. Clulow custr_free(custr_t *cus) 280196c7f05SJoshua M. Clulow { 281*773dbec3SJason King custr_alloc_t *cao; 282*773dbec3SJason King 283196c7f05SJoshua M. Clulow if (cus == NULL) 284196c7f05SJoshua M. Clulow return; 285196c7f05SJoshua M. Clulow 286fc2512cfSRobert Mustacchi if ((cus->cus_flags & CUSTR_FIXEDBUF) == 0) 287*773dbec3SJason King CUSTR_FREE(cus, cus->cus_data, cus->cus_datalen); 288*773dbec3SJason King 289*773dbec3SJason King cao = cus->cus_alloc; 290*773dbec3SJason King cao->cua_ops->custr_ao_free(cao, cus, sizeof (*cus)); 291*773dbec3SJason King } 292*773dbec3SJason King 293*773dbec3SJason King /*ARGSUSED*/ 294*773dbec3SJason King static void * 295*773dbec3SJason King custr_def_alloc(custr_alloc_t *cao __unused, size_t len) 296*773dbec3SJason King { 297*773dbec3SJason King return (malloc(len)); 298*773dbec3SJason King } 299*773dbec3SJason King 300*773dbec3SJason King /*ARGSUSED*/ 301*773dbec3SJason King static void 302*773dbec3SJason King custr_def_free(custr_alloc_t *cao __unused, void *p, size_t len __unused) 303*773dbec3SJason King { 304*773dbec3SJason King free(p); 305196c7f05SJoshua M. Clulow } 306