Files
wget2/libwget/buffer_printf.c
Tim Rühsen c6ee3d79ad Fix syntax-check 'sc_prohibit_have_config_h'
* cfg.mk: Remove sc_prohibit_have_config_h from local-checks-to-skip
* libwget/*.c: Include <config.h> unconditionally
* src/*.c: Likewise
* tests/*.c: Likewise
2017-04-30 22:01:34 +02:00

462 lines
10 KiB
C

/*
* Copyright(c) 2012 Tim Ruehsen
* Copyright(c) 2015-2016 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 <c-ctype.h>
#include <wget.h>
#include "private.h"
#define FLAG_ZERO_PADDED 1
#define FLAG_LEFT_ADJUST 2
#define FLAG_ALTERNATE 4
#define FLAG_SIGNED 8
#define FLAG_DECIMAL 16
#define FLAG_OCTAL 32
#define FLAG_HEXLO 64
#define FLAG_HEXUP 128
static void _copy_string(wget_buffer_t *buf, unsigned int flags, int field_width, int precision, const char *arg)
{
size_t length;
if (!arg) {
wget_buffer_strcat(buf, "(null)");
return;
}
length = strlen(arg);
// info_printf("flags=0x%02x field_width=%d precision=%d length=%zd arg='%s'\n",
// flags,field_width,precision,length,arg);
if (precision >= 0 && length > (size_t)precision)
length = precision;
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_t *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_t *buf, unsigned int flags, int field_width, int precision, long long arg)
{
unsigned long long argu = 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_t *buf, void *pointer)
{
static const char HEX[16] = "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);
}
size_t wget_buffer_vprintf_append(wget_buffer_t *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;
for (;*p;) {
// collect plain char sequence
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)
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;
}
// read field width (optional)
if (*p == '*') {
field_width = va_arg(args, int);
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');
}
// read precision (optional)
if (*p == '.') {
if (*++p == '*') {
precision = va_arg(args, int);
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;
}
} 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 = (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;
}
// info_printf("*p = %c arg = %lld\n", *p, 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, argu);
else if (*p == 'x')
_convert_dec(buf, flags | FLAG_HEXLO, field_width, precision, argu);
else if (*p == 'X')
_convert_dec(buf, flags | FLAG_HEXUP, field_width, precision, argu);
else if (*p == 'o')
_convert_dec(buf, flags | FLAG_OCTAL, field_width, precision, argu);
else {
// err_printf("Internal error: Unknown conversion specifier '%c'\n", *p);
wget_buffer_memset_append(buf, '%', 1);
p = begin + 1;
continue;
}
p++;
}
return buf->length;
}
size_t wget_buffer_vprintf(wget_buffer_t *buf, const char *fmt, va_list args)
{
buf->length = 0;
return wget_buffer_vprintf_append(buf, fmt, args);
}
size_t wget_buffer_printf_append(wget_buffer_t *buf, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
wget_buffer_vprintf_append(buf, fmt, args);
va_end(args);
return buf->length;
}
size_t wget_buffer_printf(wget_buffer_t *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;
}