1 /* 2 * buffer.c 3 * dynamically-managed buffers 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2024 pkgconf authors (see AUTHORS). 8 * 9 * Permission to use, copy, modify, and/or distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * This software is provided 'as is' and without any warranty, express or 14 * implied. In no event shall the authors be liable for any damages arising 15 * from the use of this software. 16 */ 17 18 #include <libpkgconf/stdinc.h> 19 #include <libpkgconf/libpkgconf.h> 20 21 /* 22 * !doc 23 * 24 * libpkgconf `buffer` module 25 * ========================== 26 * 27 * The libpkgconf `buffer` module contains the functions related to managing 28 * dynamically-allocated buffers. 29 */ 30 31 static inline size_t 32 target_allocation_size(size_t target_size) 33 { 34 return 128 + (128 * (target_size / 128)); 35 } 36 37 #if 0 38 static void 39 buffer_debug(pkgconf_buffer_t *buffer) 40 { 41 for (char *c = buffer->base; c <= buffer->end; c++) 42 { 43 fprintf(stderr, "%02x ", (unsigned char) *c); 44 } 45 46 pkgconf_output_file_fmt(stderr, "\n"); 47 } 48 #endif 49 50 /* 51 * !doc 52 * 53 * .. c:function:: bool pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text) 54 * 55 * Append a null-terminated string to the buffer, reallocating as necessary. 56 * 57 * :param pkgconf_buffer_t *buffer: The buffer to append to. 58 * :param char *text: The null-terminated string to append. 59 * :return: :code:`true` on success, :code:`false` on allocation failure. 60 */ 61 bool 62 pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text) 63 { 64 size_t needed = strlen(text) + 1; 65 size_t newsize = pkgconf_buffer_len(buffer) + needed; 66 67 char *newbase = realloc(buffer->base, target_allocation_size(newsize)); 68 if (newbase == NULL) 69 return false; 70 71 char *newend = newbase + pkgconf_buffer_len(buffer); 72 memcpy(newend, text, needed); 73 74 buffer->base = newbase; 75 buffer->end = newend + (needed - 1); 76 77 *buffer->end = '\0'; 78 return true; 79 } 80 81 /* 82 * !doc 83 * 84 * .. c:function:: bool pkgconf_buffer_append_slice(pkgconf_buffer_t *buf, const char *p, size_t n) 85 * 86 * Append a slice of *n* bytes to the buffer. Does nothing if *n* is zero. 87 * 88 * :param pkgconf_buffer_t *buf: The buffer to append to. 89 * :param char *p: Pointer to the byte sequence to append. 90 * :param size_t n: Number of bytes to append. 91 * :return: :code:`true` on success, :code:`false` on allocation failure. 92 */ 93 bool 94 pkgconf_buffer_append_slice(pkgconf_buffer_t *buf, const char *p, size_t n) 95 { 96 if (n == 0) 97 return true; 98 99 for (size_t i = 0; i < n; i++) 100 { 101 if (!pkgconf_buffer_push_byte(buf, p[i])) 102 return false; 103 } 104 105 return true; 106 } 107 108 /* 109 * !doc 110 * 111 * .. c:function:: bool pkgconf_buffer_append_vfmt(pkgconf_buffer_t *buffer, const char *fmt, va_list src_va) 112 * 113 * Append a formatted string to the buffer using a :code:`va_list`. 114 * 115 * :param pkgconf_buffer_t *buffer: The buffer to append to. 116 * :param char *fmt: A printf-style format string. 117 * :param va_list src_va: The variadic argument list for the format string. 118 * :return: :code:`true` on success, :code:`false` on allocation failure. 119 */ 120 bool 121 pkgconf_buffer_append_vfmt(pkgconf_buffer_t *buffer, const char *fmt, va_list src_va) 122 { 123 va_list va; 124 char *buf; 125 size_t needed; 126 127 va_copy(va, src_va); 128 needed = vsnprintf(NULL, 0, fmt, va) + 1; 129 va_end(va); 130 131 buf = malloc(needed); 132 if (buf == NULL) 133 return false; 134 135 va_copy(va, src_va); 136 vsnprintf(buf, needed, fmt, va); 137 va_end(va); 138 139 bool ret = pkgconf_buffer_append(buffer, buf); 140 141 free(buf); 142 143 return ret; 144 } 145 146 /* 147 * !doc 148 * 149 * .. c:function:: bool pkgconf_buffer_append_fmt(pkgconf_buffer_t *buffer, const char *fmt, ...) 150 * 151 * Append a formatted string to the buffer using variadic arguments. 152 * 153 * :param pkgconf_buffer_t *buffer: The buffer to append to. 154 * :param char *fmt: A printf-style format string. 155 * :return: :code:`true` on success, :code:`false` on allocation failure. 156 */ 157 bool 158 pkgconf_buffer_append_fmt(pkgconf_buffer_t *buffer, const char *fmt, ...) 159 { 160 va_list va; 161 162 va_start(va, fmt); 163 bool ret = pkgconf_buffer_append_vfmt(buffer, fmt, va); 164 va_end(va); 165 166 return ret; 167 } 168 169 /* 170 * !doc 171 * 172 * .. c:function:: bool pkgconf_buffer_prepend(pkgconf_buffer_t *buffer, const char *text) 173 * 174 * Prepend a null-terminated string to the beginning of the buffer. 175 * If *text* is NULL, the buffer contents are unchanged. 176 * 177 * :param pkgconf_buffer_t *buffer: The buffer to prepend to. 178 * :param char *text: The null-terminated string to prepend, or NULL. 179 * :return: :code:`true` on success, :code:`false` on allocation failure. 180 */ 181 bool 182 pkgconf_buffer_prepend(pkgconf_buffer_t *buffer, const char *text) 183 { 184 pkgconf_buffer_t tmpbuf = PKGCONF_BUFFER_INITIALIZER; 185 186 if (text != NULL && !pkgconf_buffer_append(&tmpbuf, text)) 187 return false; 188 189 if (!pkgconf_buffer_append(&tmpbuf, pkgconf_buffer_str_or_empty(buffer))) 190 { 191 pkgconf_buffer_finalize(&tmpbuf); 192 return false; 193 } 194 195 if (pkgconf_buffer_len(&tmpbuf)) 196 pkgconf_buffer_copy(&tmpbuf, buffer); 197 198 pkgconf_buffer_finalize(&tmpbuf); 199 200 return true; 201 } 202 203 /* 204 * !doc 205 * 206 * .. c:function:: bool pkgconf_buffer_push_byte(pkgconf_buffer_t *buffer, char byte) 207 * 208 * Append a single byte to the buffer, reallocating as necessary. 209 * 210 * :param pkgconf_buffer_t *buffer: The buffer to append to. 211 * :param char byte: The byte to append. 212 * :return: :code:`true` on success, :code:`false` on allocation failure. 213 */ 214 bool 215 pkgconf_buffer_push_byte(pkgconf_buffer_t *buffer, char byte) 216 { 217 size_t newsize = pkgconf_buffer_len(buffer) + 1; 218 char *newbase = realloc(buffer->base, target_allocation_size(newsize)); 219 if (newbase == NULL) 220 return false; 221 222 char *newend = newbase + newsize; 223 *(newend - 1) = byte; 224 *newend = '\0'; 225 226 buffer->base = newbase; 227 buffer->end = newend; 228 229 return true; 230 } 231 232 /* 233 * !doc 234 * 235 * .. c:function:: bool pkgconf_buffer_trim_byte(pkgconf_buffer_t *buffer) 236 * 237 * Remove the last byte from the buffer. The buffer must be non-empty. 238 * 239 * :param pkgconf_buffer_t *buffer: The buffer to trim. 240 * :return: :code:`true` on success, :code:`false` on allocation failure. 241 */ 242 bool 243 pkgconf_buffer_trim_byte(pkgconf_buffer_t *buffer) 244 { 245 size_t len = pkgconf_buffer_len(buffer); 246 if (len == 0) 247 return false; 248 249 size_t newsize = len - 1; 250 char *newbase = realloc(buffer->base, target_allocation_size(newsize)); 251 252 if (newbase == NULL) 253 return false; 254 255 buffer->base = newbase; 256 buffer->end = newbase + newsize; 257 *(buffer->end) = '\0'; 258 259 return true; 260 } 261 262 /* 263 * !doc 264 * 265 * .. c:function:: void pkgconf_buffer_finalize(pkgconf_buffer_t *buffer) 266 * 267 * Free all memory owned by the buffer and reset it to an empty state. 268 * 269 * :param pkgconf_buffer_t *buffer: The buffer to finalize. 270 * :return: nothing 271 */ 272 void 273 pkgconf_buffer_finalize(pkgconf_buffer_t *buffer) 274 { 275 free(buffer->base); 276 buffer->base = buffer->end = NULL; 277 } 278 279 /* 280 * !doc 281 * 282 * .. c:function:: bool pkgconf_buffer_fputs(pkgconf_buffer_t *buffer, FILE *out) 283 * 284 * Write the buffer contents followed by a newline to a file stream. 285 * If the buffer is empty, only a newline is written. 286 * 287 * :param pkgconf_buffer_t *buffer: The buffer to write. 288 * :param FILE *out: The output file stream. 289 * :return: :code:`true` on success, :code:`false` on I/O error. 290 * :code:`errno` will be set by fputs/fputc. 291 */ 292 bool 293 pkgconf_buffer_fputs(pkgconf_buffer_t *buffer, FILE *out) 294 { 295 if (pkgconf_buffer_len(buffer) != 0) 296 { 297 if (fputs(pkgconf_buffer_str(buffer), out) == EOF) 298 return false; 299 } 300 301 if (fputc('\n', out) == EOF) 302 return false; 303 304 return true; 305 } 306 307 /* 308 * !doc 309 * 310 * .. c:function:: bool pkgconf_buffer_vjoin(pkgconf_buffer_t *buffer, char delim, va_list src_va) 311 * 312 * Join a NULL-terminated list of strings into the buffer, separated by *delim*. 313 * Uses a :code:`va_list` for the string arguments. 314 * 315 * :param pkgconf_buffer_t *buffer: The buffer to join into. 316 * :param char delim: The delimiter byte inserted between each argument. 317 * :param va_list src_va: The variadic argument list of :code:`const char *` strings, terminated by NULL. 318 * :return: :code:`true` on success, :code:`false` on allocation failure. 319 */ 320 bool 321 pkgconf_buffer_vjoin(pkgconf_buffer_t *buffer, char delim, va_list src_va) 322 { 323 va_list va; 324 const char *arg; 325 326 va_copy(va, src_va); 327 328 while ((arg = va_arg(va, const char *)) != NULL) 329 { 330 if (pkgconf_buffer_str(buffer) != NULL) 331 { 332 if (!pkgconf_buffer_push_byte(buffer, delim)) 333 { 334 va_end(va); 335 return false; 336 } 337 } 338 339 if (!pkgconf_buffer_append(buffer, arg)) 340 { 341 va_end(va); 342 return false; 343 } 344 } 345 346 va_end(va); 347 348 return true; 349 } 350 351 /* 352 * !doc 353 * 354 * .. c:function:: bool pkgconf_buffer_join(pkgconf_buffer_t *buffer, int delim, ...) 355 * 356 * Join a NULL-terminated list of strings into the buffer, separated by *delim*. 357 * The *delim* parameter is typed as :code:`int` due to C variadic promotion rules. 358 * 359 * :param pkgconf_buffer_t *buffer: The buffer to join into. 360 * :param int delim: The delimiter byte inserted between each argument (cast to :code:`char` internally). 361 * :return: :code:`true` on success, :code:`false` on allocation failure. 362 */ 363 bool 364 pkgconf_buffer_join(pkgconf_buffer_t *buffer, int delim, ...) 365 { 366 va_list va; 367 368 va_start(va, delim); 369 bool ret = pkgconf_buffer_vjoin(buffer, (char)delim, va); 370 va_end(va); 371 372 return ret; 373 } 374 375 /* 376 * !doc 377 * 378 * .. c:function:: bool pkgconf_buffer_has_prefix(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *prefix) 379 * 380 * Test whether the buffer begins with the contents of *prefix*. 381 * 382 * :param pkgconf_buffer_t *haystack: The buffer to search in. 383 * :param pkgconf_buffer_t *prefix: The prefix to test for. 384 * :return: :code:`true` if *haystack* starts with *prefix*, :code:`false` otherwise. 385 */ 386 bool 387 pkgconf_buffer_has_prefix(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *prefix) 388 { 389 const char *haystack_str = pkgconf_buffer_str_or_empty(haystack); 390 const char *prefix_str = pkgconf_buffer_str_or_empty(prefix); 391 392 return strncmp(haystack_str, prefix_str, strlen(prefix_str)) == 0; 393 } 394 395 /* 396 * !doc 397 * 398 * .. c:function:: bool pkgconf_buffer_contains(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle) 399 * 400 * Test whether the buffer contains the contents of *needle* as a substring. 401 * 402 * :param pkgconf_buffer_t *haystack: The buffer to search in. 403 * :param pkgconf_buffer_t *needle: The substring to search for. 404 * :return: :code:`true` if *needle* is found, :code:`false` otherwise. 405 */ 406 bool 407 pkgconf_buffer_contains(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle) 408 { 409 const char *haystack_str = pkgconf_buffer_str_or_empty(haystack); 410 const char *needle_str = pkgconf_buffer_str_or_empty(needle); 411 412 return strstr(haystack_str, needle_str) != NULL; 413 } 414 415 /* 416 * !doc 417 * 418 * .. c:function:: bool pkgconf_buffer_contains_byte(const pkgconf_buffer_t *haystack, char needle) 419 * 420 * Test whether the buffer contains a given byte. 421 * 422 * :param pkgconf_buffer_t *haystack: The buffer to search in. 423 * :param char needle: The byte to search for. 424 * :return: :code:`true` if *needle* is found, :code:`false` otherwise. 425 */ 426 427 bool 428 pkgconf_buffer_contains_byte(const pkgconf_buffer_t *haystack, char needle) 429 { 430 const char *haystack_str = pkgconf_buffer_str_or_empty(haystack); 431 return strchr(haystack_str, needle) != NULL; 432 } 433 434 /* 435 * !doc 436 * 437 * .. c:function:: bool pkgconf_buffer_match(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle) 438 * 439 * Test whether two buffers have identical contents. 440 * 441 * :param pkgconf_buffer_t *haystack: The first buffer. 442 * :param pkgconf_buffer_t *needle: The second buffer. 443 * :return: :code:`true` if the buffers have the same length and contents, :code:`false` otherwise. 444 */ 445 bool 446 pkgconf_buffer_match(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle) 447 { 448 const char *haystack_str = pkgconf_buffer_str_or_empty(haystack); 449 const char *needle_str = pkgconf_buffer_str_or_empty(needle); 450 451 if (pkgconf_buffer_len(haystack) != pkgconf_buffer_len(needle)) 452 return false; 453 454 return memcmp(haystack_str, needle_str, pkgconf_buffer_len(haystack)) == 0; 455 } 456 457 /* 458 * !doc 459 * 460 * .. c:function:: bool pkgconf_buffer_subst(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const char *pattern, const char *value) 461 * 462 * Copy *src* into *dest*, replacing all occurrences of *pattern* with *value*. 463 * If *pattern* is empty, *src* is appended to *dest* unmodified. 464 * 465 * :param pkgconf_buffer_t *dest: The destination buffer. 466 * :param pkgconf_buffer_t *src: The source buffer. 467 * :param char *pattern: The pattern string to search for. 468 * :param char *value: The replacement string. 469 * :return: :code:`true` on success, :code:`false` on allocation failure. 470 */ 471 bool 472 pkgconf_buffer_subst(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const char *pattern, const char *value) 473 { 474 const char *iter = src->base; 475 476 if (pattern == NULL) 477 pattern = ""; 478 479 if (value == NULL) 480 value = ""; 481 482 size_t pattern_len = strlen(pattern); 483 484 if (!pkgconf_buffer_len(src)) 485 return true; 486 487 if (!pattern_len) 488 return pkgconf_buffer_append(dest, pkgconf_buffer_str(src)); 489 490 while (iter < src->end) 491 { 492 if ((size_t)(src->end - iter) >= pattern_len && !memcmp(iter, pattern, pattern_len)) 493 { 494 if (!pkgconf_buffer_append(dest, value)) 495 return false; 496 497 iter += pattern_len; 498 } 499 else 500 { 501 if (!pkgconf_buffer_push_byte(dest, *iter++)) 502 return false; 503 } 504 } 505 506 return true; 507 } 508 509 /* 510 * !doc 511 * 512 * .. c:function:: bool pkgconf_buffer_escape(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const pkgconf_span_t *spans, size_t nspans) 513 * 514 * Copy *src* into *dest*, inserting a backslash before any byte that falls 515 * within the provided character spans. 516 * 517 * :param pkgconf_buffer_t *dest: The destination buffer. 518 * :param pkgconf_buffer_t *src: The source buffer. 519 * :param pkgconf_span_t *spans: Array of character spans to escape. 520 * :param size_t nspans: Number of entries in the *spans* array. 521 * :return: :code:`true` on success, :code:`false` on allocation failure. 522 */ 523 bool 524 pkgconf_buffer_escape(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const pkgconf_span_t *spans, size_t nspans) 525 { 526 const char *p = pkgconf_buffer_str(src); 527 528 if (!pkgconf_buffer_len(src)) 529 return true; 530 531 for (; *p; p++) 532 { 533 if (pkgconf_span_contains((unsigned char) *p, spans, nspans)) 534 { 535 if (!pkgconf_buffer_push_byte(dest, '\\')) 536 return false; 537 } 538 539 if (!pkgconf_buffer_push_byte(dest, *p)) 540 return false; 541 } 542 543 return true; 544 } 545