Blame stdio-common/printf-parsemb.c

Packit Service 82fcde
/* Helper functions for parsing printf format strings.
Packit Service 82fcde
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of th GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <ctype.h>
Packit Service 82fcde
#include <limits.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <sys/param.h>
Packit Service 82fcde
#include <wchar.h>
Packit Service 82fcde
#include <wctype.h>
Packit Service 82fcde
Packit Service 82fcde
#ifndef COMPILE_WPRINTF
Packit Service 82fcde
# define CHAR_T		char
Packit Service 82fcde
# define UCHAR_T	unsigned char
Packit Service 82fcde
# define INT_T		int
Packit Service 82fcde
# define L_(Str)	Str
Packit Service 82fcde
# define ISDIGIT(Ch)	isdigit (Ch)
Packit Service 82fcde
# define HANDLE_REGISTERED_MODIFIER __handle_registered_modifier_mb
Packit Service 82fcde
#else
Packit Service 82fcde
# define CHAR_T		wchar_t
Packit Service 82fcde
# define UCHAR_T	unsigned int
Packit Service 82fcde
# define INT_T		wint_t
Packit Service 82fcde
# define L_(Str)	L##Str
Packit Service 82fcde
# define ISDIGIT(Ch)	iswdigit (Ch)
Packit Service 82fcde
# define HANDLE_REGISTERED_MODIFIER __handle_registered_modifier_wc
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
#include "printf-parse.h"
Packit Service 82fcde
Packit Service 82fcde
#define NDEBUG 1
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* FORMAT must point to a '%' at the beginning of a spec.  Fills in *SPEC
Packit Service 82fcde
   with the parsed details.  POSN is the number of arguments already
Packit Service 82fcde
   consumed.  At most MAXTYPES - POSN types are filled in TYPES.  Return
Packit Service 82fcde
   the number of args consumed by this spec; *MAX_REF_ARG is updated so it
Packit Service 82fcde
   remains the highest argument index used.  */
Packit Service 82fcde
size_t
Packit Service 82fcde
attribute_hidden
Packit Service 82fcde
#ifdef COMPILE_WPRINTF
Packit Service 82fcde
__parse_one_specwc (const UCHAR_T *format, size_t posn,
Packit Service 82fcde
		    struct printf_spec *spec, size_t *max_ref_arg)
Packit Service 82fcde
#else
Packit Service 82fcde
__parse_one_specmb (const UCHAR_T *format, size_t posn,
Packit Service 82fcde
		    struct printf_spec *spec, size_t *max_ref_arg)
Packit Service 82fcde
#endif
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned int n;
Packit Service 82fcde
  size_t nargs = 0;
Packit Service 82fcde
Packit Service 82fcde
  /* Skip the '%'.  */
Packit Service 82fcde
  ++format;
Packit Service 82fcde
Packit Service 82fcde
  /* Clear information structure.  */
Packit Service 82fcde
  spec->data_arg = -1;
Packit Service 82fcde
  spec->info.alt = 0;
Packit Service 82fcde
  spec->info.space = 0;
Packit Service 82fcde
  spec->info.left = 0;
Packit Service 82fcde
  spec->info.showsign = 0;
Packit Service 82fcde
  spec->info.group = 0;
Packit Service 82fcde
  spec->info.i18n = 0;
Packit Service 82fcde
  spec->info.extra = 0;
Packit Service 82fcde
  spec->info.pad = ' ';
Packit Service 82fcde
  spec->info.wide = sizeof (UCHAR_T) > 1;
Packit Service 82fcde
  spec->info.is_binary128 = 0;
Packit Service 82fcde
Packit Service 82fcde
  /* Test for positional argument.  */
Packit Service 82fcde
  if (ISDIGIT (*format))
