xref: /illumos-gate/usr/src/lib/libcustr/common/custr.c (revision 773dbec3)
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