summaryrefslogtreecommitdiff
path: root/stdio/printf-prs.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdio/printf-prs.c')
-rw-r--r--stdio/printf-prs.c211
1 files changed, 211 insertions, 0 deletions
diff --git a/stdio/printf-prs.c b/stdio/printf-prs.c
new file mode 100644
index 0000000000..2f55dd3157
--- /dev/null
+++ b/stdio/printf-prs.c
@@ -0,0 +1,211 @@
+/* Copyright (C) 1991, 1992, 1995 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library 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 of the
+License, or (at your option) any later version.
+
+The GNU C Library 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 the GNU C Library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA. */
+
+#include <ansidecl.h>
+#include <stdio.h>
+#include <printf.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef __GNUC__
+#define HAVE_LONGLONG
+#endif
+
+extern printf_arginfo_function *__printf_arginfo_table[];
+
+size_t
+DEFUN(parse_printf_format, (fmt, n, argtypes),
+ CONST char *fmt AND size_t n AND int *argtypes)
+{
+ register CONST char *f;
+ size_t need = 0;
+
+ for (f = strchr (fmt, '%'); f != NULL; f = strchr (f, '%'))
+ {
+ struct printf_info info;
+ printf_arginfo_function *arginfo;
+
+ ++f;
+
+ info.space = info.showsign = info.left = info.alt = info.group = 0;
+ info.pad = ' ';
+ while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0' ||
+ *f == '\'')
+ switch (*f++)
+ {
+ case ' ':
+ info.space = 1;
+ break;
+ case '+':
+ info.showsign = 1;
+ break;
+ case '-':
+ info.left = 1;
+ break;
+ case '#':
+ info.alt = 1;
+ break;
+ case '\'':
+ info.group = 1;
+ break;
+ case '0':
+ info.pad = '0';
+ break;
+ }
+ if (info.left)
+ info.pad = ' ';
+
+ /* Get the field width. */
+ if (*f == '*')
+ {
+ if (++need < n)
+ *argtypes++ = PA_INT;
+ info.width = INT_MIN;
+ ++f;
+ }
+ else
+ {
+ info.width = 0;
+ while (isdigit(*f))
+ {
+ info.width *= 10;
+ info.width += *f++ - '0';
+ }
+ }
+
+ /* Get the precision. */
+ /* -1 means none given; 0 means explicit 0. */
+ info.prec = -1;
+ if (*f == '.')
+ {
+ ++f;
+ if (*f == '*')
+ {
+ /* The precision is given in an argument. */
+ if (++need < n)
+ *argtypes++ = PA_INT;
+ info.prec = INT_MIN;
+ ++f;
+ }
+ else if (isdigit(*f))
+ {
+ info.prec = 0;
+ while (*f != '\0' && isdigit(*f))
+ {
+ info.prec *= 10;
+ info.prec += *f++ - '0';
+ }
+ }
+ }
+
+ /* Check for type modifiers. */
+ info.is_short = info.is_long = info.is_long_double = 0;
+ while (*f == 'h' || *f == 'l' || *f == 'L')
+ switch (*f++)
+ {
+ case 'h':
+ /* int's are short int's. */
+ info.is_short = 1;
+ break;
+ case 'l':
+#ifdef HAVE_LONGLONG
+ if (info.is_long)
+ /* A double `l' is equivalent to an `L'. */
+ info.is_long_double = 1;
+ else
+#endif
+ /* int's are long int's. */
+ info.is_long = 1;
+ break;
+ case 'L':
+ /* double's are long double's, and int's are long long int's. */
+ info.is_long_double = 1;
+ break;
+ }
+
+ if (*f == '\0')
+ return need;
+
+ info.spec = *f++;
+
+ arginfo = __printf_arginfo_table[info.spec];
+ if (arginfo != NULL)
+ {
+ size_t nargs
+ = (*arginfo) (&info, need > n ? 0 : n - need, argtypes);
+ need += nargs;
+ argtypes += nargs;
+ }
+ else
+ {
+ int type;
+ switch (info.spec)
+ {
+ case 'i':
+ case 'd':
+ case 'u':
+ case 'o':
+ case 'X':
+ case 'x':
+ type = PA_INT;
+ break;
+
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ type = PA_DOUBLE;
+ break;
+
+ case 'c':
+ type = PA_CHAR;
+ break;
+
+ case 's':
+ type = PA_STRING;
+ break;
+
+ case 'p':
+ type = PA_POINTER;
+ break;
+
+ case 'n':
+ type = PA_INT | PA_FLAG_PTR;
+ break;
+
+ default:
+ /* No arg for an unknown spec. */
+ continue;
+ }
+
+ if (info.is_long_double)
+ type |= PA_FLAG_LONG_DOUBLE;
+ if (info.is_long)
+ type |= PA_FLAG_LONG;
+ if (info.is_short)
+ type |= PA_FLAG_SHORT;
+
+ if (++need < n)
+ *argtypes++ = type;
+ }
+ }
+
+ return need;
+}