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