1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * String utility functions with dynamic memory management. 14 */ 15 16 /* 17 * Copyright 2019 Joyent, Inc. 18 */ 19 20 #include <stdlib.h> 21 #include <err.h> 22 #include <errno.h> 23 #include <string.h> 24 #include <stdio.h> 25 #include <stdarg.h> 26 #include <sys/debug.h> 27 28 #include "libcustr.h" 29 30 typedef enum { 31 CUSTR_FIXEDBUF = 0x01 32 } custr_flags_t; 33 34 struct custr { 35 size_t cus_strlen; 36 size_t cus_datalen; 37 char *cus_data; 38 custr_flags_t cus_flags; 39 }; 40 41 #define STRING_CHUNK_SIZE 64 42 43 void 44 custr_reset(custr_t *cus) 45 { 46 if (cus->cus_data == NULL) 47 return; 48 49 cus->cus_strlen = 0; 50 cus->cus_data[0] = '\0'; 51 } 52 53 size_t 54 custr_len(custr_t *cus) 55 { 56 return (cus->cus_strlen); 57 } 58 59 const char * 60 custr_cstr(custr_t *cus) 61 { 62 if (cus->cus_data == NULL) { 63 VERIFY(cus->cus_strlen == 0); 64 VERIFY(cus->cus_datalen == 0); 65 66 /* 67 * This function should never return NULL. If no buffer has 68 * been allocated, return a pointer to a zero-length string. 69 */ 70 return (""); 71 } 72 return (cus->cus_data); 73 } 74 75 int 76 custr_append_vprintf(custr_t *cus, const char *fmt, va_list ap) 77 { 78 int len = vsnprintf(NULL, 0, fmt, ap); 79 size_t chunksz = STRING_CHUNK_SIZE; 80 81 if (len < 0) { 82 return (len); 83 } 84 85 while (chunksz < len) { 86 chunksz *= 2; 87 } 88 89 if (len + cus->cus_strlen + 1 >= cus->cus_datalen) { 90 char *new_data; 91 size_t new_datalen = cus->cus_datalen + chunksz; 92 93 if (cus->cus_flags & CUSTR_FIXEDBUF) { 94 errno = EOVERFLOW; 95 return (-1); 96 } 97 98 /* 99 * Allocate replacement memory: 100 */ 101 if ((new_data = malloc(new_datalen)) == NULL) { 102 return (-1); 103 } 104 105 /* 106 * Copy existing data into replacement memory and free 107 * the old memory. 108 */ 109 if (cus->cus_data != NULL) { 110 (void) memcpy(new_data, cus->cus_data, 111 cus->cus_strlen + 1); 112 free(cus->cus_data); 113 } 114 115 /* 116 * Swap in the replacement buffer: 117 */ 118 cus->cus_data = new_data; 119 cus->cus_datalen = new_datalen; 120 } 121 /* 122 * Append new string to existing string: 123 */ 124 if ((len = vsnprintf(cus->cus_data + cus->cus_strlen, 125 cus->cus_datalen - cus->cus_strlen, fmt, ap)) < 0) { 126 return (len); 127 } 128 cus->cus_strlen += len; 129 130 return (0); 131 } 132 133 int 134 custr_appendc(custr_t *cus, char newc) 135 { 136 return (custr_append_printf(cus, "%c", newc)); 137 } 138 139 int 140 custr_append_printf(custr_t *cus, const char *fmt, ...) 141 { 142 va_list ap; 143 int ret; 144 145 va_start(ap, fmt); 146 ret = custr_append_vprintf(cus, fmt, ap); 147 va_end(ap); 148 149 return (ret); 150 } 151 152 int 153 custr_append(custr_t *cus, const char *name) 154 { 155 return (custr_append_printf(cus, "%s", name)); 156 } 157 158 int 159 custr_alloc(custr_t **cus) 160 { 161 custr_t *t; 162 163 if ((t = calloc(1, sizeof (*t))) == NULL) { 164 *cus = NULL; 165 return (-1); 166 } 167 168 *cus = t; 169 return (0); 170 } 171 172 int 173 custr_alloc_buf(custr_t **cus, void *buf, size_t buflen) 174 { 175 int ret; 176 177 if (buflen == 0 || buf == NULL) { 178 errno = EINVAL; 179 return (-1); 180 } 181 182 if ((ret = custr_alloc(cus)) != 0) 183 return (ret); 184 185 (*cus)->cus_data = buf; 186 (*cus)->cus_datalen = buflen; 187 (*cus)->cus_strlen = 0; 188 (*cus)->cus_flags = CUSTR_FIXEDBUF; 189 (*cus)->cus_data[0] = '\0'; 190 191 return (0); 192 } 193 194 void 195 custr_free(custr_t *cus) 196 { 197 if (cus == NULL) 198 return; 199 200 if ((cus->cus_flags & CUSTR_FIXEDBUF) == 0) 201 free(cus->cus_data); 202 free(cus); 203 } 204