/src/wget2/libwget/buffer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2012 Tim Ruehsen |
3 | | * Copyright (c) 2015-2024 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 | | */ |
153 | | int wget_buffer_init(wget_buffer *buf, char *data, size_t size) |
154 | 557k | { |
155 | 557k | if (data && likely(size)) { |
156 | 442k | buf->size = size - 1; |
157 | 442k | buf->data = data; |
158 | 442k | *buf->data = 0; // always 0 terminate data to allow string functions |
159 | 442k | buf->release_data = 0; |
160 | 442k | } else { |
161 | 115k | if (!size) |
162 | 0 | size = 127; |
163 | 115k | buf->size = size; |
164 | 115k | if (!(buf->data = wget_malloc(size + 1))) { |
165 | 0 | buf->error = 1; |
166 | 0 | return WGET_E_MEMORY; |
167 | 0 | } |
168 | 115k | *buf->data = 0; // always 0 terminate data to allow string functions |
169 | 115k | buf->release_data = 1; |
170 | 115k | } |
171 | | |
172 | 557k | buf->error = 0; |
173 | 557k | buf->release_buf = 0; |
174 | 557k | buf->length = 0; |
175 | | |
176 | 557k | return WGET_E_SUCCESS; |
177 | 557k | } |
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 | | */ |
189 | | wget_buffer *wget_buffer_alloc(size_t size) |
190 | 11.2k | { |
191 | 11.2k | wget_buffer *buf; |
192 | | |
193 | 11.2k | if (!(buf = wget_malloc(sizeof(wget_buffer)))) |
194 | 0 | return NULL; |
195 | | |
196 | 11.2k | if (wget_buffer_init(buf, NULL, size) < 0) { |
197 | 0 | xfree(buf); |
198 | 0 | return NULL; |
199 | 0 | } |
200 | | |
201 | 11.2k | buf->release_buf = 1; |
202 | | |
203 | 11.2k | return buf; |
204 | 11.2k | } |
205 | | |
206 | | static int buffer_realloc(wget_buffer *buf, size_t size) |
207 | 364k | { |
208 | 364k | char *old_data = buf->data; |
209 | | |
210 | 364k | if (buf->release_data) |
211 | 53.3k | buf->data = wget_realloc(buf->data, size + 1); |
212 | 311k | else |
213 | 311k | buf->data = wget_malloc(size + 1); |
214 | | |
215 | 364k | if (!buf->data) { |
216 | 0 | buf->data = old_data; |
217 | 0 | buf->error = 1; |
218 | 0 | return WGET_E_MEMORY; |
219 | 0 | } |
220 | | |
221 | 364k | if (!buf->release_data) { |
222 | 311k | if (likely(old_data) && buf->length) |
223 | 1.29k | memcpy(buf->data, old_data, buf->length + 1); |
224 | 310k | else |
225 | 310k | *buf->data = 0; // always 0 terminate data to allow string functions |
226 | | |
227 | 311k | buf->release_data = 1; |
228 | 311k | } |
229 | | |
230 | 364k | buf->size = size; |
231 | | |
232 | 364k | return WGET_E_SUCCESS; |
233 | 364k | } |
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 | | */ |
245 | | int wget_buffer_ensure_capacity(wget_buffer *buf, size_t size) |
246 | 0 | { |
247 | 0 | if (likely(buf)) { |
248 | 0 | if (buf->size < size) |
249 | 0 | return buffer_realloc(buf, size); |
250 | 0 | } |
251 | | |
252 | 0 | return WGET_E_SUCCESS; |
253 | 0 | } |
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 | | */ |
268 | | void wget_buffer_deinit(wget_buffer *buf) |
269 | 465k | { |
270 | 465k | if (buf->release_data) { |
271 | 334k | xfree(buf->data); |
272 | 334k | buf->release_data = 0; |
273 | 334k | } |
274 | | |
275 | 465k | if (buf->release_buf) |
276 | 11.2k | wget_free(buf); // do not use xfree() since buf is NONNULL |
277 | 465k | } |
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 | | */ |
291 | | void wget_buffer_free(wget_buffer **buf) |
292 | 22.9k | { |
293 | 22.9k | if (likely(buf && *buf)) { |
294 | 11.2k | wget_buffer_deinit(*buf); |
295 | 11.2k | *buf = NULL; |
296 | 11.2k | } |
297 | 22.9k | } |
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 | | */ |
311 | | void wget_buffer_free_data(wget_buffer *buf) |
312 | 0 | { |
313 | 0 | if (likely(buf)) { |
314 | 0 | if (buf->release_data) { |
315 | 0 | xfree(buf->data); |
316 | 0 | buf->release_data = 0; |
317 | 0 | buf->size = 0; |
318 | 0 | } |
319 | 0 | } |
320 | 0 | } |
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 | | */ |
333 | | void wget_buffer_reset(wget_buffer *buf) |
334 | 27.9k | { |
335 | 27.9k | if (likely(buf)) { |
336 | 27.9k | buf->length = 0; |
337 | 27.9k | *buf->data = 0; |
338 | 27.9k | } |
339 | 27.9k | } |
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 | | */ |
356 | | size_t wget_buffer_memcpy(wget_buffer *buf, const void *data, size_t length) |
357 | 6.71k | { |
358 | 6.71k | if (unlikely(!buf)) |
359 | 0 | return 0; |
360 | | |
361 | 6.71k | buf->length = 0; |
362 | | |
363 | 6.71k | return wget_buffer_memcat(buf, data, length); |
364 | 6.71k | } |
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 | | */ |
378 | | size_t wget_buffer_memcat(wget_buffer *buf, const void *data, size_t length) |
379 | 3.62M | { |
380 | 3.62M | if (unlikely(!buf)) |
381 | 0 | return 0; |
382 | | |
383 | 3.62M | if (likely(length)) { |
384 | 3.56M | if (buf->size < buf->length + length) |
385 | 361k | if (buffer_realloc(buf, buf->size * 2 + length) != WGET_E_SUCCESS) |
386 | 0 | return buf->length; |
387 | | |
388 | 3.56M | if (likely(data)) |
389 | 3.56M | memcpy(buf->data + buf->length, data, length); |
390 | 0 | else |
391 | 0 | memset(buf->data + buf->length, 0, length); |
392 | 3.56M | buf->length += length; |
393 | 3.56M | } |
394 | 3.62M | buf->data[buf->length] = 0; // always 0 terminate data to allow string functions |
395 | | |
396 | 3.62M | return buf->length; |
397 | 3.62M | } |
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 | | */ |
414 | | size_t wget_buffer_strcpy(wget_buffer *buf, const char *s) |
415 | 5.59k | { |
416 | 5.59k | if (likely(buf)) |
417 | 5.59k | buf->length = 0; |
418 | | |
419 | 5.59k | return wget_buffer_memcat(buf, s, likely(s) ? strlen(s) : 0); |
420 | 5.59k | } |
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 | | */ |
436 | | size_t wget_buffer_strcat(wget_buffer *buf, const char *s) |
437 | 285k | { |
438 | 285k | return wget_buffer_memcat(buf, s, likely(s) ? strlen(s) : 0); |
439 | 285k | } |
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 | | */ |
455 | | size_t wget_buffer_bufcpy(wget_buffer *buf, wget_buffer *src) |
456 | 0 | { |
457 | 0 | if (likely(src)) |
458 | 0 | return wget_buffer_memcpy(buf, src->data, src->length); |
459 | 0 | else |
460 | 0 | return wget_buffer_memcpy(buf, NULL, 0); |
461 | 0 | } |
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 | | */ |
476 | | size_t wget_buffer_bufcat(wget_buffer *buf, wget_buffer *src) |
477 | 3.84k | { |
478 | 3.84k | if (likely(src)) |
479 | 3.84k | return wget_buffer_memcat(buf, src->data, src->length); |
480 | 0 | else |
481 | 0 | return wget_buffer_memcat(buf, NULL, 0); |
482 | 3.84k | } |
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 | | */ |
496 | | size_t wget_buffer_memset(wget_buffer *buf, char c, size_t length) |
497 | 0 | { |
498 | 0 | if (likely(buf)) |
499 | 0 | buf->length = 0; |
500 | |
|
501 | 0 | return wget_buffer_memset_append(buf, c, length); |
502 | 0 | } |
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 | | */ |
515 | | size_t wget_buffer_memset_append(wget_buffer *buf, char c, size_t length) |
516 | 2.46M | { |
517 | 2.46M | if (unlikely(!buf)) |
518 | 0 | return 0; |
519 | | |
520 | 2.46M | if (likely(length)) { |
521 | 2.46M | if (unlikely(buf->size < buf->length + length)) |
522 | 3.71k | if (buffer_realloc(buf, buf->size * 2 + length) != WGET_E_SUCCESS) |
523 | 0 | return buf->length; |
524 | | |
525 | 2.46M | memset(buf->data + buf->length, c, length); |
526 | 2.46M | buf->length += length; |
527 | 2.46M | } |
528 | 2.46M | buf->data[buf->length] = 0; // always 0 terminate data to allow string functions |
529 | | |
530 | 2.46M | return buf->length; |
531 | 2.46M | } |
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 | | */ |
542 | | char *wget_buffer_trim(wget_buffer *buf) |
543 | 0 | { |
544 | 0 | if (unlikely(!buf)) |
545 | 0 | return NULL; |
546 | | |
547 | 0 | while (buf->length > 0 && isspace(buf->data[buf->length - 1])) { |
548 | 0 | buf->length--; |
549 | 0 | } |
550 | 0 | buf->data[buf->length] = 0; |
551 | |
|
552 | 0 | size_t spaces = 0; |
553 | 0 | while (buf->length > 0 && isspace(buf->data[spaces])) |
554 | 0 | spaces++; |
555 | |
|
556 | 0 | if (spaces) { |
557 | 0 | buf->length -= spaces; |
558 | | /* Include trailing 0 */ |
559 | 0 | memmove(buf->data, buf->data + spaces, buf->length + 1); |
560 | 0 | } |
561 | |
|
562 | 0 | return buf->data; |
563 | 0 | } |
564 | | /** @} */ |