Packit Service 82fcde
    {
Packit Service 82fcde
      const UCHAR_T *begin = format;
Packit Service 82fcde
Packit Service 82fcde
      n = read_int (&format);
Packit Service 82fcde
Packit Service 82fcde
      if (n != 0 && *format == L_('$'))
Packit Service 82fcde
	/* Is positional parameter.  */
Packit Service 82fcde
	{
Packit Service 82fcde
	  ++format;		/* Skip the '$'.  */
Packit Service 82fcde
	  if (n != -1)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      spec->data_arg = n - 1;
Packit Service 82fcde
	      *max_ref_arg = MAX (*max_ref_arg, n);
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	/* Oops; that was actually the width and/or 0 padding flag.
Packit Service 82fcde
	   Step back and read it again.  */
Packit Service 82fcde
	format = begin;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Check for spec modifiers.  */
Packit Service 82fcde
  do
Packit Service 82fcde
    {
Packit Service 82fcde
      switch (*format)
Packit Service 82fcde
	{
Packit Service 82fcde
	case L_(' '):
Packit Service 82fcde
	  /* Output a space in place of a sign, when there is no sign.  */
Packit Service 82fcde
	  spec->info.space = 1;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	case L_('+'):
Packit Service 82fcde
	  /* Always output + or - for numbers.  */
Packit Service 82fcde
	  spec->info.showsign = 1;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	case L_('-'):
Packit Service 82fcde
	  /* Left-justify things.  */
Packit Service 82fcde
	  spec->info.left = 1;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	case L_('#'):
Packit Service 82fcde
	  /* Use the "alternate form":
Packit Service 82fcde
	     Hex has 0x or 0X, FP always has a decimal point.  */
Packit Service 82fcde
	  spec->info.alt = 1;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	case L_('0'):
Packit Service 82fcde
	  /* Pad with 0s.  */
Packit Service 82fcde
	  spec->info.pad = '0';
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	case L_('\''):
Packit Service 82fcde
	  /* Show grouping in numbers if the locale information
Packit Service 82fcde
	     indicates any.  */
Packit Service 82fcde
	  spec->info.group = 1;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	case L_('I'):
Packit Service 82fcde
	  /* Use the internationalized form of the output.  Currently
Packit Service 82fcde
	     means to use the `outdigits' of the current locale.  */
Packit Service 82fcde
	  spec->info.i18n = 1;
Packit Service 82fcde
	  continue;
Packit Service 82fcde
	default:
Packit Service 82fcde
	  break;
Packit Service 82fcde
	}
Packit Service 82fcde
      break;
Packit Service 82fcde
    }
Packit Service 82fcde
  while (*++format);
Packit Service 82fcde
Packit Service 82fcde
  if (spec->info.left)
Packit Service 82fcde
    spec->info.pad = ' ';
Packit Service 82fcde
Packit Service 82fcde
  /* Get the field width.  */
Packit Service 82fcde
  spec->width_arg = -1;
Packit Service 82fcde
  spec->info.width = 0;
Packit Service 82fcde
  if (*format == L_('*'))
Packit Service 82fcde
    {
Packit Service 82fcde
      /* The field width is given in an argument.
Packit Service 82fcde
	 A negative field width indicates left justification.  */
Packit Service 82fcde
      const UCHAR_T *begin = ++format;
Packit Service 82fcde
Packit Service 82fcde
      if (ISDIGIT (*format))
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* The width argument might be found in a positional parameter.  */
Packit Service 82fcde
	  n = read_int (&format);
Packit Service 82fcde
Packit Service 82fcde
	  if (n != 0 && *format == L_('$'))
Packit Service 82fcde
	    {
Packit Service 82fcde
	      if (n != -1)
Packit Service 82fcde
		{
Packit Service 82fcde
		  spec->width_arg = n - 1;
Packit Service 82fcde
		  *max_ref_arg = MAX (*max_ref_arg, n);
Packit Service 82fcde
		}
Packit Service 82fcde
	      ++format;		/* Skip '$'.  */
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      if (spec->width_arg < 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Not in a positional parameter.  Consume one argument.  */
Packit Service 82fcde
	  spec->width_arg = posn++;
Packit Service 82fcde
	  ++nargs;
Packit Service 82fcde
	  format = begin;	/* Step back and reread.  */
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  else if (ISDIGIT (*format))
Packit Service 82fcde
    {
Packit Service 82fcde
      int n = read_int (&format);
Packit Service 82fcde
Packit Service 82fcde
      /* Constant width specification.  */
Packit Service 82fcde
      if (n != -1)
Packit Service 82fcde
	spec->info.width = n;
Packit Service 82fcde
    }
Packit Service 82fcde
  /* Get the precision.  */
Packit Service 82fcde
  spec->prec_arg = -1;
Packit Service 82fcde
  /* -1 means none given; 0 means explicit 0.  */
Packit Service 82fcde
  spec->info.prec = -1;
Packit Service 82fcde
  if (*format == L_('.'))
Packit Service 82fcde
    {
Packit Service 82fcde
      ++format;
Packit Service 82fcde
      if (*format == L_('*'))
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* The precision is given in an argument.  */
Packit Service 82fcde
	  const UCHAR_T *begin = ++format;
Packit Service 82fcde
Packit Service 82fcde
	  if (ISDIGIT (*format))
Packit Service 82fcde
	    {
Packit Service 82fcde
	      n = read_int (&format);
Packit Service 82fcde
Packit Service 82fcde
	      if (n != 0 && *format == L_('$'))
Packit Service 82fcde
		{
Packit Service 82fcde
		  if (n != -1)
Packit Service 82fcde
		    {
Packit Service 82fcde
		      spec->prec_arg = n - 1;
Packit Service 82fcde
		      *max_ref_arg = MAX (*max_ref_arg, n);
Packit Service 82fcde
		    }
Packit Service 82fcde
		  ++format;
Packit Service 82fcde
		}
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (spec->prec_arg < 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* Not in a positional parameter.  */
Packit Service 82fcde
	      spec->prec_arg = posn++;
Packit Service 82fcde
	      ++nargs;
Packit Service 82fcde
	      format = begin;
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
      else if (ISDIGIT (*format))
Packit Service 82fcde
	{
Packit Service 82fcde
	  int n = read_int (&format);
Packit Service 82fcde
Packit Service 82fcde
	  if (n != -1)
Packit Service 82fcde
	    spec->info.prec = n;
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	/* "%.?" is treated like "%.0?".  */
Packit Service 82fcde
	spec->info.prec = 0;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Check for type modifiers.  */
Packit Service 82fcde
  spec->info.is_long_double = 0;
Packit Service 82fcde
  spec->info.is_short = 0;
Packit Service 82fcde
  spec->info.is_long = 0;
Packit Service 82fcde
  spec->info.is_char = 0;
Packit Service 82fcde
  spec->info.user = 0;
Packit Service 82fcde
Packit Service 82fcde
  if (__builtin_expect (__printf_modifier_table == NULL, 1)
Packit Service 82fcde
      || __printf_modifier_table[*format] == NULL
Packit Service 82fcde
      || HANDLE_REGISTERED_MODIFIER (&format, &spec->info) != 0)
Packit Service 82fcde
    switch (*format++)
Packit Service 82fcde
      {
Packit Service 82fcde
      case L_('h'):
Packit Service 82fcde
	/* ints are short ints or chars.  */
Packit Service 82fcde
	if (*format != L_('h'))
Packit Service 82fcde
	  spec->info.is_short = 1;
Packit Service 82fcde
	else
Packit Service 82fcde
	  {
Packit Service 82fcde
	    ++format;
Packit Service 82fcde
	    spec->info.is_char = 1;
Packit Service 82fcde
	  }
Packit Service 82fcde
	break;
Packit Service 82fcde
      case L_('l'):
Packit Service 82fcde
	/* ints are long ints.  */
Packit Service 82fcde
	spec->info.is_long = 1;
Packit Service 82fcde
	if (*format != L_('l'))
Packit Service 82fcde
	  break;
Packit Service 82fcde
	++format;
Packit Service 82fcde
	/* FALLTHROUGH */
Packit Service 82fcde
      case L_('L'):
Packit Service 82fcde
	/* doubles are long doubles, and ints are long long ints.  */
Packit Service 82fcde
      case L_('q'):
Packit Service 82fcde
	/* 4.4 uses this for long long.  */
Packit Service 82fcde
	spec->info.is_long_double = 1;
Packit Service 82fcde
	break;
Packit Service 82fcde
      case L_('z'):
Packit Service 82fcde
      case L_('Z'):
Packit Service 82fcde
	/* ints are size_ts.  */
Packit Service 82fcde
	assert (sizeof (size_t) <= sizeof (unsigned long long int));
Packit Service 82fcde
#if LONG_MAX != LONG_LONG_MAX
Packit Service 82fcde
	spec->info.is_long_double = (sizeof (size_t)
Packit Service 82fcde
				     > sizeof (unsigned long int));
Packit Service 82fcde
#endif
Packit Service 82fcde
	spec->info.is_long = sizeof (size_t) > sizeof (unsigned int);
Packit Service 82fcde
	break;
Packit Service 82fcde
      case L_('t'):
Packit Service 82fcde
	assert (sizeof (ptrdiff_t) <= sizeof (long long int));
Packit Service 82fcde
#if LONG_MAX != LONG_LONG_MAX
Packit Service 82fcde
	spec->info.is_long_double = (sizeof (ptrdiff_t) > sizeof (long int));
Packit Service 82fcde
#endif
Packit Service 82fcde
	spec->info.is_long = sizeof (ptrdiff_t) > sizeof (int);
Packit Service 82fcde
	break;
Packit Service 82fcde
      case L_('j'):
Packit Service 82fcde
	assert (sizeof (uintmax_t) <= sizeof (unsigned long long int));
Packit Service 82fcde
#if LONG_MAX != LONG_LONG_MAX
Packit Service 82fcde
	spec->info.is_long_double = (sizeof (uintmax_t)
Packit Service 82fcde
				     > sizeof (unsigned long int));
Packit Service 82fcde
#endif
Packit Service 82fcde
	spec->info.is_long = sizeof (uintmax_t) > sizeof (unsigned int);
Packit Service 82fcde
	break;
Packit Service 82fcde
      default:
Packit Service 82fcde
	/* Not a recognized modifier.  Backup.  */
Packit Service 82fcde
	--format;
Packit Service 82fcde
	break;
Packit Service 82fcde
      }
Packit Service 82fcde
Packit Service 82fcde
  /* Get the format specification.  */
Packit Service 82fcde
  spec->info.spec = (wchar_t) *format++;
Packit Service 82fcde
  spec->size = -1;
Packit Service 82fcde
  if (__builtin_expect (__printf_function_table == NULL, 1)
Packit Service 82fcde
      || spec->info.spec > UCHAR_MAX
Packit Service 82fcde
      || __printf_arginfo_table[spec->info.spec] == NULL
Packit Service 82fcde
      /* We don't try to get the types for all arguments if the format
Packit Service 82fcde
	 uses more than one.  The normal case is covered though.  If
Packit Service 82fcde
	 the call returns -1 we continue with the normal specifiers.  */
Packit Service 82fcde
      || (int) (spec->ndata_args = (*__printf_arginfo_table[spec->info.spec])
Packit Service 82fcde
				   (&spec->info, 1, &spec->data_arg_type,
Packit Service 82fcde
				    &spec->size)) < 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Find the data argument types of a built-in spec.  */
Packit Service 82fcde
      spec->ndata_args = 1;
Packit Service 82fcde
Packit Service 82fcde
      switch (spec->info.spec)
Packit Service 82fcde
	{
Packit Service 82fcde
	case L'i':
Packit Service 82fcde
	case L'd':
Packit Service 82fcde
	case L'u':
Packit Service 82fcde
	case L'o':
Packit Service 82fcde
	case L'X':
Packit Service 82fcde
	case L'x':
Packit Service 82fcde
#if LONG_MAX != LONG_LONG_MAX
Packit Service 82fcde
	  if (spec->info.is_long_double)
Packit Service 82fcde
	    spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG;
Packit Service 82fcde
	  else
Packit Service 82fcde
#endif
Packit Service 82fcde
	    if (spec->info.is_long)
Packit Service 82fcde
	      spec->data_arg_type = PA_INT|PA_FLAG_LONG;
Packit Service 82fcde
	    else if (spec->info.is_short)
Packit Service 82fcde
	      spec->data_arg_type = PA_INT|PA_FLAG_SHORT;
Packit Service 82fcde
	    else if (spec->info.is_char)
Packit Service 82fcde
	      spec->data_arg_type = PA_CHAR;
Packit Service 82fcde
	    else
Packit Service 82fcde
	      spec->data_arg_type = PA_INT;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	case L'e':
Packit Service 82fcde
	case L'E':
Packit Service 82fcde
	case L'f':
Packit Service 82fcde
	case L'F':
Packit Service 82fcde
	case L'g':
Packit Service 82fcde
	case L'G':
Packit Service 82fcde
	case L'a':
Packit Service 82fcde
	case L'A':
Packit Service 82fcde
	  if (spec->info.is_long_double)
Packit Service 82fcde
	    spec->data_arg_type = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
Packit Service 82fcde
	  else
Packit Service 82fcde
	    spec->data_arg_type = PA_DOUBLE;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	case L'c':
Packit Service 82fcde
	  spec->data_arg_type = PA_CHAR;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	case L'C':
Packit Service 82fcde
	  spec->data_arg_type = PA_WCHAR;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	case L's':
Packit Service 82fcde
	  spec->data_arg_type = PA_STRING;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	case L'S':
Packit Service 82fcde
	  spec->data_arg_type = PA_WSTRING;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	case L'p':
Packit Service 82fcde
	  spec->data_arg_type = PA_POINTER;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	case L'n':
Packit Service 82fcde
	  spec->data_arg_type = PA_INT|PA_FLAG_PTR;
Packit Service 82fcde
	  break;
Packit Service 82fcde
Packit Service 82fcde
	case L'm':
Packit Service 82fcde
	default:
Packit Service 82fcde
	  /* An unknown spec will consume no args.  */
Packit Service 82fcde
	  spec->ndata_args = 0;
Packit Service 82fcde
	  break;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (spec->data_arg == -1 && spec->ndata_args > 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* There are args consumed, but no positional spec.  Use the
Packit Service 82fcde
	 next sequential arg position.  */
Packit Service 82fcde
      spec->data_arg = posn;
Packit Service 82fcde
      nargs += spec->ndata_args;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (spec->info.spec == L'\0')
Packit Service 82fcde
    /* Format ended before this spec was complete.  */
Packit Service 82fcde
    spec->end_of_fmt = spec->next_fmt = format - 1;
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Find the next format spec.  */
Packit Service 82fcde
      spec->end_of_fmt = format;
Packit Service 82fcde
#ifdef COMPILE_WPRINTF
Packit Service 82fcde
      spec->next_fmt = __find_specwc (format);
Packit Service 82fcde
#else
Packit Service 82fcde
      spec->next_fmt = __find_specmb (format);
Packit Service 82fcde
#endif
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  return nargs;
Packit Service 82fcde
}