mirror of
https://github.com/pkgconf/pkgconf.git
synced 2026-01-27 18:05:05 +00:00
247 lines
5.4 KiB
C
247 lines
5.4 KiB
C
/*
|
|
* buffer.c
|
|
* dynamically-managed buffers
|
|
*
|
|
* Copyright (c) 2024 pkgconf authors (see AUTHORS).
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* This software is provided 'as is' and without any warranty, express or
|
|
* implied. In no event shall the authors be liable for any damages arising
|
|
* from the use of this software.
|
|
*/
|
|
|
|
#include <libpkgconf/stdinc.h>
|
|
#include <libpkgconf/libpkgconf.h>
|
|
|
|
/*
|
|
* !doc
|
|
*
|
|
* libpkgconf `buffer` module
|
|
* ==========================
|
|
*
|
|
* The libpkgconf `buffer` module contains the functions related to managing
|
|
* dynamically-allocated buffers.
|
|
*/
|
|
|
|
static inline size_t
|
|
target_allocation_size(size_t target_size)
|
|
{
|
|
return 4096 + (4096 * (target_size / 4096));
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
buffer_debug(pkgconf_buffer_t *buffer)
|
|
{
|
|
for (char *c = buffer->base; c <= buffer->end; c++) {
|
|
fprintf(stderr, "%02x ", (unsigned char) *c);
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
|
}
|
|
#endif
|
|
|
|
void
|
|
pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text)
|
|
{
|
|
size_t needed = strlen(text) + 1;
|
|
size_t newsize = pkgconf_buffer_len(buffer) + needed;
|
|
|
|
char *newbase = realloc(buffer->base, target_allocation_size(newsize));
|
|
|
|
/* XXX: silently failing here is antisocial */
|
|
if (newbase == NULL)
|
|
return;
|
|
|
|
char *newend = newbase + pkgconf_buffer_len(buffer);
|
|
pkgconf_strlcpy(newend, text, needed);
|
|
|
|
buffer->base = newbase;
|
|
buffer->end = newend + (needed - 1);
|
|
|
|
*buffer->end = '\0';
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_append_vfmt(pkgconf_buffer_t *buffer, const char *fmt, va_list src_va)
|
|
{
|
|
va_list va;
|
|
char *buf;
|
|
size_t needed;
|
|
|
|
va_copy(va, src_va);
|
|
needed = vsnprintf(NULL, 0, fmt, va) + 1;
|
|
va_end(va);
|
|
|
|
buf = malloc(needed);
|
|
|
|
va_copy(va, src_va);
|
|
vsnprintf(buf, needed, fmt, va);
|
|
va_end(va);
|
|
|
|
pkgconf_buffer_append(buffer, buf);
|
|
|
|
free(buf);
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_append_fmt(pkgconf_buffer_t *buffer, const char *fmt, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
pkgconf_buffer_append_vfmt(buffer, fmt, va);
|
|
va_end(va);
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_push_byte(pkgconf_buffer_t *buffer, char byte)
|
|
{
|
|
size_t newsize = pkgconf_buffer_len(buffer) + 1;
|
|
char *newbase = realloc(buffer->base, target_allocation_size(newsize));
|
|
|
|
/* XXX: silently failing here remains antisocial */
|
|
if (newbase == NULL)
|
|
return;
|
|
|
|
char *newend = newbase + newsize;
|
|
*(newend - 1) = byte;
|
|
*newend = '\0';
|
|
|
|
buffer->base = newbase;
|
|
buffer->end = newend;
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_trim_byte(pkgconf_buffer_t *buffer)
|
|
{
|
|
size_t newsize = pkgconf_buffer_len(buffer) - 1;
|
|
char *newbase = realloc(buffer->base, target_allocation_size(newsize));
|
|
|
|
buffer->base = newbase;
|
|
buffer->end = newbase + newsize;
|
|
*(buffer->end) = '\0';
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_finalize(pkgconf_buffer_t *buffer)
|
|
{
|
|
free(buffer->base);
|
|
buffer->base = buffer->end = NULL;
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_fputs(pkgconf_buffer_t *buffer, FILE *out)
|
|
{
|
|
if (pkgconf_buffer_len(buffer) != 0)
|
|
fputs(pkgconf_buffer_str(buffer), out);
|
|
|
|
fputc('\n', out);
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_vjoin(pkgconf_buffer_t *buffer, char delim, va_list src_va)
|
|
{
|
|
va_list va;
|
|
const char *arg;
|
|
|
|
va_copy(va, src_va);
|
|
|
|
while ((arg = va_arg(va, const char *)) != NULL)
|
|
{
|
|
if (pkgconf_buffer_str(buffer) != NULL)
|
|
pkgconf_buffer_push_byte(buffer, delim);
|
|
|
|
pkgconf_buffer_append(buffer, arg);
|
|
}
|
|
|
|
va_end(va);
|
|
}
|
|
|
|
// NOTE: due to C's rules regarding promotion in variable args and permissible variables, delim must
|
|
// be an int here.
|
|
void
|
|
pkgconf_buffer_join(pkgconf_buffer_t *buffer, int delim, ...)
|
|
{
|
|
va_list va;
|
|
|
|
va_start(va, delim);
|
|
pkgconf_buffer_vjoin(buffer, (char)delim, va);
|
|
va_end(va);
|
|
}
|
|
|
|
bool
|
|
pkgconf_buffer_contains(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle)
|
|
{
|
|
const char *haystack_str = pkgconf_buffer_str_or_empty(haystack);
|
|
const char *needle_str = pkgconf_buffer_str_or_empty(needle);
|
|
|
|
return strstr(haystack_str, needle_str) != NULL;
|
|
}
|
|
|
|
bool
|
|
pkgconf_buffer_contains_byte(const pkgconf_buffer_t *haystack, char needle)
|
|
{
|
|
const char *haystack_str = pkgconf_buffer_str_or_empty(haystack);
|
|
return strchr(haystack_str, needle) != NULL;
|
|
}
|
|
|
|
bool
|
|
pkgconf_buffer_match(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle)
|
|
{
|
|
const char *haystack_str = pkgconf_buffer_str_or_empty(haystack);
|
|
const char *needle_str = pkgconf_buffer_str_or_empty(needle);
|
|
|
|
if (pkgconf_buffer_len(haystack) != pkgconf_buffer_len(needle))
|
|
return false;
|
|
|
|
return memcmp(haystack_str, needle_str, pkgconf_buffer_len(haystack)) == 0;
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_subst(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const char *pattern, const char *value)
|
|
{
|
|
const char *iter = src->base;
|
|
size_t pattern_len = strlen(pattern);
|
|
|
|
if (!pkgconf_buffer_len(src))
|
|
return;
|
|
|
|
if (!pattern_len)
|
|
{
|
|
pkgconf_buffer_append(dest, pkgconf_buffer_str(src));
|
|
return;
|
|
}
|
|
|
|
while (iter < src->end)
|
|
{
|
|
if ((size_t)(src->end - iter) >= pattern_len && !memcmp(iter, pattern, pattern_len))
|
|
{
|
|
pkgconf_buffer_append(dest, value);
|
|
iter += pattern_len;
|
|
}
|
|
else
|
|
pkgconf_buffer_push_byte(dest, *iter++);
|
|
}
|
|
}
|
|
|
|
void
|
|
pkgconf_buffer_escape(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const pkgconf_span_t *spans, size_t nspans)
|
|
{
|
|
const char *p = pkgconf_buffer_str(src);
|
|
|
|
if (!pkgconf_buffer_len(src))
|
|
return;
|
|
|
|
for (; *p; p++)
|
|
{
|
|
if (pkgconf_span_contains((unsigned char) *p, spans, nspans))
|
|
pkgconf_buffer_push_byte(dest, '\\');
|
|
|
|
pkgconf_buffer_push_byte(dest, *p);
|
|
}
|
|
}
|