gstreamer/gst/printf/printf-parse.c
Tim-Philipp Müller e8a9f7acdf printf: fix handling of old printf extension specifiers for ABI compatibility
Fixes abort when the old specifiers are used. Fix up the conversion
specifier, it would get overwritten with 'c' below to the extension
format char, which then later is unhandled, leading to the abort.
Also fix up and enable unit test for this.

https://bugzilla.gnome.org/process_bug.cgi
2013-04-18 00:46:58 +01:00

478 lines
14 KiB
C

/* Formatted output to strings.
Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, or (at your option)
any later version.
This program 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "gst-printf.h"
/* Specification. */
#include "printf-parse.h"
/* Get size_t, NULL. */
#include <stddef.h>
/* Get intmax_t. */
#ifdef HAVE_STDINT_H_WITH_UINTMAX
# include <stdint.h>
#endif
#ifdef HAVE_INTTYPES_H_WITH_UINTMAX
# include <inttypes.h>
#endif
/* malloc(), realloc(), free(). */
#include <stdlib.h>
#ifdef STATIC
STATIC
#endif
int
printf_parse (const char *format, char_directives * d, arguments * a)
{
const char *cp = format; /* pointer into format */
int arg_posn = 0; /* number of regular arguments consumed */
unsigned int d_allocated; /* allocated elements of d->dir */
unsigned int a_allocated; /* allocated elements of a->arg */
unsigned int max_width_length = 0;
unsigned int max_precision_length = 0;
d->count = 0;
d_allocated = 1;
d->dir = malloc (d_allocated * sizeof (char_directive));
if (d->dir == NULL)
/* Out of memory. */
return -1;
a->count = 0;
a_allocated = 0;
a->arg = NULL;
#define REGISTER_ARG(_index_,_type_) \
{ \
unsigned int n = (_index_); \
if (n >= a_allocated) \
{ \
argument *memory; \
a_allocated = 2 * a_allocated; \
if (a_allocated <= n) \
a_allocated = n + 1; \
memory = (a->arg \
? realloc (a->arg, a_allocated * sizeof (argument)) \
: malloc (a_allocated * sizeof (argument))); \
if (memory == NULL) \
/* Out of memory. */ \
goto error; \
a->arg = memory; \
} \
while (a->count <= n) { \
a->arg[a->count].type = TYPE_NONE; \
a->arg[a->count].ext_string = (char *) 0; \
++a->count; \
} \
if (a->arg[n].type == TYPE_NONE) \
a->arg[n].type = (_type_); \
else if (a->arg[n].type != (_type_)) \
/* Ambiguous type for positional argument. */ \
goto error; \
}
while (*cp != '\0') {
char c = *cp++;
if (c == '%') {
int arg_index = -1;
char_directive *dp = &d->dir[d->count]; /* pointer to next directive */
/* Initialize the next directive. */
dp->dir_start = cp - 1;
dp->flags = 0;
dp->width_start = NULL;
dp->width_end = NULL;
dp->width_arg_index = -1;
dp->precision_start = NULL;
dp->precision_end = NULL;
dp->precision_arg_index = -1;
dp->arg_index = -1;
/* Test for positional argument. */
if (*cp >= '0' && *cp <= '9') {
const char *np;
for (np = cp; *np >= '0' && *np <= '9'; np++);
if (*np == '$') {
unsigned int n = 0;
for (np = cp; *np >= '0' && *np <= '9'; np++)
n = 10 * n + (*np - '0');
if (n == 0)
/* Positional argument 0. */
goto error;
arg_index = n - 1;
cp = np + 1;
}
}
/* Read the flags. */
for (;;) {
if (*cp == '\'') {
dp->flags |= FLAG_GROUP;
cp++;
} else if (*cp == '-') {
dp->flags |= FLAG_LEFT;
cp++;
} else if (*cp == '+') {
dp->flags |= FLAG_SHOWSIGN;
cp++;
} else if (*cp == ' ') {
dp->flags |= FLAG_SPACE;
cp++;
} else if (*cp == '#') {
dp->flags |= FLAG_ALT;
cp++;
} else if (*cp == '0') {
dp->flags |= FLAG_ZERO;
cp++;
} else
break;
}
/* Parse the field width. */
if (*cp == '*') {
dp->width_start = cp;
cp++;
dp->width_end = cp;
if (max_width_length < 1)
max_width_length = 1;
/* Test for positional argument. */
if (*cp >= '0' && *cp <= '9') {
const char *np;
for (np = cp; *np >= '0' && *np <= '9'; np++);
if (*np == '$') {
unsigned int n = 0;
for (np = cp; *np >= '0' && *np <= '9'; np++)
n = 10 * n + (*np - '0');
if (n == 0)
/* Positional argument 0. */
goto error;
dp->width_arg_index = n - 1;
cp = np + 1;
}
}
if (dp->width_arg_index < 0)
dp->width_arg_index = arg_posn++;
REGISTER_ARG (dp->width_arg_index, TYPE_INT);
} else if (*cp >= '0' && *cp <= '9') {
unsigned int width_length;
dp->width_start = cp;
for (; *cp >= '0' && *cp <= '9'; cp++);
dp->width_end = cp;
width_length = dp->width_end - dp->width_start;
if (max_width_length < width_length)
max_width_length = width_length;
}
/* Parse the precision. */
if (*cp == '.') {
cp++;
if (*cp == '*') {
dp->precision_start = cp - 1;
cp++;
dp->precision_end = cp;
if (max_precision_length < 2)
max_precision_length = 2;
/* Test for positional argument. */
if (*cp >= '0' && *cp <= '9') {
const char *np;
for (np = cp; *np >= '0' && *np <= '9'; np++);
if (*np == '$') {
unsigned int n = 0;
for (np = cp; *np >= '0' && *np <= '9'; np++)
n = 10 * n + (*np - '0');
if (n == 0)
/* Positional argument 0. */
goto error;
dp->precision_arg_index = n - 1;
cp = np + 1;
}
}
if (dp->precision_arg_index < 0)
dp->precision_arg_index = arg_posn++;
REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
} else {
unsigned int precision_length;
dp->precision_start = cp - 1;
for (; *cp >= '0' && *cp <= '9'; cp++);
dp->precision_end = cp;
precision_length = dp->precision_end - dp->precision_start;
if (max_precision_length < precision_length)
max_precision_length = precision_length;
}
}
{
arg_type type;
/* Parse argument type/size specifiers. */
{
int flags = 0;
for (;;) {
if (*cp == 'h') {
flags |= (1 << (flags & 1));
cp++;
} else if (*cp == 'L') {
flags |= 4;
cp++;
} else if (*cp == 'l') {
flags += 8;
cp++;
}
#ifdef HAVE_INT64_AND_I64
else if (cp[0] == 'I' && cp[1] == '6' && cp[2] == '4') {
flags = 64;
cp += 3;
}
#endif
#ifdef HAVE_INTMAX_T
else if (*cp == 'j') {
if (sizeof (intmax_t) > sizeof (long)) {
/* intmax_t = long long */
flags += 16;
} else if (sizeof (intmax_t) > sizeof (int)) {
/* intmax_t = long */
flags += 8;
}
cp++;
}
#endif
else if (*cp == 'z' || *cp == 'Z') {
/* 'z' is standardized in ISO C 99, but glibc uses 'Z'
because the warning facility in gcc-2.95.2 understands
only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */
if (sizeof (size_t) > sizeof (long)) {
/* size_t = long long */
flags += 16;
} else if (sizeof (size_t) > sizeof (int)) {
/* size_t = long */
flags += 8;
}
cp++;
} else if (*cp == 't') {
if (sizeof (ptrdiff_t) > sizeof (long)) {
/* ptrdiff_t = long long */
flags += 16;
} else if (sizeof (ptrdiff_t) > sizeof (int)) {
/* ptrdiff_t = long */
flags += 8;
}
cp++;
} else
break;
}
/* Read the conversion character. */
c = *cp++;
switch (c) {
case 'd':
case 'i':
#ifdef HAVE_INT64_AND_I64
if (flags == 64)
type = TYPE_INT64;
else
#endif
#ifdef HAVE_LONG_LONG
if (flags >= 16 || (flags & 4))
type = TYPE_LONGLONGINT;
else
#endif
if (flags >= 8)
type = TYPE_LONGINT;
else if (flags & 2)
type = TYPE_SCHAR;
else if (flags & 1)
type = TYPE_SHORT;
else
type = TYPE_INT;
break;
case 'o':
case 'u':
case 'x':
case 'X':
#ifdef HAVE_INT64_AND_I64
if (flags == 64)
type = TYPE_UINT64;
else
#endif
#ifdef HAVE_LONG_LONG
if (flags >= 16 || (flags & 4))
type = TYPE_ULONGLONGINT;
else
#endif
if (flags >= 8)
type = TYPE_ULONGINT;
else if (flags & 2)
type = TYPE_UCHAR;
else if (flags & 1)
type = TYPE_USHORT;
else
type = TYPE_UINT;
break;
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
case 'a':
case 'A':
#ifdef HAVE_LONG_DOUBLE
if (flags >= 16 || (flags & 4))
type = TYPE_LONGDOUBLE;
else
#endif
type = TYPE_DOUBLE;
break;
case 'c':
if (flags >= 8)
#ifdef HAVE_WINT_T
type = TYPE_WIDE_CHAR;
#else
goto error;
#endif
else
type = TYPE_CHAR;
break;
#ifdef HAVE_WINT_T
case 'C':
type = TYPE_WIDE_CHAR;
c = 'c';
break;
#endif
case 's':
if (flags >= 8)
#ifdef HAVE_WCHAR_T
type = TYPE_WIDE_STRING;
#else
goto error;
#endif
else
type = TYPE_STRING;
break;
#ifdef HAVE_WCHAR_T
case 'S':
type = TYPE_WIDE_STRING;
c = 's';
break;
#endif
/* Old GST_PTR_FORMAT, handle for binary backwards compatibility */
case 'P':
type = TYPE_POINTER_EXT;
dp->flags |= FLAG_PTR_EXT;
dp->ptr_ext_char = 'A';
c = 'p';
break;
case 'p':
/* Note: cp points already to the char after the 'p' now */
if (cp[0] == POINTER_EXT_SIGNIFIER_CHAR && cp[1] != '\0') {
type = TYPE_POINTER_EXT;
dp->flags |= FLAG_PTR_EXT;
dp->ptr_ext_char = cp[1];
cp += 2;
/* we do not use dp->conversion='s' on purpose here, so we
* can fall back to printing just the pointer with %p if the
* serialisation function returned NULL for some reason */
} else {
type = TYPE_POINTER;
}
break;
/* Old GST_SEGMENT_FORMAT, handle for backwards compatibility */
case 'Q':
type = TYPE_POINTER_EXT;
dp->flags |= FLAG_PTR_EXT;
dp->ptr_ext_char = 'B';
c = 'p';
break;
case 'n':
#ifdef HAVE_LONG_LONG
if (flags >= 16 || (flags & 4))
type = TYPE_COUNT_LONGLONGINT_POINTER;
else
#endif
if (flags >= 8)
type = TYPE_COUNT_LONGINT_POINTER;
else if (flags & 2)
type = TYPE_COUNT_SCHAR_POINTER;
else if (flags & 1)
type = TYPE_COUNT_SHORT_POINTER;
else
type = TYPE_COUNT_INT_POINTER;
break;
case '%':
type = TYPE_NONE;
break;
default:
/* Unknown conversion character. */
goto error;
}
}
if (type != TYPE_NONE) {
dp->arg_index = arg_index;
if (dp->arg_index < 0)
dp->arg_index = arg_posn++;
REGISTER_ARG (dp->arg_index, type);
}
dp->conversion = c;
dp->dir_end = cp;
}
d->count++;
if (d->count >= d_allocated) {
char_directive *memory;
d_allocated = 2 * d_allocated;
memory = realloc (d->dir, d_allocated * sizeof (char_directive));
if (memory == NULL)
/* Out of memory. */
goto error;
d->dir = memory;
}
}
}
d->dir[d->count].dir_start = cp;
d->max_width_length = max_width_length;
d->max_precision_length = max_precision_length;
return 0;
error:
if (a->arg)
free (a->arg);
if (d->dir)
free (d->dir);
return -1;
}