mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 13:18:52 +00:00
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
477 lines
14 KiB
C
477 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;
|
|
}
|