Implement a custom print formatter (archive_string_vsprintf)
for libarchive error messages. Mostly, this avoids a portability headache related to copying va_list arguments (some FreeBSD 5 platforms require va_copy; FreeBSD 4 doesn't support va_copy at all). It also dramatically reduces the size of libarchive for embedded applications: a minimal "untar" program using libarchive can now be under 64k statically linked (as opposed to ~100k using library *printf() functions). MFC after: 14 days
This commit is contained in:
@@ -105,6 +105,12 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* FreeBSD 4 and earlier lack intmax_t/uintmax_t */
|
||||||
|
#if defined(__FreeBSD__) && __FreeBSD__ < 5
|
||||||
|
#define intmax_t int64_t
|
||||||
|
#define uintmax_t uint64_t
|
||||||
|
#endif
|
||||||
|
|
||||||
/* TODO: Test for the functions we use as well... */
|
/* TODO: Test for the functions we use as well... */
|
||||||
#if HAVE_SYS_ACL_H
|
#if HAVE_SYS_ACL_H
|
||||||
#define HAVE_POSIX_ACLS 1
|
#define HAVE_POSIX_ACLS 1
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ __archive_strappend_char_UTF8(struct archive_string *, int);
|
|||||||
/* Append an integer in the specified base (2 <= base <= 16). */
|
/* Append an integer in the specified base (2 <= base <= 16). */
|
||||||
struct archive_string *
|
struct archive_string *
|
||||||
__archive_strappend_int(struct archive_string *as, int d, int base);
|
__archive_strappend_int(struct archive_string *as, int d, int base);
|
||||||
|
#define archive_strappend_int __archive_strappend_int
|
||||||
|
|
||||||
/* Basic append operation. */
|
/* Basic append operation. */
|
||||||
struct archive_string *
|
struct archive_string *
|
||||||
|
|||||||
@@ -28,10 +28,16 @@
|
|||||||
__FBSDID("$FreeBSD$");
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This uses 'printf' family functions, which can cause issues
|
* The use of printf()-family functions can be troublesome
|
||||||
* for size-critical applications. I've separated it out to make
|
* for space-constrained applications. In addition, correctly
|
||||||
* this issue clear. (Currently, it is called directly from within
|
* implementing this function in terms of vsnprintf() requires
|
||||||
* the core code, so it cannot easily be omitted.)
|
* two calls (one to determine the size, another to format the
|
||||||
|
* result), which in turn requires duplicating the argument list
|
||||||
|
* using va_copy, which isn't yet universally available.
|
||||||
|
*
|
||||||
|
* So, I've implemented a bare minimum of printf()-like capability
|
||||||
|
* here. This is only used to format error messages, so doesn't
|
||||||
|
* require any floating-point support or field-width handling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -46,21 +52,78 @@ void
|
|||||||
__archive_string_vsprintf(struct archive_string *as, const char *fmt,
|
__archive_string_vsprintf(struct archive_string *as, const char *fmt,
|
||||||
va_list ap)
|
va_list ap)
|
||||||
{
|
{
|
||||||
size_t l;
|
char long_flag;
|
||||||
va_list ap1;
|
intmax_t s; /* Signed integer temp. */
|
||||||
|
uintmax_t u; /* Unsigned integer temp. */
|
||||||
|
const char *p, *p2;
|
||||||
|
|
||||||
|
__archive_string_ensure(as, 64);
|
||||||
|
|
||||||
if (fmt == NULL) {
|
if (fmt == NULL) {
|
||||||
as->s[0] = 0;
|
as->s[0] = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
va_copy(ap1, ap);
|
long_flag = '\0';
|
||||||
l = vsnprintf(as->s, as->buffer_length, fmt, ap);
|
for (p = fmt; *p != '\0'; p++) {
|
||||||
/* If output is bigger than the buffer, resize and try again. */
|
const char *saved_p = p;
|
||||||
if (l+1 >= as->buffer_length) {
|
|
||||||
__archive_string_ensure(as, l + 1);
|
if (*p != '%') {
|
||||||
l = vsnprintf(as->s, as->buffer_length, fmt, ap1);
|
archive_strappend_char(as, *p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
p++;
|
||||||
|
|
||||||
|
switch(*p) {
|
||||||
|
case 'j':
|
||||||
|
long_flag = 'j';
|
||||||
|
p++;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
long_flag = 'l';
|
||||||
|
p++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*p) {
|
||||||
|
case '%':
|
||||||
|
__archive_strappend_char(as, '%');
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
s = va_arg(ap, int);
|
||||||
|
__archive_strappend_char(as, s);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
switch(long_flag) {
|
||||||
|
case 'j': s = va_arg(ap, intmax_t); break;
|
||||||
|
case 'l': s = va_arg(ap, long); break;
|
||||||
|
default: s = va_arg(ap, int); break;
|
||||||
|
}
|
||||||
|
archive_strappend_int(as, s, 10);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
p2 = va_arg(ap, char *);
|
||||||
|
archive_strcat(as, p2);
|
||||||
|
break;
|
||||||
|
case 'o': case 'u': case 'x': case 'X':
|
||||||
|
/* Common handling for unsigned integer formats. */
|
||||||
|
switch(long_flag) {
|
||||||
|
case 'j': u = va_arg(ap, uintmax_t); break;
|
||||||
|
case 'l': u = va_arg(ap, unsigned long); break;
|
||||||
|
default: u = va_arg(ap, unsigned int); break;
|
||||||
|
}
|
||||||
|
/* Format it in the correct base. */
|
||||||
|
switch (*p) {
|
||||||
|
case 'o': archive_strappend_int(as, u, 8); break;
|
||||||
|
case 'u': archive_strappend_int(as, u, 10); break;
|
||||||
|
default: archive_strappend_int(as, u, 16); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Rewind and print the initial '%' literally. */
|
||||||
|
p = saved_p;
|
||||||
|
archive_strappend_char(as, *p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
as->length = l;
|
|
||||||
va_end(ap1);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ AC_CHECK_TYPE(__int64_t, [long long])
|
|||||||
AC_CHECK_TYPE(_int64_t, [__int64_t])
|
AC_CHECK_TYPE(_int64_t, [__int64_t])
|
||||||
AC_CHECK_TYPE(int64_t, [_int64_t])
|
AC_CHECK_TYPE(int64_t, [_int64_t])
|
||||||
|
|
||||||
|
# Likewise, consider defining "intmax_t" in terms of int64_t
|
||||||
|
AC_CHECK_TYPE(intmax_t, [int64_t])
|
||||||
|
|
||||||
#
|
#
|
||||||
# If any of the common 64-bit unsigned types is defined, set "uint64_t"
|
# If any of the common 64-bit unsigned types is defined, set "uint64_t"
|
||||||
#
|
#
|
||||||
@@ -44,6 +47,9 @@ AC_CHECK_TYPE(_uint64_t, [__uint64_t])
|
|||||||
AC_CHECK_TYPE(u_int64_t, [_uint64_t])
|
AC_CHECK_TYPE(u_int64_t, [_uint64_t])
|
||||||
AC_CHECK_TYPE(uint64_t, [u_int64_t])
|
AC_CHECK_TYPE(uint64_t, [u_int64_t])
|
||||||
|
|
||||||
|
# Likewise, consider defining "uintmax_t" in terms of uint64_t
|
||||||
|
AC_CHECK_TYPE(uintmax_t, [uint64_t])
|
||||||
|
|
||||||
AC_CHECK_MEMBERS([struct stat.st_rdev, struct stat.st_mtimespec.tv_nsec, struct stat.st_mtim.tv_nsec])
|
AC_CHECK_MEMBERS([struct stat.st_rdev, struct stat.st_mtimespec.tv_nsec, struct stat.st_mtim.tv_nsec])
|
||||||
|
|
||||||
AC_CHECK_DECL([EFTYPE],
|
AC_CHECK_DECL([EFTYPE],
|
||||||
|
|||||||
Reference in New Issue
Block a user