1 /*
2 ***** BEGIN LICENSE BLOCK *****
3
4 Copyright (C) 2001-2020 Olof Hagsand
5
6 This file is part of CLIgen.
7
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11
12 http://www.apache.org/licenses/LICENSE-2.0
13
14 Unless required by applicable law or agreed to in writing, software
15 distributed under the License is distributed on an "AS IS" BASIS,
16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 See the License for the specific language governing permissions and
18 limitations under the License.
19
20 Alternatively, the contents of this file may be used under the terms of
21 the GNU General Public License Version 2 or later (the "GPL"),
22 in which case the provisions of the GPL are applicable instead
23 of those above. If you wish to allow use of your version of this file only
24 under the terms of the GPL, and not to allow others to
25 use your version of this file under the terms of Apache License version 2, indicate
26 your decision by deleting the provisions above and replace them with the
27 notice and other provisions required by the GPL. If you do not delete
28 the provisions above, a recipient may use your version of this file under
29 the terms of any one of the Apache License version 2 or the GPL.
30
31 ***** END LICENSE BLOCK *****
32
33 * CLIgen dynamic buffers
34 * @code
35 * cbuf *cb;
36 * if ((cb = cbuf_new()) == NULL)
37 * err();
38 * cprintf(cb, "content %d", 42);
39 * if (write(f, cbuf_get(cb), cbuf_len(cb)) < 0)
40 * err();
41 * cbuf_free(cb);
42 * @endcode
43 */
44
45 /*
46 * Constants
47 */
48 /* Initial alloc mem length of a cbuf, then grows exponentially, with 2*, 4*, etc
49 * 1K could be a bit much for large syntaxes and small entries
50 * @see cbuf_alloc_set
51 */
52 #define CBUFLEN_START 1024
53 #define CBUFLEN_THRESHOLD 65536
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <stdint.h>
58 #include <string.h>
59 #include <errno.h>
60
61 #include "cligen_buf.h" /* External API */
62 #include "cligen_buf_internal.h"
63
64 /*
65 * Variables
66 */
67 /* This is how large an initial cbuf is after calling cbuf_new. Note that the cbuf
68 * may grow after calls to cprintf or cbuf_alloc
69 */
70 static size_t cbuflen_start = CBUFLEN_START;
71
72 /* Threshold to where a cbuf grows exponentially, thereafter it grows linearly
73 * If 0 continue with exponential growth
74 */
75 static size_t cbuflen_threshold = CBUFLEN_THRESHOLD;
76
77 /*! Get global cbuf initial memory allocation size
78 * This is how large a cbuf is after calling cbuf_new. Note that the cbuf
79 * may grow after calls to cprintf or cbuf_alloc
80 * @param[out] default Initial default cbuf size
81 * @param[out] threshold Threshold where cbuf grows linearly instead of exponentially
82 */
83 int
cbuf_alloc_get(size_t * start,size_t * threshold)84 cbuf_alloc_get(size_t *start,
85 size_t *threshold)
86 {
87 *start = cbuflen_start;
88 *threshold = cbuflen_threshold;
89 return 0;
90 }
91
92 /*! Set global cbuf initial memory allocation size
93 * This is how large a cbuf is after calling cbuf_new. Note that the cbuf
94 * may grow after calls to cprintf or cbuf_alloc
95 * If 0 continue with exponential growth
96 */
97 int
cbuf_alloc_set(size_t start,size_t threshold)98 cbuf_alloc_set(size_t start,
99 size_t threshold)
100 {
101 cbuflen_start = start;
102 cbuflen_threshold = threshold;
103 return 0;
104 }
105
106 /*! Allocate cligen buffer. Returned handle can be used in sprintf calls
107 * which dynamically print a string.
108 * The handle should be freed by cbuf_free()
109 * @param[in] How much buffer space for initial allocation
110 * @retval cb The allocated objecyt handle on success.
111 * @retval NULL Error.
112 * @see cbuf_new with auto buffer allocation
113 */
114 cbuf *
cbuf_new_alloc(size_t sz)115 cbuf_new_alloc(size_t sz)
116 {
117 cbuf *cb;
118
119 if ((cb = (cbuf*)malloc(sizeof(*cb))) == NULL)
120 return NULL;
121 memset(cb, 0, sizeof(*cb));
122 cb->cb_buflen = sz;
123 if ((cb->cb_buffer = malloc(cb->cb_buflen)) == NULL)
124 return NULL;
125 memset(cb->cb_buffer, 0, cb->cb_buflen);
126 cb->cb_strlen = 0;
127 return cb;
128 }
129
130 /*! Allocate cligen buffer with auto buffer allocation. Returned handle can be used in sprintf calls
131 * which dynamically print a string.
132 * The handle should be freed by cbuf_free()
133 * @retval cb The allocated objecyt handle on success.
134 * @retval NULL Error.
135 * @see cbuf_new_alloc with explicit buffer allocation
136 */
137 cbuf *
cbuf_new(void)138 cbuf_new(void)
139 {
140 return cbuf_new_alloc(cbuflen_start);
141 }
142
143 /*! Free cligen buffer previously allocated with cbuf_new
144 * @param[in] cb Cligen buffer
145 */
146 void
cbuf_free(cbuf * cb)147 cbuf_free(cbuf *cb)
148 {
149 if (cb) {
150 if (cb->cb_buffer)
151 free(cb->cb_buffer);
152 free(cb);
153 }
154 }
155
156 /*! Return actual byte buffer of cligen buffer
157 * @param[in] cb Cligen buffer
158 */
159 char*
cbuf_get(cbuf * cb)160 cbuf_get(cbuf *cb)
161 {
162 return cb->cb_buffer;
163 }
164
165 /*! Return length of string in cligen buffer (not buffer length itself)
166 * @param[in] cb Cligen buffer
167 * @see cbuf_buflen
168 */
169 int
cbuf_len(cbuf * cb)170 cbuf_len(cbuf *cb)
171 {
172 return cb->cb_strlen;
173 }
174
175 /*! Return length of buffer itself, ie allocated bytes
176 * @param[in] cb Cligen buffer
177 * @see cbuf_len
178 */
179 int
cbuf_buflen(cbuf * cb)180 cbuf_buflen(cbuf *cb)
181 {
182 return cb->cb_buflen;
183 }
184
185 /*! Reset a cligen buffer. That is, restart it from scratch.
186 * @param[in] cb Cligen buffer
187 */
188 void
cbuf_reset(cbuf * cb)189 cbuf_reset(cbuf *cb)
190 {
191 cb->cb_strlen = 0;
192 cb->cb_buffer[0] = '\0';
193 }
194
195 /*! Internal buffer reallocator, Ensure buffer is large enough
196 * use quadratic expansion (2* size)
197 * @param[in] cb CLIgen buffer
198 * @param[in] len Extra length added
199 */
200 static int
cbuf_realloc(cbuf * cb,size_t sz)201 cbuf_realloc(cbuf *cb,
202 size_t sz)
203 {
204 int retval = -1;
205 int diff;
206
207 diff = cb->cb_buflen - (cb->cb_strlen + sz + 1);
208 if (diff <= 0){
209 while (diff <= 0){
210 if (cbuflen_threshold == 0 || cb->cb_buflen < cbuflen_threshold)
211 cb->cb_buflen *= 2; /* Double the space - exponential */
212 else
213 cb->cb_buflen += cbuflen_threshold; /* Add - linear growth*/
214 diff = cb->cb_buflen - (cb->cb_strlen + sz + 1);
215 }
216 if ((cb->cb_buffer = realloc(cb->cb_buffer, cb->cb_buflen)) == NULL)
217 goto done;
218 }
219 retval = 0;
220 done:
221 return retval;
222 }
223
224 /*! Append a cligen buf by printf like semantics
225 *
226 * @param [in] cb cligen buffer allocated by cbuf_new(), may be reallocated.
227 * @param [in] format arguments uses printf syntax.
228 * @retval See printf
229 * @see cbuf_append_str for the optimized special case of string append
230 * @note cprintf assume null-terminated string as %s, use cbuf_memcp for a raw interface
231 */
232 int
cprintf(cbuf * cb,const char * format,...)233 cprintf(cbuf *cb,
234 const char *format, ...)
235 {
236 int retval = -1;
237 va_list ap;
238 int len;
239 int ret;
240
241 if (cb == NULL)
242 goto ok;
243 va_start(ap, format); /* dryrun */
244 if ((len = vsnprintf(NULL, 0, format, ap)) < 0) /* dryrun, just get len */
245 goto done;
246 va_end(ap);
247 /* Ensure buffer is large enough */
248 if (cbuf_realloc(cb, len) < 0)
249 goto done;
250 va_start(ap, format); /* real */
251 if ((ret = vsnprintf(cb->cb_buffer+cb->cb_strlen, /* str */
252 cb->cb_buflen-cb->cb_strlen, /* size */
253 format, ap)) < 0)
254 goto done;
255 va_end(ap);
256 cb->cb_strlen += ret;
257 ok:
258 retval = 0;
259 done:
260 return retval;
261 }
262
263 /*! Append a string to a cbuf
264 *
265 * An optimized special case of cprintf
266 * @param [in] cb cligen buffer allocated by cbuf_new(), may be reallocated.
267 * @param [in] str string
268 * @retval 0 OK
269 * @retval -1 Error
270 * @see cprintf for the generic function
271 */
272 int
cbuf_append_str(cbuf * cb,char * str)273 cbuf_append_str(cbuf *cb,
274 char *str)
275 {
276 size_t len0;
277 size_t len;
278
279 if (str == NULL){
280 errno = EINVAL;
281 return -1;
282 }
283 len0 = strlen(str);
284 len = cb->cb_strlen + len0;
285 /* Ensure buffer is large enough */
286 if (cbuf_realloc(cb, len) < 0)
287 return -1;
288 strncpy(cb->cb_buffer+cb->cb_strlen, str, len0+1);
289 cb->cb_strlen = len;
290 return 0;
291 }
292
293 /*! Append a character to a cbuf
294 *
295 * @param [in] cb cligen buffer allocated by cbuf_new(), may be reallocated.
296 * @param [in] c character to append
297 * @retval 0 OK
298 * @retval -1 Error
299 * @see cbuf_append_str, use that function instead
300 */
301 int
cbuf_append(cbuf * cb,int c)302 cbuf_append(cbuf *cb,
303 int c)
304 {
305 char str[2] = {0,};
306
307 str[0] = c;
308 return cbuf_append_str(cb, str);
309 }
310
311 /*! Append a raw memory buffer and add null-termion
312 *
313 * A raw buffer handler to cprintf
314 * @param [in] cb cligen buffer allocated by cbuf_new(), may be reallocated.
315 * @param [in] src Source buffer
316 * @param [in] n Number of bytes to copy, add a null
317 * @retval 0 OK
318 * @retval -1 Error
319 * @see cprintf for the generic function
320 */
321 int
cbuf_append_buf(cbuf * cb,void * src,size_t n)322 cbuf_append_buf(cbuf *cb,
323 void *src,
324 size_t n)
325 {
326 size_t len0;
327 size_t len;
328
329 if (src == NULL){
330 errno = EINVAL;
331 return -1;
332 }
333 len0 = cb->cb_strlen;
334 len = cb->cb_strlen + n + 1;
335 /* Ensure buffer is large enough */
336 if (cbuf_realloc(cb, len) < 0)
337 return -1;
338 memcpy(cb->cb_buffer+len0, src, n);
339 cb->cb_buffer[len-1] = '\0'; /* Add a null byte */
340 cb->cb_strlen = len;
341 return 0;
342 }
343
344