| /* 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 |
| else if (cp[0] == 'I' && cp[1] == '3' && cp[2] == '2') { |
| //flags = 32; |
| cp += 3; |
| } |
| #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; |
| } |