2014-12-11 08:15:37 +00:00
|
|
|
/* Estimate the length of the string generated by a vprintf-like
|
|
|
|
function. Used by vasprintf and xvasprintf.
|
2024-01-03 11:19:35 +00:00
|
|
|
Copyright (C) 1994-2024 Free Software Foundation, Inc.
|
2014-12-11 08:15:37 +00:00
|
|
|
|
|
|
|
This file is part of the libiberty library.
|
|
|
|
Libiberty 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.
|
|
|
|
|
|
|
|
Libiberty 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 libiberty; see the file COPYING.LIB. If not, write
|
|
|
|
to the Free Software Foundation, Inc., 51 Franklin Street - Fifth
|
|
|
|
Floor, Boston, MA 02110-1301, USA. */
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
#include <ansidecl.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#if !defined (va_copy) && defined (__va_copy)
|
|
|
|
# define va_copy(d,s) __va_copy((d),(s))
|
|
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
#include <string.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
|
|
#include <stdlib.h>
|
|
|
|
#else
|
|
|
|
extern unsigned long strtoul ();
|
|
|
|
#endif
|
|
|
|
#include "libiberty.h"
|
|
|
|
|
|
|
|
int
|
|
|
|
libiberty_vprintf_buffer_size (const char *format, va_list args)
|
|
|
|
{
|
|
|
|
const char *p = format;
|
|
|
|
/* Add one to make sure that it is never zero, which might cause malloc
|
|
|
|
to return NULL. */
|
|
|
|
int total_width = strlen (format) + 1;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
#ifdef va_copy
|
|
|
|
va_copy (ap, args);
|
|
|
|
#else
|
2022-05-10 14:00:53 +00:00
|
|
|
memcpy ((void *) &ap, (void *) &args, sizeof (va_list));
|
2014-12-11 08:15:37 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
while (*p != '\0')
|
|
|
|
{
|
|
|
|
if (*p++ == '%')
|
|
|
|
{
|
libiberty: Fix up libiberty_vprintf_buffer_size
When writing the HOST_SIZE_T_PRINT_UNSIGNED incremental patch,
my first bootstrap failed on i686-linux. That is because I've also had
@@ -1344,8 +1344,10 @@ adjust_field_rtx_def (type_p t, options_
}
subfields = create_field (subfields, t,
- xasprintf (".fld[%lu].%s",
- (unsigned long) aindex,
+ xasprintf (".fld["
+ HOST_SIZE_T_PRINT_UNSIGNED
+ "].%s",
+ (fmt_size_t) aindex,
subname));
subfields->opt = nodot;
if (t == note_union_tp)
hunk in gengtype.cc. While sprintf obviously can print in this case %llu
with fmt_size_t being unsigned long long (that is another bug I'll fix
incrementally), seems libiberty_vprintf_buffer_size
can't deal with that, it ignores h, hh, l, ll and L modifiers and
unconditionally, estimates 30 chars as upper bounds for integers (that is
fine) and then uses (void) va_arg (ap, int); to skip over the argument
regardless if it was %d, %ld, %lld, %hd, %hhd etc.
Now, on x86_64 that happens to work fine probably for all of those,
on ia32 for everything but %lld, because it then skips just one half
of the long long argument; now as there is %s after it, it will try to
compute strlen not from the pointer argument corresponding to %s, but
from the most significant half of the previous long long argument.
So, the following patch attempts not to completely ignore the modifiers,
but figure out from them whether to va_arg an int (used for h and hh as
well), or long, or long long, or size_t, or ptrdiff_t - added support for
z and t there, plus for Windows I64. And also %Lf etc. for long double.
2024-02-12 Jakub Jelinek <jakub@redhat.com>
* vprintf-support.c (libiberty_vprintf_buffer_size): Handle
properly l, ll, z, t or on _WIN32 I64 modifiers for diouxX
and L modifier for fFgGeE.
2024-02-12 17:50:16 +00:00
|
|
|
int prec = 0;
|
2014-12-11 08:15:37 +00:00
|
|
|
while (strchr ("-+ #0", *p))
|
|
|
|
++p;
|
|
|
|
if (*p == '*')
|
|
|
|
{
|
|
|
|
++p;
|
|
|
|
total_width += abs (va_arg (ap, int));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
total_width += strtoul (p, (char **) &p, 10);
|
|
|
|
if (*p == '.')
|
|
|
|
{
|
|
|
|
++p;
|
|
|
|
if (*p == '*')
|
|
|
|
{
|
|
|
|
++p;
|
|
|
|
total_width += abs (va_arg (ap, int));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
total_width += strtoul (p, (char **) &p, 10);
|
|
|
|
}
|
libiberty: Fix up libiberty_vprintf_buffer_size
When writing the HOST_SIZE_T_PRINT_UNSIGNED incremental patch,
my first bootstrap failed on i686-linux. That is because I've also had
@@ -1344,8 +1344,10 @@ adjust_field_rtx_def (type_p t, options_
}
subfields = create_field (subfields, t,
- xasprintf (".fld[%lu].%s",
- (unsigned long) aindex,
+ xasprintf (".fld["
+ HOST_SIZE_T_PRINT_UNSIGNED
+ "].%s",
+ (fmt_size_t) aindex,
subname));
subfields->opt = nodot;
if (t == note_union_tp)
hunk in gengtype.cc. While sprintf obviously can print in this case %llu
with fmt_size_t being unsigned long long (that is another bug I'll fix
incrementally), seems libiberty_vprintf_buffer_size
can't deal with that, it ignores h, hh, l, ll and L modifiers and
unconditionally, estimates 30 chars as upper bounds for integers (that is
fine) and then uses (void) va_arg (ap, int); to skip over the argument
regardless if it was %d, %ld, %lld, %hd, %hhd etc.
Now, on x86_64 that happens to work fine probably for all of those,
on ia32 for everything but %lld, because it then skips just one half
of the long long argument; now as there is %s after it, it will try to
compute strlen not from the pointer argument corresponding to %s, but
from the most significant half of the previous long long argument.
So, the following patch attempts not to completely ignore the modifiers,
but figure out from them whether to va_arg an int (used for h and hh as
well), or long, or long long, or size_t, or ptrdiff_t - added support for
z and t there, plus for Windows I64. And also %Lf etc. for long double.
2024-02-12 Jakub Jelinek <jakub@redhat.com>
* vprintf-support.c (libiberty_vprintf_buffer_size): Handle
properly l, ll, z, t or on _WIN32 I64 modifiers for diouxX
and L modifier for fFgGeE.
2024-02-12 17:50:16 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
switch (*p)
|
|
|
|
{
|
|
|
|
case 'h':
|
|
|
|
++p;
|
|
|
|
continue;
|
|
|
|
case 'l':
|
|
|
|
case 'L':
|
|
|
|
++prec;
|
|
|
|
++p;
|
|
|
|
continue;
|
|
|
|
case 'z':
|
|
|
|
prec = 3;
|
|
|
|
++p;
|
|
|
|
continue;
|
|
|
|
case 't':
|
|
|
|
prec = 4;
|
|
|
|
++p;
|
|
|
|
continue;
|
|
|
|
#ifdef _WIN32
|
|
|
|
case 'I':
|
|
|
|
if (p[1] == '6' && p[2] == '4')
|
|
|
|
{
|
|
|
|
prec = 2;
|
|
|
|
p += 3;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
while (1);
|
|
|
|
|
2014-12-11 08:15:37 +00:00
|
|
|
/* Should be big enough for any format specifier except %s and floats. */
|
|
|
|
total_width += 30;
|
|
|
|
switch (*p)
|
|
|
|
{
|
|
|
|
case 'd':
|
|
|
|
case 'i':
|
|
|
|
case 'o':
|
|
|
|
case 'u':
|
|
|
|
case 'x':
|
|
|
|
case 'X':
|
libiberty: Fix up libiberty_vprintf_buffer_size
When writing the HOST_SIZE_T_PRINT_UNSIGNED incremental patch,
my first bootstrap failed on i686-linux. That is because I've also had
@@ -1344,8 +1344,10 @@ adjust_field_rtx_def (type_p t, options_
}
subfields = create_field (subfields, t,
- xasprintf (".fld[%lu].%s",
- (unsigned long) aindex,
+ xasprintf (".fld["
+ HOST_SIZE_T_PRINT_UNSIGNED
+ "].%s",
+ (fmt_size_t) aindex,
subname));
subfields->opt = nodot;
if (t == note_union_tp)
hunk in gengtype.cc. While sprintf obviously can print in this case %llu
with fmt_size_t being unsigned long long (that is another bug I'll fix
incrementally), seems libiberty_vprintf_buffer_size
can't deal with that, it ignores h, hh, l, ll and L modifiers and
unconditionally, estimates 30 chars as upper bounds for integers (that is
fine) and then uses (void) va_arg (ap, int); to skip over the argument
regardless if it was %d, %ld, %lld, %hd, %hhd etc.
Now, on x86_64 that happens to work fine probably for all of those,
on ia32 for everything but %lld, because it then skips just one half
of the long long argument; now as there is %s after it, it will try to
compute strlen not from the pointer argument corresponding to %s, but
from the most significant half of the previous long long argument.
So, the following patch attempts not to completely ignore the modifiers,
but figure out from them whether to va_arg an int (used for h and hh as
well), or long, or long long, or size_t, or ptrdiff_t - added support for
z and t there, plus for Windows I64. And also %Lf etc. for long double.
2024-02-12 Jakub Jelinek <jakub@redhat.com>
* vprintf-support.c (libiberty_vprintf_buffer_size): Handle
properly l, ll, z, t or on _WIN32 I64 modifiers for diouxX
and L modifier for fFgGeE.
2024-02-12 17:50:16 +00:00
|
|
|
switch (prec)
|
|
|
|
{
|
|
|
|
case 0: (void) va_arg (ap, int); break;
|
|
|
|
case 1: (void) va_arg (ap, long int); break;
|
|
|
|
case 2: (void) va_arg (ap, long long int); break;
|
|
|
|
case 3: (void) va_arg (ap, size_t); break;
|
|
|
|
case 4: (void) va_arg (ap, ptrdiff_t); break;
|
|
|
|
}
|
|
|
|
break;
|
2014-12-11 08:15:37 +00:00
|
|
|
case 'c':
|
|
|
|
(void) va_arg (ap, int);
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
case 'e':
|
|
|
|
case 'E':
|
|
|
|
case 'g':
|
|
|
|
case 'G':
|
libiberty: Fix up libiberty_vprintf_buffer_size
When writing the HOST_SIZE_T_PRINT_UNSIGNED incremental patch,
my first bootstrap failed on i686-linux. That is because I've also had
@@ -1344,8 +1344,10 @@ adjust_field_rtx_def (type_p t, options_
}
subfields = create_field (subfields, t,
- xasprintf (".fld[%lu].%s",
- (unsigned long) aindex,
+ xasprintf (".fld["
+ HOST_SIZE_T_PRINT_UNSIGNED
+ "].%s",
+ (fmt_size_t) aindex,
subname));
subfields->opt = nodot;
if (t == note_union_tp)
hunk in gengtype.cc. While sprintf obviously can print in this case %llu
with fmt_size_t being unsigned long long (that is another bug I'll fix
incrementally), seems libiberty_vprintf_buffer_size
can't deal with that, it ignores h, hh, l, ll and L modifiers and
unconditionally, estimates 30 chars as upper bounds for integers (that is
fine) and then uses (void) va_arg (ap, int); to skip over the argument
regardless if it was %d, %ld, %lld, %hd, %hhd etc.
Now, on x86_64 that happens to work fine probably for all of those,
on ia32 for everything but %lld, because it then skips just one half
of the long long argument; now as there is %s after it, it will try to
compute strlen not from the pointer argument corresponding to %s, but
from the most significant half of the previous long long argument.
So, the following patch attempts not to completely ignore the modifiers,
but figure out from them whether to va_arg an int (used for h and hh as
well), or long, or long long, or size_t, or ptrdiff_t - added support for
z and t there, plus for Windows I64. And also %Lf etc. for long double.
2024-02-12 Jakub Jelinek <jakub@redhat.com>
* vprintf-support.c (libiberty_vprintf_buffer_size): Handle
properly l, ll, z, t or on _WIN32 I64 modifiers for diouxX
and L modifier for fFgGeE.
2024-02-12 17:50:16 +00:00
|
|
|
if (!prec)
|
|
|
|
{
|
|
|
|
(void) va_arg (ap, double);
|
|
|
|
/* Since an ieee double can have an exponent of 308, we'll
|
|
|
|
make the buffer wide enough to cover the gross case. */
|
|
|
|
total_width += 308;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
(void) va_arg (ap, long double);
|
|
|
|
total_width += 4932;
|
|
|
|
}
|
2014-12-11 08:15:37 +00:00
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
total_width += strlen (va_arg (ap, char *));
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
case 'n':
|
|
|
|
(void) va_arg (ap, char *);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef va_copy
|
|
|
|
va_end (ap);
|
|
|
|
#endif
|
|
|
|
return total_width;
|
|
|
|
}
|