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 2018 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 == -1) 82 return (len); 83 84 while (chunksz < len) { 85 chunksz *= 2; 86 } 87 88 if (len + cus->cus_strlen + 1 >= cus->cus_datalen) { 89 char *new_data; 90 size_t new_datalen = cus->cus_datalen + chunksz; 91 92 if (cus->cus_flags & CUSTR_FIXEDBUF) { 93 errno = EOVERFLOW; 94 return (-1); 95 } 96 97 /* 98 * Allocate replacement memory: 99 */ 100 if ((new_data = malloc(new_datalen)) == NULL) { 101 return (-1); 102 } 103 104 /* 105 * Copy existing data into replacement memory and free 106 * the old memory. 107 */ 108 if (cus->cus_data != NULL) { 109 (void) memcpy(new_data, cus->cus_data, 110 cus->cus_strlen + 1); 111 free(cus->cus_data); 112 } 113 114 /* 115 * Swap in the replacement buffer: 116 */ 117 cus->cus_data = new_data; 118 cus->cus_datalen = new_datalen; 119 } 120 /* 121 * Append new string to existing string: 122 */ 123 len = vsnprintf(cus->cus_data + cus->cus_strlen, 124 (uintptr_t)cus->cus_data - (uintptr_t)cus->cus_strlen, fmt, ap); 125 if (len == -1) 126 return (len); 127 cus->cus_strlen += len; 128 129 return (0); 130 } 131 132 int 133 custr_appendc(custr_t *cus, char newc) 134 { 135 return (custr_append_printf(cus, "%c", newc)); 136 } 137 138 int 139 custr_append_printf(custr_t *cus, const char *fmt, ...) 140 { 141 va_list ap; 142 int ret; 143 144 va_start(ap, fmt); 145 ret = custr_append_vprintf(cus, fmt, ap); 146 va_end(ap); 147 148 return (ret); 149 } 150 151 int 152 custr_append(custr_t *cus, const char *name) 153 { 154 return (custr_append_printf(cus, "%s", name)); 155 } 156 157 int 158 custr_alloc(custr_t **cus) 159 { 160 custr_t *t; 161 162 if ((t = calloc(1, sizeof (*t))) == NULL) { 163 *cus = NULL; 164 return (-1); 165 } 166 167 *cus = t; 168 return (0); 169 } 170 171 int 172 custr_alloc_buf(custr_t **cus, void *buf, size_t buflen) 173 { 174 int ret; 175 176 if (buflen == 0 || buf == NULL) { 177 errno = EINVAL; 178 return (-1); 179 } 180 181 if ((ret = custr_alloc(cus)) != 0) 182 return (ret); 183 184 (*cus)->cus_data = buf; 185 (*cus)->cus_datalen = buflen; 186 (*cus)->cus_strlen = 0; 187 (*cus)->cus_flags = CUSTR_FIXEDBUF; 188 (*cus)->cus_data[0] = '\0'; 189 190 return (0); 191 } 192 193 void 194 custr_free(custr_t *cus) 195 { 196 if (cus == NULL) 197 return; 198 199 if ((cus->cus_flags & CUSTR_FIXEDBUF) == 0) 200 free(cus->cus_data); 201 free(cus); 202 } 203