1 /*
2  * Copyright (c) 2012 Tim Ruehsen
3  * Copyright (c) 2015-2021 Free Software Foundation, Inc.
4  *
5  * This file is part of libwget.
6  *
7  * Libwget is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Libwget is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  *
21  * Memory buffer data structure routines
22  *
23  * Changelog
24  * 22.08.2012  Tim Ruehsen  created
25  *
26  */
27 
28 #include <config.h>
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 
35 #include <wget.h>
36 #include "private.h"
37 
38 /**
39  * \file
40  * \brief Buffer management functions
41  * \defgroup libwget-buffer Buffer management functions
42  * @{
43  *
44  * A buffer (represented with an opaque `wget_buffer`) is a managed memory area.
45  *
46  * Apart from a pointer to a raw chunk of memory (`char *`), it also has some metadata attached
47  * such as the length of the buffer and the actual occupied positions.
48  *
49  * Actually, when we talk about the **length** of the buffer, we refer to the actual number of bytes stored
50  * in it by the user. On the other hand, the **size** is the total number of slots in the buffer, either occupied
51  * or not.
52  *
53  * The stored data is always 0-terminated, so you safely use it with standard string functions.
54  *
55  * The functions here allow you to easily work with buffers, providing shortcuts to commonly used
56  * memory and string management operations and avoiding usual pitfalls, such as buffer overflows.
57  * They provide a higher-level abstraction to working with memory than the @link libwget-mem memory management functions@endlink.
58  *
59  * If your application uses memory allocation functions that may return %NULL values (e.g. like the standard libc functions),
60  * you have to check the `error` value before using the result. If set, it indicates that a memory allocation failure
61  * occurred during internal (re-)allocation.
62  *
63  * Example with wget_buffer on the stack (initial 16 bytes heap allocation)
64  * ```
65  * wget_buffer buf;
66  *
67  * wet_buffer_init(&buf, NULL, 16);
68  *
69  * wget_buffer_strcpy(&buf, "A");
70  * wget_buffer_strcat(&buf, "B");
71  * wget_buffer_memcat(&buf, "C", 1);
72  * wget_buffer_memset_append(&buf, 'D', 1);
73  * wget_buffer_printf_append(&buf, "%s", "E");
74  *
75  * // buf.data now contains the 0-terminated string "ABCDE"
76  * printf("buf.data = %s\n", buf.data);
77  *
78  * wget_buffer_deinit(&buf);
79  * ```
80  *
81  * Example with wget_buffer on the stack and 16 bytes initial stack buffer (no heap allocation is needed)
82  * ```
83  * wget_buffer buf;
84  * char sbuf[16];
85  *
86  * wet_buffer_init(&buf, sbuf, sizeof(sbuf));
87  *
88  * wget_buffer_strcpy(&buf, "A");
89  * wget_buffer_strcat(&buf, "B");
90  * wget_buffer_memcat(&buf, "C", 1);
91  * wget_buffer_memset_append(&buf, 'D', 1);
92  * wget_buffer_printf_append(&buf, "%s", "E");
93  *
94  * // buf.data now contains the 0-terminated string "ABCDE"
95  * printf("buf.data = %s\n", buf.data);
96  *
97  * wget_buffer_deinit(&buf);
98  * ```
99  *
100  * Example on how to check for memory failure and how to transfer buffer data
101  * ```
102  * wget_buffer buf;
103  *
104  * wet_buffer_init(&buf, NULL, 16);
105  *
106  * wget_buffer_strcpy(&buf, "A");
107  * wget_buffer_strcat(&buf, "B");
108  * wget_buffer_memcat(&buf, "C", 1);
109  * wget_buffer_memset_append(&buf, 'D', 1);
110  * wget_buffer_printf_append(&buf, "%s", "E");
111  *
112  *	if (buf.error)
113  *		panic("No memory !");
114  *
115  *	// transfer ownership away from wget_buffer
116  *	char *ret = buf.data;
117  *	buf.data = NULL; // avoid double frees
118  *
119  * wget_buffer_deinit(&buf);
120  *
121  *	return ret; // the caller must free() this value after use
122  * ```
123  */
124 
125 /**
126  * \param[in] buf Pointer to the buffer that should become initialized.
127  * \param[in] data Initial contents of the buffer. Might be NULL.
128  * \param[in] size Initial length of the buffer. Might be zero (will default to 128 bytes).
129  * \return WGET_E_SUCCESS or WGET_E_MEMORY in case of a memory allocation failure.
130  *
131  * Create a new buffer.
132  *
133  * If \p data is NULL, the buffer will be empty, but it will be pre-allocated with \p size bytes.
134  * This will make future operations on the buffer faster since there will be less re-allocations needed.
135  *
136  * <b>If \p size is zero, the buffer will be pre-allocated with 128 bytes.</b>
137  *
138  * You may provide some \p data to fill the buffer with it. The contents of the \p data pointer
139  * are not copied, but rather the pointer itself is referenced directly within the buffer. If you modify the contents
140  * of \p data, those changes will be reflected in the buffer as they both point to the same memory area.
141  *
142  * Apart from that, there are other concerns you should keep in mind if you provide your own \p data here:
143  *
144  *  - wget_buffer_deinit() _will not_ free that memory when you call it. So if you provide
145  * a \p data pointer, you must free it yourself before your program ends.
146  *  - wget_buffer_realloc() will also not free that memory. It will allocate a new buffer and copy the contents
147  *  there, but will not touch the old buffer. The new buffer _will_ be freed by these functions since it's been
148  *  allocated by libwget internally and thus it knows it can be freed without harm.
149  *
150  * If an existing buffer is provided in \p buf, it will be initialized with the provided \p data and \p size
151  * according to the rules stated above.
152  */
wget_buffer_init(wget_buffer * buf,char * data,size_t size)153 int wget_buffer_init(wget_buffer *buf, char *data, size_t size)
154 {
155 	if (data && likely(size)) {
156 		buf->size = size - 1;
157 		buf->data = data;
158 		*buf->data = 0; // always 0 terminate data to allow string functions
159 		buf->release_data = 0;
160 	} else {
161 		if (!size)
162 			size = 127;
163 		buf->size = size;
164 		if (!(buf->data = wget_malloc(size + 1))) {
165 			buf->error = 1;
166 			return WGET_E_MEMORY;
167 		}
168 		*buf->data = 0; // always 0 terminate data to allow string functions
169 		buf->release_data = 1;
170 	}
171 
172 	buf->error = 0;
173 	buf->release_buf = 0;
174 	buf->length = 0;
175 
176 	return WGET_E_SUCCESS;
177 }
178 
179 /**
180  * \param[in] size Initial length of the buffer.
181  * \return A new buffer.
182  *
183  * Allocates a new buffer of size \p size bytes.
184  *
185  * The buffer will be pre-allocated with that many bytes, all zeros.
186  *
187  * This is equivalent to wget_buffer_init(NULL, NULL, size).
188  */
wget_buffer_alloc(size_t size)189 wget_buffer *wget_buffer_alloc(size_t size)
190 {
191 	wget_buffer *buf;
192 
193 	if (!(buf = wget_malloc(sizeof(wget_buffer))))
194 		return NULL;
195 
196 	if (wget_buffer_init(buf, NULL, size) < 0) {
197 		xfree(buf);
198 		return NULL;
199 	}
200 
201 	buf->release_buf = 1;
202 
203 	return buf;
204 }
205 
buffer_realloc(wget_buffer * buf,size_t size)206 static int buffer_realloc(wget_buffer *buf, size_t size)
207 {
208 	char *old_data = buf->data;
209 
210 	if (buf->release_data)
211 		buf->data = wget_realloc(buf->data, size + 1);
212 	else
213 		buf->data = wget_malloc(size + 1);
214 
215 	if (!buf->data) {
216 		buf->data = old_data;
217 		buf->error = 1;
218 		return WGET_E_MEMORY;
219 	}
220 
221 	if (!buf->release_data) {
222 		if (likely(old_data) && buf->length)
223 			memcpy(buf->data, old_data, buf->length + 1);
224 		else
225 			*buf->data = 0; // always 0 terminate data to allow string functions
226 
227 		buf->release_data = 1;
228 	}
229 
230 	buf->size = size;
231 
232 	return WGET_E_SUCCESS;
233 }
234 
235 /**
236  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
237  * \param[in] size Total size (in bytes) required in the buffer
238  * \return WGET_E_SUCCESS on success, else WGET_E_MEMORY if the memory allocation failed
239  *
240  * Make sure the buffer \p buf has at least a **size** of \p size bytes.
241  *
242  * If the buffer's size is less than that, it will automatically enlarge it
243  * (with wget_buffer_realloc()) to make it at least as long.
244  */
wget_buffer_ensure_capacity(wget_buffer * buf,size_t size)245 int wget_buffer_ensure_capacity(wget_buffer *buf, size_t size)
246 {
247 	if (likely(buf)) {
248 		if (buf->size < size)
249 			return buffer_realloc(buf, size);
250 	}
251 
252 	return WGET_E_SUCCESS;
253 }
254 
255 /**
256  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
257  *
258  * Free the buffer, and all its contents.
259  *
260  * If you provided your own data when calling wget_buffer_init() (you passed a non-NULL \p data pointer)
261  * then **that buffer will not be freed**. As stated in the description of wget_buffer_init() you
262  * must free that buffer yourself: this function will only free the `wget_buffer` structure.
263  *
264  * Similarly, if you provided your own buffer when calling wget_buffer_init() (\p buf was non-NULL)
265  * the buffer (the `wget_buffer` structure) **will not** be freed, and the data might or might not be freed
266  * depending on the above condition.
267  */
wget_buffer_deinit(wget_buffer * buf)268 void wget_buffer_deinit(wget_buffer *buf)
269 {
270 	if (buf->release_data) {
271 		xfree(buf->data);
272 		buf->release_data = 0;
273 	}
274 
275 	if (buf->release_buf)
276 		wget_free(buf); // do not use xfree() since buf is NONNULL
277 }
278 
279 /**
280  * \param[in] buf A double pointer to a buffer
281  *
282  * Free the buffer, and all its contents.
283  *
284  * It behaves like wget_buffer_deinit() but it also sets the \p buf pointer to NULL.
285  *
286  * This function is equivalent to:
287  *
288  *     wget_buffer_deinit(*buf);
289  *     *buf = NULL;
290  */
wget_buffer_free(wget_buffer ** buf)291 void wget_buffer_free(wget_buffer **buf)
292 {
293 	if (likely(buf && *buf)) {
294 		wget_buffer_deinit(*buf);
295 		*buf = NULL;
296 	}
297 }
298 
299 /**
300  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
301  *
302  * Release the buffer's data, but keep the buffer itself (the `wget_buffer` structure).
303  *
304  * The **length** of the buffer will be maintained, but after this function succeeds, the **size**
305  * will obviously be zero.
306  *
307  * The same rules that apply to wget_buffer_deinit() also apply here: if you provided your own data
308  * when calling wget_buffer_init() (ie. \p data was non-NULL) then **that data will not be freed**, and this
309  * function will essentially be a no-op.
310  */
wget_buffer_free_data(wget_buffer * buf)311 void wget_buffer_free_data(wget_buffer *buf)
312 {
313 	if (likely(buf)) {
314 		if (buf->release_data) {
315 			xfree(buf->data);
316 			buf->release_data = 0;
317 			buf->size = 0;
318 		}
319 	}
320 }
321 
322 /**
323  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
324  *
325  * This function is lighter than wget_buffer_free_data(). It does not free the data buffer, it just
326  * sets its first byte to zero, as well as the length.
327  *
328  * This function is equivalent to:
329  *
330  *     buf->length = 0;
331  *     *buf->data = 0;
332  */
wget_buffer_reset(wget_buffer * buf)333 void wget_buffer_reset(wget_buffer *buf)
334 {
335 	if (likely(buf)) {
336 		buf->length = 0;
337 		*buf->data = 0;
338 	}
339 }
340 
341 /**
342  * \param[in] buf  A buffer, created with wget_buffer_init() or wget_buffer_alloc()
343  * \param[in] data A pointer to the data to be copied
344  * \param[in] length How many bytes from \p data (starting at the beginning) should be copied
345  * \return The new length of the buffer after copying the data
346  *
347  * Copy the contents in the pointer \p data to the buffer \p buf,
348  * clobbering the previous contents.
349  *
350  * The first \p length bytes of \p data are written to \p buf.
351  * The content of \p buf is overwritten with the new \p data.
352  *
353  * If the buffer is not large enough to store that amount of data,
354  * it is enlarged automatically at least \p length bytes (with wget_buffer_realloc()).
355  */
wget_buffer_memcpy(wget_buffer * buf,const void * data,size_t length)356 size_t wget_buffer_memcpy(wget_buffer *buf, const void *data, size_t length)
357 {
358 	if (unlikely(!buf))
359 		return 0;
360 
361 	buf->length = 0;
362 
363 	return wget_buffer_memcat(buf, data, length);
364 }
365 
366 /**
367  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
368  * \param[in] data A pointer to the data to be appended
369  * \param[in] length How many bytes of \p data should be written to \p buf
370  * \return The new length of the buffer after appending the data
371  *
372  * Append the provided \p data to the end of the buffer \p buf (preserving contents).
373  *
374  * If there's not enough space in \p buf, it is enlarged automatically
375  * (with wget_buffer_realloc()) at least \p length bytes, so that the whole
376  * data can be written.
377  */
wget_buffer_memcat(wget_buffer * buf,const void * data,size_t length)378 size_t wget_buffer_memcat(wget_buffer *buf, const void *data, size_t length)
379 {
380 	if (unlikely(!buf))
381 		return 0;
382 
383 	if (likely(length)) {
384 		if (buf->size < buf->length + length)
385 			if (buffer_realloc(buf, buf->size * 2 + length) != WGET_E_SUCCESS)
386 				return buf->length;
387 
388 		if (likely(data))
389 			memcpy(buf->data + buf->length, data, length);
390 		else
391 			memset(buf->data + buf->length, 0, length);
392 		buf->length += length;
393 	}
394 	buf->data[buf->length] = 0; // always 0 terminate data to allow string functions
395 
396 	return buf->length;
397 }
398 
399 /**
400  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
401  * \param[in] s A NULL-terminated string
402  * \return The new length of the buffer after copying the string
403  *
404  * Copy the NULL-terminated string \p s to the buffer \p buf,
405  * overwriting its original contents.
406  *
407  * If the buffer is not large enough it is enlarged automatically.
408  *
409  * This is essentially equivalent to:
410  *
411  *     buf->length = 0;
412  *     wget_buffer_memcat(buf, s, strlen(s));
413  */
wget_buffer_strcpy(wget_buffer * buf,const char * s)414 size_t wget_buffer_strcpy(wget_buffer *buf, const char *s)
415 {
416 	if (likely(buf))
417 		buf->length = 0;
418 
419 	return wget_buffer_memcat(buf, s, likely(s) ? strlen(s) : 0);
420 }
421 
422 /**
423  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
424  * \param[in] s A NULL-terminated string
425  * \return The new length of the buffer after appending the string
426  *
427  * Append the NULL-terminated string \p s to the end of the buffer  \p buf
428  * (preserving its contents).
429  *
430  * If the buffer is not large enough it is enlarged automatically.
431  *
432  * This is essentially equivalent to calling wget_buffer_memcat() with length equal to `strlen(s)`:
433  *
434  *     wget_buffer_memcat(buf, s, strlen(s));
435  */
wget_buffer_strcat(wget_buffer * buf,const char * s)436 size_t wget_buffer_strcat(wget_buffer *buf, const char *s)
437 {
438 	return wget_buffer_memcat(buf, s, likely(s) ? strlen(s) : 0);
439 }
440 
441 /**
442  * \param[in] buf The destination buffer
443  * \param[in] src The source buffer
444  * \return The new length of the destination buffer \p buf after copying the contents of \p src
445  *
446  * Copy the contents of the buffer \p src in the buffer \p buf,
447  * clobbering its previous contents.
448  *
449  * If the buffer \p buf is not large enough it is enlarged automatically.
450  *
451  * This is equivalent to:
452  *
453  *     wget_buffer_memcpy(buf, src->data, src->length);
454  */
wget_buffer_bufcpy(wget_buffer * buf,wget_buffer * src)455 size_t wget_buffer_bufcpy(wget_buffer *buf, wget_buffer *src)
456 {
457 	if (likely(src))
458 		return wget_buffer_memcpy(buf, src->data, src->length);
459 	else
460 		return wget_buffer_memcpy(buf, NULL, 0);
461 }
462 
463 /**
464  * \param[in] buf The destination buffer
465  * \param[in] src The source buffer
466  * \return The new length of the destination buffer \p buf after appending the contents of \p src
467  *
468  * Append the contents of the buffer \p src to the end of the buffer \p buf.
469  *
470  * If the buffer \p buf is not large enough it is enlarged automatically.
471  *
472  * This is equivalent to:
473  *
474  *     wget_buffer_memcat(buf, src->data, src->length);
475  */
wget_buffer_bufcat(wget_buffer * buf,wget_buffer * src)476 size_t wget_buffer_bufcat(wget_buffer *buf, wget_buffer *src)
477 {
478 	if (likely(src))
479 		return wget_buffer_memcat(buf, src->data, src->length);
480 	else
481 		return wget_buffer_memcat(buf, NULL, 0);
482 }
483 
484 /**
485  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
486  * \param[in] c The byte to be copied at the end of the buffer
487  * \param[in] length How many times will the byte \p c be copied.
488  * \return The new length of the buffer \p buf.
489  *
490  * Copy the byte \p c repeatedly \p length times **starting at the beginning of the buffer**,
491  * so the first \p length bytes of the buffer are overwritten.
492  *
493  * If there's not enough space in \p buf, it is enlarged automatically
494  * (with wget_buffer_realloc()) at least \p length bytes.
495  */
wget_buffer_memset(wget_buffer * buf,char c,size_t length)496 size_t wget_buffer_memset(wget_buffer *buf, char c, size_t length)
497 {
498 	if (likely(buf))
499 		buf->length = 0;
500 
501 	return wget_buffer_memset_append(buf, c, length);
502 }
503 
504 /**
505  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
506  * \param[in] c The byte to be copied at the end of the buffer
507  * \param[in] length How many times will the byte \p c be copied.
508  * \return The new length of the buffer \p buf.
509  *
510  * Copy the byte \p c at the end of the buffer \p buf repeatedly \p length times.
511  *
512  * If there's not enough space in \p buf, it is enlarged automatically
513  * (with wget_buffer_realloc()) at least \p length bytes.
514  */
wget_buffer_memset_append(wget_buffer * buf,char c,size_t length)515 size_t wget_buffer_memset_append(wget_buffer *buf, char c, size_t length)
516 {
517 	if (unlikely(!buf))
518 		return 0;
519 
520 	if (likely(length)) {
521 		if (unlikely(buf->size < buf->length + length))
522 			if (buffer_realloc(buf, buf->size * 2 + length) != WGET_E_SUCCESS)
523 				return buf->length;
524 
525 		memset(buf->data + buf->length, c, length);
526 		buf->length += length;
527 	}
528 	buf->data[buf->length] = 0; // always 0 terminate data to allow string functions
529 
530 	return buf->length;
531 }
532 
533 /**
534  * \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
535  * \return The buffer's new contents
536  *
537  * Remove all leading and trailing whitespace from the buffer \p buf.
538  *
539  * The transformation is done in-place, that is, the buffer's original content is overwritten
540  * with the new trimmed content.
541  */
wget_buffer_trim(wget_buffer * buf)542 char *wget_buffer_trim(wget_buffer *buf)
543 {
544 	if (unlikely(!buf))
545 		return NULL;
546 
547 	if (buf->length) {
548 		char *start = buf->data;
549 		char *end = start + buf->length - 1;
550 
551 		if (isspace(*end)) {
552 			/* Skip trailing spaces */
553 			for (; isspace(*end) && end >= start; end--)
554 				;
555 			end[1] = 0;
556 			buf->length = (size_t) (end - start + 1);
557 		}
558 
559 		if (isspace(*start)) {
560 			/* Skip leading spaces */
561 			for (; isspace(*start) && end >= start; start++)
562 				;
563 			buf->length = (size_t) (end - start + 1);
564 			/* Include trailing 0 */
565 			memmove(buf->data, start, buf->length + 1);
566 		}
567 	}
568 
569 	return buf->data;
570 }
571 /** @} */
572