mirror of
https://gitlab.com/gnuwget/wget2.git
synced 2026-01-14 02:01:39 +00:00
* libwget/base64.c (base64_encode): Drop explicit error length. * libwget/buffer_printf.c (convert_pointer): Likewise. Just cosmetics - allows to build with -Werror.
577 lines
14 KiB
C
577 lines
14 KiB
C
/*
|
|
* Copyright (c) 2012 Tim Ruehsen
|
|
* Copyright (c) 2015-2024 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of libwget.
|
|
*
|
|
* Libwget is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Libwget is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with libwget. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
* Memory buffer printf routines
|
|
*
|
|
* Changelog
|
|
* 24.09.2012 Tim Ruehsen created
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <c-ctype.h>
|
|
|
|
#include <wget.h>
|
|
#include "private.h"
|
|
|
|
/**
|
|
* \file
|
|
* \brief Buffer management functions
|
|
* \defgroup libwget-buffer Buffer management functions
|
|
* @{
|
|
*/
|
|
|
|
/* \cond _hide_internal_symbols */
|
|
#define FLAG_ZERO_PADDED 1U
|
|
#define FLAG_LEFT_ADJUST 2U
|
|
#define FLAG_ALTERNATE 4U
|
|
#define FLAG_SIGNED 8U
|
|
#define FLAG_DECIMAL 16U
|
|
#define FLAG_OCTAL 32U
|
|
#define FLAG_HEXLO 64U
|
|
#define FLAG_HEXUP 128U
|
|
/* \endcond */
|
|
|
|
static void copy_string(wget_buffer *buf, unsigned int flags, int field_width, int precision, const char *arg)
|
|
{
|
|
size_t length;
|
|
|
|
if (!arg) {
|
|
wget_buffer_strcat(buf, "(null)");
|
|
return;
|
|
}
|
|
|
|
if (precision >= 0) {
|
|
length = strnlen(arg, precision);
|
|
} else {
|
|
length = strlen(arg);
|
|
}
|
|
|
|
// debug_printf("flags=0x%02x field_width=%d precision=%d length=%zu arg='%s'\n",
|
|
// flags,field_width,precision,length,arg);
|
|
|
|
if (field_width) {
|
|
if ((unsigned)field_width > length) {
|
|
if (flags & FLAG_LEFT_ADJUST) {
|
|
wget_buffer_memcat(buf, arg, length);
|
|
wget_buffer_memset_append(buf, ' ', field_width - length);
|
|
} else {
|
|
wget_buffer_memset_append(buf, ' ', field_width - length);
|
|
wget_buffer_memcat(buf, arg, length);
|
|
}
|
|
} else {
|
|
wget_buffer_memcat(buf, arg, length);
|
|
}
|
|
} else {
|
|
wget_buffer_memcat(buf, arg, length);
|
|
}
|
|
}
|
|
|
|
static void convert_dec_fast(wget_buffer *buf, int arg)
|
|
{
|
|
char str[32]; // long enough to hold decimal long long
|
|
char *dst = str + sizeof(str) - 1;
|
|
int minus;
|
|
|
|
if (arg < 0) {
|
|
minus = 1;
|
|
arg = -arg;
|
|
} else
|
|
minus = 0;
|
|
|
|
while (arg >= 10) {
|
|
*dst-- = (arg % 10) + '0';
|
|
arg /= 10;
|
|
}
|
|
*dst-- = (arg % 10) + '0';
|
|
|
|
if (minus)
|
|
*dst-- = '-';
|
|
|
|
wget_buffer_memcat(buf, dst + 1, sizeof(str) - (dst - str) - 1);
|
|
}
|
|
|
|
static void convert_dec(wget_buffer *buf, unsigned int flags, int field_width, int precision, long long arg)
|
|
{
|
|
unsigned long long argu = (unsigned long long) arg;
|
|
char str[32], minus = 0; // long enough to hold decimal long long
|
|
char *dst = str + sizeof(str) - 1;
|
|
unsigned char c;
|
|
size_t length;
|
|
|
|
// info_printf("arg1 = %lld %lld\n",arg,-arg);
|
|
|
|
if (flags & FLAG_DECIMAL) {
|
|
if (flags & FLAG_SIGNED && arg < 0) {
|
|
minus = 1;
|
|
argu = -arg;
|
|
}
|
|
|
|
while (argu) {
|
|
*dst-- = argu % 10 + '0';
|
|
argu /= 10;
|
|
}
|
|
} else if (flags & FLAG_HEXLO) {
|
|
while (argu) {
|
|
// slightly faster than having a HEX[] lookup table
|
|
*dst-- = (c = (argu & 0xf)) >= 10 ? c + 'a' - 10 : c + '0';
|
|
argu >>= 4;
|
|
}
|
|
} else if (flags & FLAG_HEXUP) {
|
|
while (argu) {
|
|
// slightly faster than having a HEX[] lookup table
|
|
*dst-- = (c = (argu & 0xf)) >= 10 ? c + 'A' - 10 : c + '0';
|
|
argu >>= 4;
|
|
}
|
|
} else if (flags & FLAG_OCTAL) {
|
|
while (argu) {
|
|
*dst-- = (argu & 0x07) + '0';
|
|
argu >>= 3;
|
|
}
|
|
}
|
|
|
|
// info_printf("arg2 = %lld\n",arg);
|
|
|
|
|
|
dst++;
|
|
|
|
length = sizeof(str) - (dst - str);
|
|
|
|
if (precision < 0) {
|
|
precision = 1;
|
|
} else {
|
|
flags &= ~FLAG_ZERO_PADDED;
|
|
}
|
|
|
|
// info_printf("flags=0x%02x field_width=%d precision=%d length=%zd dst='%.*s'\n",
|
|
// flags,field_width,precision,length,length,dst);
|
|
|
|
if (field_width) {
|
|
if ((unsigned)field_width > length + minus) {
|
|
if (flags & FLAG_LEFT_ADJUST) {
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
|
|
if (length < (unsigned)precision) {
|
|
wget_buffer_memset_append(buf, '0', precision - length);
|
|
wget_buffer_memcat(buf, dst, length);
|
|
if (field_width > precision + minus)
|
|
wget_buffer_memset_append(buf, ' ', field_width - precision - minus);
|
|
} else {
|
|
wget_buffer_memcat(buf, dst, length);
|
|
wget_buffer_memset_append(buf, ' ', field_width - length - minus);
|
|
}
|
|
} else {
|
|
if (length < (unsigned)precision) {
|
|
if (field_width > precision + minus) {
|
|
if (flags & FLAG_ZERO_PADDED) {
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
wget_buffer_memset_append(buf, '0', field_width - precision - minus);
|
|
} else {
|
|
wget_buffer_memset_append(buf, ' ', field_width - precision - minus);
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
}
|
|
} else {
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
}
|
|
wget_buffer_memset_append(buf, '0', precision - length);
|
|
} else {
|
|
if (flags & FLAG_ZERO_PADDED) {
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
wget_buffer_memset_append(buf, '0', field_width - length - minus);
|
|
} else {
|
|
wget_buffer_memset_append(buf, ' ', field_width - length - minus);
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
}
|
|
}
|
|
wget_buffer_memcat(buf, dst, length);
|
|
}
|
|
} else {
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
if (length < (unsigned)precision)
|
|
wget_buffer_memset_append(buf, '0', precision - length);
|
|
wget_buffer_memcat(buf, dst, length);
|
|
}
|
|
} else {
|
|
if (minus)
|
|
wget_buffer_memset_append(buf, '-', 1);
|
|
|
|
if (length < (unsigned)precision)
|
|
wget_buffer_memset_append(buf, '0', precision - length);
|
|
|
|
wget_buffer_memcat(buf, dst, length);
|
|
}
|
|
}
|
|
|
|
static void convert_pointer(wget_buffer *buf, void *pointer)
|
|
{
|
|
static const char HEX[] = "0123456789abcdef";
|
|
char str[32]; // long enough to hold hexadecimal pointer
|
|
char *dst;
|
|
int length;
|
|
size_t arg;
|
|
|
|
if (!pointer) {
|
|
wget_buffer_memcat(buf, "0x0", 3);
|
|
return;
|
|
} else {
|
|
wget_buffer_memcat(buf, "0x", 2);
|
|
}
|
|
|
|
// convert to a size_t (covers full address room) tp allow integer arithmetic
|
|
arg = (size_t)pointer;
|
|
|
|
length = 0;
|
|
dst = str + sizeof(str);
|
|
*--dst = 0;
|
|
do {
|
|
*--dst = HEX[arg&0xF];
|
|
arg >>= 4;
|
|
length++;
|
|
} while (arg);
|
|
|
|
wget_buffer_memcat(buf, dst, length);
|
|
}
|
|
|
|
static const char *read_precision(const char *p, int *out, bool precision_is_external)
|
|
{
|
|
int precision;
|
|
|
|
if (precision_is_external) {
|
|
precision = *out;
|
|
if (precision < 0 )
|
|
precision = 0;
|
|
p++;
|
|
} else if (c_isdigit(*p)) {
|
|
precision = 0;
|
|
do {
|
|
precision = precision * 10 + (*p - '0');
|
|
} while (c_isdigit(*++p));
|
|
} else {
|
|
precision = -1;
|
|
}
|
|
|
|
*out = precision;
|
|
return p;
|
|
}
|
|
|
|
static const char *read_flag_chars(const char *p, unsigned int *out)
|
|
{
|
|
unsigned int flags;
|
|
|
|
for (flags = 0; *p; p++) {
|
|
if (*p == '0')
|
|
flags |= FLAG_ZERO_PADDED;
|
|
else if (*p == '-')
|
|
flags |= FLAG_LEFT_ADJUST;
|
|
else if (*p == '#')
|
|
flags |= FLAG_ALTERNATE;
|
|
else
|
|
break;
|
|
}
|
|
|
|
*out = flags;
|
|
return p;
|
|
}
|
|
|
|
static const char *read_field_width(const char *p, int *out, unsigned int *flags, bool width_is_external)
|
|
{
|
|
int field_width;
|
|
|
|
if (width_is_external) {
|
|
field_width = *out;
|
|
|
|
if (field_width < 0) {
|
|
*flags |= FLAG_LEFT_ADJUST;
|
|
field_width = -field_width;
|
|
}
|
|
|
|
p++;
|
|
} else {
|
|
for (field_width = 0; c_isdigit(*p); p++)
|
|
field_width = field_width * 10 + (*p - '0');
|
|
}
|
|
|
|
*out = field_width;
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
|
|
* \param[in] fmt A `printf(3)`-like format string
|
|
* \param[in] args A `va_list` with the format string placeholders' values
|
|
* \return Length of the buffer after appending the formatted string
|
|
*
|
|
* Formats the string \p fmt (with `printf(3)`-like args) and appends the result to the end
|
|
* of the buffer \p buf (using wget_buffer_memcat()).
|
|
*
|
|
* For more information, see `vprintf(3)`.
|
|
*/
|
|
size_t wget_buffer_vprintf_append(wget_buffer *buf, const char *fmt, va_list args)
|
|
{
|
|
const char *p = fmt, *begin;
|
|
int field_width, precision;
|
|
unsigned int flags;
|
|
long long arg;
|
|
unsigned long long argu;
|
|
|
|
if (!p)
|
|
return 0;
|
|
|
|
for (;*p;) {
|
|
|
|
/*
|
|
* Collect plain char sequence.
|
|
* Walk the string until we find a '%' character.
|
|
*/
|
|
for (begin = p; *p && *p != '%'; p++);
|
|
if (p != begin)
|
|
wget_buffer_memcat(buf, begin, p - begin);
|
|
|
|
if (!*p)
|
|
break;
|
|
|
|
/* Shortcut to %s and %p, handle %% */
|
|
if (*++p == 's') {
|
|
const char *s = va_arg(args, const char *);
|
|
wget_buffer_strcat(buf, s ? s : "(null)");
|
|
p++;
|
|
continue;
|
|
} else if (*p == 'd') {
|
|
convert_dec_fast(buf, va_arg(args, int));
|
|
p++;
|
|
continue;
|
|
} else if (*p == 'c') {
|
|
char c = (char ) va_arg(args, int);
|
|
wget_buffer_memcat(buf, &c, 1);
|
|
p++;
|
|
continue;
|
|
} else if (*p == 'p') {
|
|
convert_pointer(buf, va_arg(args, void *));
|
|
p++;
|
|
continue;
|
|
} else if (*p == '%') {
|
|
wget_buffer_memset_append(buf, '%', 1);
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
/* Read the flag chars (optional, simplified) */
|
|
p = read_flag_chars(p, &flags);
|
|
|
|
/*
|
|
* Read field width (optional).
|
|
* If '*', then the field width is given as an additional argument,
|
|
* which precedes the argument to be formatted.
|
|
*/
|
|
if (*p == '*') {
|
|
field_width = va_arg(args, int);
|
|
p = read_field_width(p, &field_width, &flags, 1);
|
|
} else {
|
|
p = read_field_width(p, &field_width, &flags, 0);
|
|
}
|
|
|
|
/*
|
|
* Read precision (optional).
|
|
* If '*', the precision is given as an additional argument,
|
|
* just as the case for the field width.
|
|
*/
|
|
if (*p == '.') {
|
|
if (*++p == '*') {
|
|
precision = va_arg(args, int);
|
|
p = read_precision(p, &precision, 1);
|
|
} else {
|
|
p = read_precision(p, &precision, 0);
|
|
}
|
|
} else
|
|
precision = -1;
|
|
|
|
/* Read length modifier (optional) */
|
|
switch (*p) {
|
|
case 'z':
|
|
arg = va_arg(args, ssize_t);
|
|
argu = (size_t)arg;
|
|
p++;
|
|
break;
|
|
|
|
case 'l':
|
|
if (p[1] == 'l') {
|
|
p += 2;
|
|
arg = va_arg(args, long long);
|
|
argu = (unsigned long long)arg;
|
|
} else {
|
|
p++;
|
|
arg = (long)va_arg(args, long);
|
|
argu = (unsigned long)arg;
|
|
}
|
|
break;
|
|
|
|
case 'L':
|
|
p++;
|
|
arg = va_arg(args, long long);
|
|
argu = (unsigned long long)arg;
|
|
break;
|
|
|
|
case 'h':
|
|
if (p[1] == 'h') {
|
|
p += 2;
|
|
arg = (signed char) va_arg(args, int);
|
|
argu = (unsigned char) arg;
|
|
} else {
|
|
p++;
|
|
arg = (short) va_arg(args, int);
|
|
argu = (unsigned short) arg;
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
p++;
|
|
copy_string(buf, flags, field_width, precision, va_arg(args, const char *));
|
|
continue;
|
|
|
|
case 'c':
|
|
{
|
|
char c[2] = { (char) va_arg(args, int), 0 };
|
|
p++;
|
|
copy_string(buf, flags, field_width, precision, c);
|
|
continue;
|
|
}
|
|
|
|
case 'p': // %p shortcut
|
|
p++;
|
|
convert_dec(buf, flags | FLAG_HEXLO | FLAG_ALTERNATE, field_width, precision, (long long)(ptrdiff_t)va_arg(args, void *));
|
|
continue;
|
|
|
|
default:
|
|
arg = va_arg(args, int);
|
|
argu = (unsigned int)arg;
|
|
}
|
|
|
|
if (*p == 'd' || *p == 'i') {
|
|
convert_dec(buf, flags | FLAG_SIGNED | FLAG_DECIMAL, field_width, precision, arg);
|
|
} else if (*p == 'u') {
|
|
convert_dec(buf, flags | FLAG_DECIMAL, field_width, precision, (long long) argu);
|
|
} else if (*p == 'x') {
|
|
convert_dec(buf, flags | FLAG_HEXLO, field_width, precision, (long long) argu);
|
|
} else if (*p == 'X') {
|
|
convert_dec(buf, flags | FLAG_HEXUP, field_width, precision, (long long) argu);
|
|
} else if (*p == 'o') {
|
|
convert_dec(buf, flags | FLAG_OCTAL, field_width, precision, (long long) argu);
|
|
} else {
|
|
/*
|
|
* This is an unknown conversion specifier,
|
|
* so just put '%' and move on.
|
|
*/
|
|
wget_buffer_memset_append(buf, '%', 1);
|
|
p = begin + 1;
|
|
continue;
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
return buf->length;
|
|
}
|
|
|
|
/**
|
|
* \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
|
|
* \param[in] fmt A `printf(3)`-like format string
|
|
* \param[in] args A `va_list` with the format string placeholders' values
|
|
* \return Length of the buffer after appending the formatted string
|
|
*
|
|
* Formats the string \p fmt (with `printf(3)`-like args) and overwrites the contents
|
|
* of the buffer \p buf with that formatted string.
|
|
*
|
|
* This is equivalent to the following code:
|
|
*
|
|
* buf->length = 0;
|
|
* wget_buffer_vprintf_append(buf, fmt, args);
|
|
*
|
|
* For more information, see `vprintf(3)`.
|
|
*/
|
|
size_t wget_buffer_vprintf(wget_buffer *buf, const char *fmt, va_list args)
|
|
{
|
|
buf->length = 0;
|
|
|
|
return wget_buffer_vprintf_append(buf, fmt, args);
|
|
}
|
|
|
|
/**
|
|
* \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
|
|
* \param[in] fmt A `printf(3)`-like format string
|
|
* \param[in] ... Variable arguments
|
|
* \return Length of the buffer after appending the formatted string
|
|
*
|
|
* Formats the string \p fmt (with `printf(3)`-like args) and appends the result to the end
|
|
* of the buffer \p buf (using wget_buffer_memcat()).
|
|
*
|
|
* This function is equivalent to wget_buffer_vprintf_append(), except in that it uses
|
|
* a variable number of arguments rather than a `va_list`.
|
|
*
|
|
* For more information, see `printf(3)`.
|
|
*/
|
|
size_t wget_buffer_printf_append(wget_buffer *buf, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
wget_buffer_vprintf_append(buf, fmt, args);
|
|
va_end(args);
|
|
|
|
return buf->length;
|
|
}
|
|
|
|
/**
|
|
* \param[in] buf A buffer, created with wget_buffer_init() or wget_buffer_alloc()
|
|
* \param[in] fmt A `printf(3)`-like format string
|
|
* \param[in] ... Variable arguments
|
|
* \return Length of the buffer after appending the formatted string
|
|
*
|
|
* Formats the string \p fmt (with `printf(3)`-like args) and overwrites the contents
|
|
* of the buffer \p buf with that formatted string.
|
|
*
|
|
* This function is equivalent to wget_buffer_vprintf(), except in that it uses
|
|
* a variable number of arguments rather than a `va_list`.
|
|
*
|
|
* For more information, see `printf(3)`.
|
|
*/
|
|
size_t wget_buffer_printf(wget_buffer *buf, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
size_t len = wget_buffer_vprintf(buf, fmt, args);
|
|
va_end(args);
|
|
|
|
return len;
|
|
}
|
|
/** @} */
|