xref: /illumos-gate/usr/src/lib/libcustr/common/custr.c (revision 356ce177)
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 /*
17*356ce177SJason King  * Copyright 2020 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 
30773dbec3SJason King /*
31773dbec3SJason King  * libcustr is used by some things in usr/src/tools.  If we are building
32773dbec3SJason King  * on an older platform, __unused might not be defined on the build host.
33773dbec3SJason King  * We define it here if needed.
34773dbec3SJason King  */
35773dbec3SJason King #ifndef __unused
36773dbec3SJason King #if __GNUC_VERSION >= 20700
37773dbec3SJason King #define	__unused __attribute__((_unused__))
38773dbec3SJason King #else
39773dbec3SJason King #define	__unused
40773dbec3SJason King #endif /* __GNUC_VERSION */
41773dbec3SJason King #endif /* __unused */
42773dbec3SJason 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;
52773dbec3SJason King 	custr_alloc_t *cus_alloc;
53196c7f05SJoshua M. Clulow };
54773dbec3SJason King #define	CUSTR_ALLOC(_cus, _len) \
55773dbec3SJason King 	(_cus)->cus_alloc->cua_ops->custr_ao_alloc((_cus)->cus_alloc, (_len))
56773dbec3SJason King #define	CUSTR_FREE(_cus, _p, _len) \
57773dbec3SJason King 	(_cus)->cus_alloc->cua_ops->custr_ao_free((_cus)->cus_alloc, \
58773dbec3SJason King 	(_p), (_len))
59196c7f05SJoshua M. Clulow 
60196c7f05SJoshua M. Clulow #define	STRING_CHUNK_SIZE	64
61196c7f05SJoshua M. Clulow 
62773dbec3SJason King static void *custr_def_alloc(custr_alloc_t *, size_t);
63773dbec3SJason King static void custr_def_free(custr_alloc_t *, void *, size_t);
64773dbec3SJason King 
65773dbec3SJason King static custr_alloc_ops_t custr_alloc_ops_default = {
66773dbec3SJason King 	NULL,			/* custr_ao_init */
67773dbec3SJason King 	NULL,			/* custr_ao_fini */
68773dbec3SJason King 	custr_def_alloc,	/* custr_ao_alloc */
69773dbec3SJason King 	custr_def_free		/* custr_ao_free */
70773dbec3SJason King };
71773dbec3SJason King 
72773dbec3SJason King static custr_alloc_t custr_alloc_default = {
73773dbec3SJason King 	CUSTR_VERSION,			/* cua_version */
74773dbec3SJason King 	&custr_alloc_ops_default,	/* cua_ops */
75773dbec3SJason King 	NULL				/* cua_arg */
76773dbec3SJason King };
77773dbec3SJason King 
78196c7f05SJoshua M. Clulow void
custr_reset(custr_t * cus)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 
88*356ce177SJason King int
custr_remove(custr_t * cus,size_t idx,size_t len)89*356ce177SJason King custr_remove(custr_t *cus, size_t idx, size_t len)
90*356ce177SJason King {
91*356ce177SJason King 	size_t endidx = idx + len;
92*356ce177SJason King 
93*356ce177SJason King 	/*
94*356ce177SJason King 	 * Once gcc4 is dropped as a shadow compiler, we can migrate to
95*356ce177SJason King 	 * using builtins for the overflow check.
96*356ce177SJason King 	 */
97*356ce177SJason King 	if (endidx < idx || endidx < len) {
98*356ce177SJason King 		errno = EINVAL;
99*356ce177SJason King 		return (-1);
100*356ce177SJason King 	}
101*356ce177SJason King 
102*356ce177SJason King 	if (idx >= cus->cus_strlen || endidx > cus->cus_strlen) {
103*356ce177SJason King 		errno = EINVAL;
104*356ce177SJason King 		return (-1);
105*356ce177SJason King 	}
106*356ce177SJason King 
107*356ce177SJason King 	if (len == 0)
108*356ce177SJason King 		return (0);
109*356ce177SJason King 
110*356ce177SJason King 	/* The +1 will include the terminating NUL in the move */
111*356ce177SJason King 	(void) memmove(cus->cus_data + idx, cus->cus_data + endidx,
112*356ce177SJason King 	    cus->cus_strlen - endidx + 1);
113*356ce177SJason King 	cus->cus_strlen -= len;
114*356ce177SJason King 
115*356ce177SJason King 	/* The result should be NUL */
116*356ce177SJason King 	VERIFY0(cus->cus_data[cus->cus_strlen]);
117*356ce177SJason King 	return (0);
118*356ce177SJason King }
119*356ce177SJason King 
120*356ce177SJason King int
custr_rremove(custr_t * cus,size_t ridx,size_t len)121*356ce177SJason King custr_rremove(custr_t *cus, size_t ridx, size_t len)
122*356ce177SJason King {
123*356ce177SJason King 	size_t idx;
124*356ce177SJason King 
125*356ce177SJason King 	if (ridx >= cus->cus_strlen) {
126*356ce177SJason King 		errno = EINVAL;
127*356ce177SJason King 		return (-1);
128*356ce177SJason King 	}
129*356ce177SJason King 
130*356ce177SJason King 	idx = cus->cus_strlen - ridx - 1;
131*356ce177SJason King 	return (custr_remove(cus, idx, len));
132*356ce177SJason King }
133*356ce177SJason King 
134*356ce177SJason King int
custr_trunc(custr_t * cus,size_t idx)135*356ce177SJason King custr_trunc(custr_t *cus, size_t idx)
136*356ce177SJason King {
137*356ce177SJason King 	if (idx >= cus->cus_strlen) {
138*356ce177SJason King 		errno = EINVAL;
139*356ce177SJason King 		return (-1);
140*356ce177SJason King 	}
141*356ce177SJason King 
142*356ce177SJason King 	cus->cus_data[idx] = '\0';
143*356ce177SJason King 	cus->cus_strlen = idx;
144*356ce177SJason King 	return (0);
145*356ce177SJason King }
146*356ce177SJason King 
147*356ce177SJason King int
custr_rtrunc(custr_t * cus,size_t ridx)148*356ce177SJason King custr_rtrunc(custr_t *cus, size_t ridx)
149*356ce177SJason King {
150*356ce177SJason King 	size_t idx;
151*356ce177SJason King 
152*356ce177SJason King 	if (ridx >= cus->cus_strlen) {
153*356ce177SJason King 		errno = EINVAL;
154*356ce177SJason King 		return (-1);
155*356ce177SJason King 	}
156*356ce177SJason King 
157*356ce177SJason King 	idx = cus->cus_strlen - ridx - 1;
158*356ce177SJason King 	cus->cus_data[idx] = '\0';
159*356ce177SJason King 	cus->cus_strlen = idx;
160*356ce177SJason King 	return (0);
161*356ce177SJason King }
162*356ce177SJason King 
163196c7f05SJoshua M. Clulow size_t
custr_len(custr_t * cus)164196c7f05SJoshua M. Clulow custr_len(custr_t *cus)
165196c7f05SJoshua M. Clulow {
166196c7f05SJoshua M. Clulow 	return (cus->cus_strlen);
167196c7f05SJoshua M. Clulow }
168196c7f05SJoshua M. Clulow 
169196c7f05SJoshua M. Clulow const char *
custr_cstr(custr_t * cus)170196c7f05SJoshua M. Clulow custr_cstr(custr_t *cus)
171196c7f05SJoshua M. Clulow {
172fc2512cfSRobert Mustacchi 	if (cus->cus_data == NULL) {
173fc2512cfSRobert Mustacchi 		VERIFY(cus->cus_strlen == 0);
174fc2512cfSRobert Mustacchi 		VERIFY(cus->cus_datalen == 0);
175196c7f05SJoshua M. Clulow 
176fc2512cfSRobert Mustacchi 		/*
177fc2512cfSRobert Mustacchi 		 * This function should never return NULL.  If no buffer has
178fc2512cfSRobert Mustacchi 		 * been allocated, return a pointer to a zero-length string.
179fc2512cfSRobert Mustacchi 		 */
180fc2512cfSRobert Mustacchi 		return ("");
181fc2512cfSRobert Mustacchi 	}
182fc2512cfSRobert Mustacchi 	return (cus->cus_data);
183196c7f05SJoshua M. Clulow }
184196c7f05SJoshua M. Clulow 
185196c7f05SJoshua M. Clulow int
custr_append_vprintf(custr_t * cus,const char * fmt,va_list ap)186fc2512cfSRobert Mustacchi custr_append_vprintf(custr_t *cus, const char *fmt, va_list ap)
187196c7f05SJoshua M. Clulow {
188fc2512cfSRobert Mustacchi 	int len = vsnprintf(NULL, 0, fmt, ap);
189196c7f05SJoshua M. Clulow 	size_t chunksz = STRING_CHUNK_SIZE;
190196c7f05SJoshua M. Clulow 
19109cb6b0fSJoshua M. Clulow 	if (len < 0) {
192fc2512cfSRobert Mustacchi 		return (len);
19309cb6b0fSJoshua M. Clulow 	}
194fc2512cfSRobert Mustacchi 
195196c7f05SJoshua M. Clulow 	while (chunksz < len) {
196196c7f05SJoshua M. Clulow 		chunksz *= 2;
197196c7f05SJoshua M. Clulow 	}
198196c7f05SJoshua M. Clulow 
199196c7f05SJoshua M. Clulow 	if (len + cus->cus_strlen + 1 >= cus->cus_datalen) {
200196c7f05SJoshua M. Clulow 		char *new_data;
201196c7f05SJoshua M. Clulow 		size_t new_datalen = cus->cus_datalen + chunksz;
202196c7f05SJoshua M. Clulow 
203fc2512cfSRobert Mustacchi 		if (cus->cus_flags & CUSTR_FIXEDBUF) {
204fc2512cfSRobert Mustacchi 			errno = EOVERFLOW;
205fc2512cfSRobert Mustacchi 			return (-1);
206fc2512cfSRobert Mustacchi 		}
207fc2512cfSRobert Mustacchi 
208196c7f05SJoshua M. Clulow 		/*
209196c7f05SJoshua M. Clulow 		 * Allocate replacement memory:
210196c7f05SJoshua M. Clulow 		 */
211773dbec3SJason King 		if ((new_data = CUSTR_ALLOC(cus, new_datalen)) == NULL) {
212196c7f05SJoshua M. Clulow 			return (-1);
213196c7f05SJoshua M. Clulow 		}
214196c7f05SJoshua M. Clulow 
215196c7f05SJoshua M. Clulow 		/*
216196c7f05SJoshua M. Clulow 		 * Copy existing data into replacement memory and free
217196c7f05SJoshua M. Clulow 		 * the old memory.
218196c7f05SJoshua M. Clulow 		 */
219196c7f05SJoshua M. Clulow 		if (cus->cus_data != NULL) {
220196c7f05SJoshua M. Clulow 			(void) memcpy(new_data, cus->cus_data,
221196c7f05SJoshua M. Clulow 			    cus->cus_strlen + 1);
222773dbec3SJason King 			CUSTR_FREE(cus, cus->cus_data, cus->cus_datalen);
223196c7f05SJoshua M. Clulow 		}
224196c7f05SJoshua M. Clulow 
225196c7f05SJoshua M. Clulow 		/*
226196c7f05SJoshua M. Clulow 		 * Swap in the replacement buffer:
227196c7f05SJoshua M. Clulow 		 */
228196c7f05SJoshua M. Clulow 		cus->cus_data = new_data;
229196c7f05SJoshua M. Clulow 		cus->cus_datalen = new_datalen;
230196c7f05SJoshua M. Clulow 	}
231196c7f05SJoshua M. Clulow 	/*
232196c7f05SJoshua M. Clulow 	 * Append new string to existing string:
233196c7f05SJoshua M. Clulow 	 */
23409cb6b0fSJoshua M. Clulow 	if ((len = vsnprintf(cus->cus_data + cus->cus_strlen,
23509cb6b0fSJoshua M. Clulow 	    cus->cus_datalen - cus->cus_strlen, fmt, ap)) < 0) {
236fc2512cfSRobert Mustacchi 		return (len);
23709cb6b0fSJoshua M. Clulow 	}
238196c7f05SJoshua M. Clulow 	cus->cus_strlen += len;
239196c7f05SJoshua M. Clulow 
240196c7f05SJoshua M. Clulow 	return (0);
241196c7f05SJoshua M. Clulow }
242196c7f05SJoshua M. Clulow 
243fc2512cfSRobert Mustacchi int
custr_appendc(custr_t * cus,char newc)244fc2512cfSRobert Mustacchi custr_appendc(custr_t *cus, char newc)
245fc2512cfSRobert Mustacchi {
246fc2512cfSRobert Mustacchi 	return (custr_append_printf(cus, "%c", newc));
247fc2512cfSRobert Mustacchi }
248fc2512cfSRobert Mustacchi 
249fc2512cfSRobert Mustacchi int
custr_append_printf(custr_t * cus,const char * fmt,...)250fc2512cfSRobert Mustacchi custr_append_printf(custr_t *cus, const char *fmt, ...)
251fc2512cfSRobert Mustacchi {
252fc2512cfSRobert Mustacchi 	va_list ap;
253fc2512cfSRobert Mustacchi 	int ret;
254fc2512cfSRobert Mustacchi 
255fc2512cfSRobert Mustacchi 	va_start(ap, fmt);
256fc2512cfSRobert Mustacchi 	ret = custr_append_vprintf(cus, fmt, ap);
257fc2512cfSRobert Mustacchi 	va_end(ap);
258fc2512cfSRobert Mustacchi 
259fc2512cfSRobert Mustacchi 	return (ret);
260fc2512cfSRobert Mustacchi }
261fc2512cfSRobert Mustacchi 
262fc2512cfSRobert Mustacchi int
custr_append(custr_t * cus,const char * name)263fc2512cfSRobert Mustacchi custr_append(custr_t *cus, const char *name)
264fc2512cfSRobert Mustacchi {
265fc2512cfSRobert Mustacchi 	return (custr_append_printf(cus, "%s", name));
266fc2512cfSRobert Mustacchi }
267fc2512cfSRobert Mustacchi 
268196c7f05SJoshua M. Clulow int
custr_alloc_init(custr_alloc_t * cua,const custr_alloc_ops_t * ops,...)269773dbec3SJason King custr_alloc_init(custr_alloc_t *cua, const custr_alloc_ops_t *ops, ...)
270773dbec3SJason King {
271773dbec3SJason King 	int ret = 0;
272773dbec3SJason King 
273773dbec3SJason King 	if (cua->cua_version != CUSTR_VERSION || ops->custr_ao_alloc == NULL ||
274773dbec3SJason King 	    ops->custr_ao_free == NULL) {
275773dbec3SJason King 		errno = EINVAL;
276773dbec3SJason King 		return (-1);
277773dbec3SJason King 	}
278773dbec3SJason King 
279773dbec3SJason King 	cua->cua_ops = ops;
280773dbec3SJason King 	cua->cua_arg = NULL;
281773dbec3SJason King 
282773dbec3SJason King 	if (ops->custr_ao_init != NULL) {
283773dbec3SJason King 		va_list ap;
284773dbec3SJason King 
285773dbec3SJason King 		va_start(ap, ops);
286773dbec3SJason King 		ret = ops->custr_ao_init(cua, ap);
287773dbec3SJason King 		va_end(ap);
288773dbec3SJason King 	}
289773dbec3SJason King 
290773dbec3SJason King 	return ((ret == 0) ? 0 : -1);
291773dbec3SJason King }
292773dbec3SJason King 
293773dbec3SJason King void
custr_alloc_fini(custr_alloc_t * cua)294773dbec3SJason King custr_alloc_fini(custr_alloc_t *cua)
295773dbec3SJason King {
296773dbec3SJason King 	if (cua->cua_ops->custr_ao_fini != NULL)
297773dbec3SJason King 		cua->cua_ops->custr_ao_fini(cua);
298773dbec3SJason King }
299773dbec3SJason King 
300773dbec3SJason King int
custr_xalloc(custr_t ** cus,custr_alloc_t * cao)301773dbec3SJason King custr_xalloc(custr_t **cus, custr_alloc_t *cao)
302196c7f05SJoshua M. Clulow {
303196c7f05SJoshua M. Clulow 	custr_t *t;
304196c7f05SJoshua M. Clulow 
305773dbec3SJason King 	if (cao == NULL)
306773dbec3SJason King 		cao = &custr_alloc_default;
307773dbec3SJason King 
308773dbec3SJason King 	if ((t = cao->cua_ops->custr_ao_alloc(cao, sizeof (*t))) == NULL) {
309196c7f05SJoshua M. Clulow 		*cus = NULL;
310196c7f05SJoshua M. Clulow 		return (-1);
311196c7f05SJoshua M. Clulow 	}
312773dbec3SJason King 	(void) memset(t, 0, sizeof (*t));
313196c7f05SJoshua M. Clulow 
314773dbec3SJason King 	t->cus_alloc = cao;
315196c7f05SJoshua M. Clulow 	*cus = t;
316196c7f05SJoshua M. Clulow 	return (0);
317196c7f05SJoshua M. Clulow }
318196c7f05SJoshua M. Clulow 
319fc2512cfSRobert Mustacchi int
custr_alloc(custr_t ** cus)320773dbec3SJason King custr_alloc(custr_t **cus)
321773dbec3SJason King {
322773dbec3SJason King 	return (custr_xalloc(cus, NULL));
323773dbec3SJason King }
324773dbec3SJason King 
325773dbec3SJason King int
custr_xalloc_buf(custr_t ** cus,void * buf,size_t buflen,custr_alloc_t * cao)326773dbec3SJason King custr_xalloc_buf(custr_t **cus, void *buf, size_t buflen, custr_alloc_t *cao)
327fc2512cfSRobert Mustacchi {
328fc2512cfSRobert Mustacchi 	int ret;
329fc2512cfSRobert Mustacchi 
330fc2512cfSRobert Mustacchi 	if (buflen == 0 || buf == NULL) {
331fc2512cfSRobert Mustacchi 		errno = EINVAL;
332fc2512cfSRobert Mustacchi 		return (-1);
333fc2512cfSRobert Mustacchi 	}
334fc2512cfSRobert Mustacchi 
335773dbec3SJason King 	if ((ret = custr_xalloc(cus, cao)) != 0)
336fc2512cfSRobert Mustacchi 		return (ret);
337fc2512cfSRobert Mustacchi 
338fc2512cfSRobert Mustacchi 	(*cus)->cus_data = buf;
339fc2512cfSRobert Mustacchi 	(*cus)->cus_datalen = buflen;
340fc2512cfSRobert Mustacchi 	(*cus)->cus_strlen = 0;
341fc2512cfSRobert Mustacchi 	(*cus)->cus_flags = CUSTR_FIXEDBUF;
342fc2512cfSRobert Mustacchi 	(*cus)->cus_data[0] = '\0';
343fc2512cfSRobert Mustacchi 
344fc2512cfSRobert Mustacchi 	return (0);
345fc2512cfSRobert Mustacchi }
346fc2512cfSRobert Mustacchi 
347773dbec3SJason King int
custr_alloc_buf(custr_t ** cus,void * buf,size_t buflen)348773dbec3SJason King custr_alloc_buf(custr_t **cus, void *buf, size_t buflen)
349773dbec3SJason King {
350773dbec3SJason King 	return (custr_xalloc_buf(cus, buf, buflen, NULL));
351773dbec3SJason King }
352773dbec3SJason King 
353196c7f05SJoshua M. Clulow void
custr_free(custr_t * cus)354196c7f05SJoshua M. Clulow custr_free(custr_t *cus)
355196c7f05SJoshua M. Clulow {
356773dbec3SJason King 	custr_alloc_t *cao;
357773dbec3SJason King 
358196c7f05SJoshua M. Clulow 	if (cus == NULL)
359196c7f05SJoshua M. Clulow 		return;
360196c7f05SJoshua M. Clulow 
361fc2512cfSRobert Mustacchi 	if ((cus->cus_flags & CUSTR_FIXEDBUF) == 0)
362773dbec3SJason King 		CUSTR_FREE(cus, cus->cus_data, cus->cus_datalen);
363773dbec3SJason King 
364773dbec3SJason King 	cao = cus->cus_alloc;
365773dbec3SJason King 	cao->cua_ops->custr_ao_free(cao, cus, sizeof (*cus));
366773dbec3SJason King }
367773dbec3SJason King 
368773dbec3SJason King /*ARGSUSED*/
369773dbec3SJason King static void *
custr_def_alloc(custr_alloc_t * cao __unused,size_t len)370773dbec3SJason King custr_def_alloc(custr_alloc_t *cao __unused, size_t len)
371773dbec3SJason King {
372773dbec3SJason King 	return (malloc(len));
373773dbec3SJason King }
374773dbec3SJason King 
375773dbec3SJason King /*ARGSUSED*/
376773dbec3SJason King static void
custr_def_free(custr_alloc_t * cao __unused,void * p,size_t len __unused)377773dbec3SJason King custr_def_free(custr_alloc_t *cao __unused, void *p, size_t len __unused)
378773dbec3SJason King {
379773dbec3SJason King 	free(p);
380196c7f05SJoshua M. Clulow }
381