Example #1
0
static void *
format_parse (const char *format, bool translated, char **invalid_reason)
{
  struct spec spec;
  unsigned int unnumbered_arg_count;
  struct spec *result;

  spec.directives = 0;
  spec.numbered_arg_count = 0;
  spec.allocated = 0;
  spec.numbered = NULL;
  unnumbered_arg_count = 0;

  for (; *format != '\0';)
    if (*format++ == '%')
      {
	/* A directive.  */
	spec.directives++;

	if (*format == '%')
	  format++;
	else
	  {
	    bool brackets = false;
	    bool done = false;
	    unsigned int number = 0;
	    enum format_arg_type type = FAT_NONE;

	    if (*format == '|')
	      {
		format++;
		brackets = true;
	      }

	    if (isdigit (*format) && *format != '0')
	      {
		const char *f = format;
		unsigned int m = 0;

		do
		  {
		    m = 10 * m + (*f - '0');
		    f++;
		  }
		while (isdigit (*f));

		if ((!brackets && *f == '%') || *f == '$')
		  {
		    if (m == 0) /* can happen if m overflows */
		      {
			*invalid_reason = INVALID_ARGNO_0 (spec.directives);
			goto bad_format;
		      }
		    number = m;
		    if (*f == '%')
		      {
			type = FAT_ANY;
			done = true;
		      }
		    format = ++f;
		  }
	      }

	    if (!done)
	      {
		/* Parse flags.  */
		for (;;)
		  {
		    if (*format == ' ' || *format == '+' || *format == '-'
			|| *format == '#' || *format == '0' || *format == '\''
			|| *format == '_' || *format == '=' || *format == 'h'
			|| *format == 'l')
		      format++;
		    else
		      break;
		  }

		/* Parse width.  */
		if (*format == '*')
		  {
		    unsigned int width_number = 0;

		    format++;

		    if (isdigit (*format))
		      {
			const char *f = format;
			unsigned int m = 0;

			do
			  {
			    m = 10 * m + (*f - '0');
			    f++;
			  }
			while (isdigit (*f));

			if (*f == '$')
			  {
			    if (m == 0)
			      {
				*invalid_reason =
				  INVALID_WIDTH_ARGNO_0 (spec.directives);
				goto bad_format;
			      }
			    width_number = m;
			    format = ++f;
			  }
		      }

		    if (width_number)
		      {
			/* Numbered argument.  */

			/* Numbered and unnumbered specifications are
			   exclusive.  */
			if (unnumbered_arg_count > 0)
			  {
			    *invalid_reason =
			      INVALID_MIXES_NUMBERED_UNNUMBERED ();
			    goto bad_format;
			  }

			if (spec.allocated == spec.numbered_arg_count)
			  {
			    spec.allocated = 2 * spec.allocated + 1;
			    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
			  }
			spec.numbered[spec.numbered_arg_count].number = width_number;
			spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
			spec.numbered_arg_count++;
		      }
		    else
		      {
			/* Unnumbered argument.  */

			/* Numbered and unnumbered specifications are
			   exclusive.  */
			if (spec.numbered_arg_count > 0)
			  {
			    *invalid_reason =
			      INVALID_MIXES_NUMBERED_UNNUMBERED ();
			    goto bad_format;
			  }

			if (spec.allocated == unnumbered_arg_count)
			  {
			    spec.allocated = 2 * spec.allocated + 1;
			    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
			  }
			spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
			spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
			unnumbered_arg_count++;
		      }
		  }
		else if (isdigit (*format))
		  {
		    do format++; while (isdigit (*format));
		  }

		/* Parse precision.  */
		if (*format == '.')
		  {
		    format++;

		    if (*format == '*')
		      {
			unsigned int precision_number = 0;

			format++;

			if (isdigit (*format))
			  {
			    const char *f = format;
			    unsigned int m = 0;

			    do
			      {
				m = 10 * m + (*f - '0');
				f++;
			      }
			    while (isdigit (*f));

			    if (*f == '$')
			      {
				if (m == 0)
				  {
				    *invalid_reason =
				      INVALID_PRECISION_ARGNO_0 (spec.directives);
				    goto bad_format;
				  }
				precision_number = m;
				format = ++f;
			      }
			  }

			if (precision_number)
			  {
			    /* Numbered argument.  */

			    /* Numbered and unnumbered specifications are
			       exclusive.  */
			    if (unnumbered_arg_count > 0)
			      {
				*invalid_reason =
				  INVALID_MIXES_NUMBERED_UNNUMBERED ();
				goto bad_format;
			      }

			    if (spec.allocated == spec.numbered_arg_count)
			      {
				spec.allocated = 2 * spec.allocated + 1;
				spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
			      }
			    spec.numbered[spec.numbered_arg_count].number = precision_number;
			    spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
			    spec.numbered_arg_count++;
			  }
			else
			  {
			    /* Unnumbered argument.  */

			    /* Numbered and unnumbered specifications are
			       exclusive.  */
			    if (spec.numbered_arg_count > 0)
			      {
				*invalid_reason =
				  INVALID_MIXES_NUMBERED_UNNUMBERED ();
				goto bad_format;
			      }

			    if (spec.allocated == unnumbered_arg_count)
			      {
				spec.allocated = 2 * spec.allocated + 1;
				spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated  * sizeof (struct numbered_arg));
			      }
			    spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
			    spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
			    unnumbered_arg_count++;
			  }
		      }
		    else if (isdigit (*format))
		      {
			do format++; while (isdigit (*format));
		      }
		  }

		/* Parse size.  */
		for (;;)
		  {
		    if (*format == 'h' || *format == 'l' || *format == 'L')
		      format++;
		    else
		      break;
		  }

		switch (*format++)
		  {
		  case 'c': case 'C':
		    type = FAT_CHAR;
		    break;
		  case 's': case 'S':
		    type = FAT_ANY;
		    break;
		  case 'i': case 'd': case 'o': case 'u': case 'x': case 'X':
		    type = FAT_INTEGER;
		    break;
		  case 'e': case 'E': case 'f': case 'g': case 'G':
		    type = FAT_DOUBLE;
		    break;
		  case 'p':
		    type = FAT_POINTER;
		    break;
		  case 't':
		    type = FAT_NONE;
		    break;
		  case 'T':
		    if (*format == '\0')
		      {
			*invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
			goto bad_format;
		      }
		    format++;
		    type = FAT_NONE;
		    break;
		  case 'n':
		    type = FAT_NONE;
		    break;
		  case '|':
		    if (brackets)
		      {
			--format;
			type = FAT_ANY;
			break;
		      }
		    /*FALLTHROUGH*/
		  default:
		    --format;
		    *invalid_reason =
		      (*format == '\0'
		       ? INVALID_UNTERMINATED_DIRECTIVE ()
		       : INVALID_CONVERSION_SPECIFIER (spec.directives,
						       *format));
		    goto bad_format;
		  }
		if (brackets)
		  {
		    if (*format != '|')
		      {
			*invalid_reason =
			  (*format == '\0'
			   ? INVALID_UNTERMINATED_DIRECTIVE ()
			   : xasprintf (_("The directive number %u starts with | but does not end with |."),
					spec.directives));
			goto bad_format;
		      }
		    format++;
		  }
	      }

	    if (type != FAT_NONE)
	      {
		if (number)
		  {
		    /* Numbered argument.  */

		    /* Numbered and unnumbered specifications are exclusive.  */
		    if (unnumbered_arg_count > 0)
		      {
			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
			goto bad_format;
		      }

		    if (spec.allocated == spec.numbered_arg_count)
		      {
			spec.allocated = 2 * spec.allocated + 1;
			spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
		      }
		    spec.numbered[spec.numbered_arg_count].number = number;
		    spec.numbered[spec.numbered_arg_count].type = type;
		    spec.numbered_arg_count++;
		  }
		else
		  {
		    /* Unnumbered argument.  */

		    /* Numbered and unnumbered specifications are exclusive.  */
		    if (spec.numbered_arg_count > 0)
		      {
			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
			goto bad_format;
		      }

		    if (spec.allocated == unnumbered_arg_count)
		      {
			spec.allocated = 2 * spec.allocated + 1;
			spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
		      }
		    spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
		    spec.numbered[unnumbered_arg_count].type = type;
		    unnumbered_arg_count++;
		  }
	      }
	  }
      }

  /* Convert the unnumbered argument array to numbered arguments.  */
  if (unnumbered_arg_count > 0)
    spec.numbered_arg_count = unnumbered_arg_count;
  /* Sort the numbered argument array, and eliminate duplicates.  */
  else if (spec.numbered_arg_count > 1)
    {
      unsigned int i, j;
      bool err;

      qsort (spec.numbered, spec.numbered_arg_count,
	     sizeof (struct numbered_arg), numbered_arg_compare);

      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
      err = false;
      for (i = j = 0; i < spec.numbered_arg_count; i++)
	if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
	  {
	    enum format_arg_type type1 = spec.numbered[i].type;
	    enum format_arg_type type2 = spec.numbered[j-1].type;
	    enum format_arg_type type_both;

	    if (type1 == type2 || type2 == FAT_ANY)
	      type_both = type1;
	    else if (type1 == FAT_ANY)
	      type_both = type2;
	    else
	      {
		/* Incompatible types.  */
		type_both = FAT_NONE;
		if (!err)
		  *invalid_reason =
		    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
		err = true;
	      }

	    spec.numbered[j-1].type = type_both;
	  }
	else
	  {
	    if (j < i)
	      {
		spec.numbered[j].number = spec.numbered[i].number;
		spec.numbered[j].type = spec.numbered[i].type;
	      }
	    j++;
	  }
      spec.numbered_arg_count = j;
      if (err)
	/* *invalid_reason has already been set above.  */
	goto bad_format;
    }

  result = (struct spec *) xmalloc (sizeof (struct spec));
  *result = spec;
  return result;

 bad_format:
  if (spec.numbered != NULL)
    free (spec.numbered);
  return NULL;
}
Example #2
0
static void *
format_parse (const char *format, bool translated, char *fdi,
              char **invalid_reason)
{
  const char *const format_start = format;
  struct spec spec;
  unsigned int unnumbered_arg_count;
  struct spec *result;

  spec.directives = 0;
  spec.numbered_arg_count = 0;
  spec.allocated = 0;
  spec.numbered = NULL;
  unnumbered_arg_count = 0;

  for (; *format != '\0';)
    if (*format++ == '%')
      {
        /* A directive.  */
        unsigned int number = 0;
        enum format_arg_type type;

        FDI_SET (format - 1, FMTDIR_START);
        spec.directives++;

        if (isdigit (*format))
          {
            const char *f = format;
            unsigned int m = 0;

            do
              {
                m = 10 * m + (*f - '0');
                f++;
              }
            while (isdigit (*f));

            if (*f == '$')
              {
                if (m == 0)
                  {
                    *invalid_reason = INVALID_ARGNO_0 (spec.directives);
                    FDI_SET (f, FMTDIR_ERROR);
                    goto bad_format;
                  }
                number = m;
                format = ++f;
              }
          }

        /* Parse flags.  */
        while (*format == '-' || *format == '+' || *format == ' '
               || *format == '0' || *format == 'I')
          format++;

        /* Parse width.  */
        while (isdigit (*format))
          format++;

        if (*format == '.')
          {
            format++;

            while (isdigit (*format))
              format++;
          }

        switch (*format)
          {
          case '%':
            type = FAT_NONE;
            break;
          case 'c':
            type = FAT_CHARACTER;
            break;
          case 's':
            type = FAT_STRING;
            break;
          case 'b': case 'd': case 'o': case 'x': case 'X':
            type = FAT_INTEGER;
            break;
          case 'f':
            type = FAT_FLOAT;
            break;
          case 'j':
            type = FAT_ANY;
            break;
          default:
            if (*format == '\0')
              {
                *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
                FDI_SET (format - 1, FMTDIR_ERROR);
              }
            else
              {
                *invalid_reason =
                  INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
                FDI_SET (format, FMTDIR_ERROR);
              }
            goto bad_format;
          }

        if (type != FAT_NONE)
          {
            if (number)
              {
                /* Numbered argument.  */

                /* Numbered and unnumbered specifications are exclusive.  */
                if (unnumbered_arg_count > 0)
                  {
                    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
                    FDI_SET (format, FMTDIR_ERROR);
                    goto bad_format;
                  }

                if (spec.allocated == spec.numbered_arg_count)
                  {
                    spec.allocated = 2 * spec.allocated + 1;
                    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
                  }
                spec.numbered[spec.numbered_arg_count].number = number;
                spec.numbered[spec.numbered_arg_count].type = type;
                spec.numbered_arg_count++;
              }
            else
              {
                /* Unnumbered argument.  */

                /* Numbered and unnumbered specifications are exclusive.  */
                if (spec.numbered_arg_count > 0)
                  {
                    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
                    FDI_SET (format, FMTDIR_ERROR);
                    goto bad_format;
                  }

                if (spec.allocated == unnumbered_arg_count)
                  {
                    spec.allocated = 2 * spec.allocated + 1;
                    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
                  }
                spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
                spec.numbered[unnumbered_arg_count].type = type;
                unnumbered_arg_count++;
              }
          }

        FDI_SET (format, FMTDIR_END);

        format++;
      }

  /* Convert the unnumbered argument array to numbered arguments.  */
  if (unnumbered_arg_count > 0)
    spec.numbered_arg_count = unnumbered_arg_count;
  /* Sort the numbered argument array, and eliminate duplicates.  */
  else if (spec.numbered_arg_count > 1)
    {
      unsigned int i, j;
      bool err;

      qsort (spec.numbered, spec.numbered_arg_count,
             sizeof (struct numbered_arg), numbered_arg_compare);

      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
      err = false;
      for (i = j = 0; i < spec.numbered_arg_count; i++)
        if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
          {
            enum format_arg_type type1 = spec.numbered[i].type;
            enum format_arg_type type2 = spec.numbered[j-1].type;
            enum format_arg_type type_both;

            if (type1 == type2)
              type_both = type1;
            else
              {
                /* Incompatible types.  */
                type_both = FAT_NONE;
                if (!err)
                  *invalid_reason =
                    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
                err = true;
              }

            spec.numbered[j-1].type = type_both;
          }
        else
          {
            if (j < i)
              {
                spec.numbered[j].number = spec.numbered[i].number;
                spec.numbered[j].type = spec.numbered[i].type;
              }
            j++;
          }
      spec.numbered_arg_count = j;
      if (err)
        /* *invalid_reason has already been set above.  */
        goto bad_format;
    }

  result = XMALLOC (struct spec);
  *result = spec;
  return result;

 bad_format:
  if (spec.numbered != NULL)
    free (spec.numbered);
  return NULL;
}
Example #3
0
static void *
format_parse (const char *format, bool translated, bool objc_extensions,
	      char *fdi, char **invalid_reason)
{
  const char *const format_start = format;
  struct spec spec;
  unsigned int numbered_arg_count;
  struct numbered_arg *numbered;
  struct spec *result;

  spec.directives = 0;
  numbered_arg_count = 0;
  spec.unnumbered_arg_count = 0;
  spec.allocated = 0;
  numbered = NULL;
  spec.unnumbered = NULL;
  spec.unlikely_intentional = false;
  spec.sysdep_directives_count = 0;
  spec.sysdep_directives = NULL;

  for (; *format != '\0';)
    if (*format++ == '%')
      {
	/* A directive.  */
	unsigned int number = 0;
	format_arg_type_t type;
	format_arg_type_t size;

	FDI_SET (format - 1, FMTDIR_START);
	spec.directives++;

	if (isdigit (*format))
	  {
	    const char *f = format;
	    unsigned int m = 0;

	    do
	      {
		m = 10 * m + (*f - '0');
		f++;
	      }
	    while (isdigit (*f));

	    if (*f == '$')
	      {
		if (m == 0)
		  {
		    *invalid_reason = INVALID_ARGNO_0 (spec.directives);
		    FDI_SET (f, FMTDIR_ERROR);
		    goto bad_format;
		  }
		number = m;
		format = ++f;
	      }
	  }

	/* Parse flags.  */
	for (;;)
	  {
	    if (*format == ' ' || *format == '+' || *format == '-'
		|| *format == '#' || *format == '0' || *format == '\'')
	      format++;
	    else if (translated && *format == 'I')
	      {
		spec.sysdep_directives =
		  (const char **)
		  xrealloc (spec.sysdep_directives,
			    2 * (spec.sysdep_directives_count + 1)
			    * sizeof (const char *));
		spec.sysdep_directives[2 * spec.sysdep_directives_count] = format;
		spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1;
		spec.sysdep_directives_count++;
		format++;
	      }
	    else
	      break;
	  }

	/* Parse width.  */
	if (*format == '*')
	  {
	    unsigned int width_number = 0;

	    format++;

	    if (isdigit (*format))
	      {
		const char *f = format;
		unsigned int m = 0;

		do
		  {
		    m = 10 * m + (*f - '0');
		    f++;
		  }
		while (isdigit (*f));

		if (*f == '$')
		  {
		    if (m == 0)
		      {
			*invalid_reason =
			  INVALID_WIDTH_ARGNO_0 (spec.directives);
			FDI_SET (f, FMTDIR_ERROR);
			goto bad_format;
		      }
		    width_number = m;
		    format = ++f;
		  }
	      }

	    if (width_number)
	      {
		/* Numbered argument.  */

		/* Numbered and unnumbered specifications are exclusive.  */
		if (spec.unnumbered_arg_count > 0)
		  {
		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
		    FDI_SET (format - 1, FMTDIR_ERROR);
		    goto bad_format;
		  }

		if (spec.allocated == numbered_arg_count)
		  {
		    spec.allocated = 2 * spec.allocated + 1;
		    numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
		  }
		numbered[numbered_arg_count].number = width_number;
		numbered[numbered_arg_count].type = FAT_INTEGER;
		numbered_arg_count++;
	      }
	    else
	      {
		/* Unnumbered argument.  */

		/* Numbered and unnumbered specifications are exclusive.  */
		if (numbered_arg_count > 0)
		  {
		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
		    FDI_SET (format - 1, FMTDIR_ERROR);
		    goto bad_format;
		  }

		if (spec.allocated == spec.unnumbered_arg_count)
		  {
		    spec.allocated = 2 * spec.allocated + 1;
		    spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
		  }
		spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER;
		spec.unnumbered_arg_count++;
	      }
	  }
	else if (isdigit (*format))
	  {
	    do format++; while (isdigit (*format));
	  }

	/* Parse precision.  */
	if (*format == '.')
	  {
	    format++;

	    if (*format == '*')
	      {
		unsigned int precision_number = 0;

		format++;

		if (isdigit (*format))
		  {
		    const char *f = format;
		    unsigned int m = 0;

		    do
		      {
			m = 10 * m + (*f - '0');
			f++;
		      }
		    while (isdigit (*f));

		    if (*f == '$')
		      {
			if (m == 0)
			  {
			    *invalid_reason =
			      INVALID_PRECISION_ARGNO_0 (spec.directives);
			    FDI_SET (f, FMTDIR_ERROR);
			    goto bad_format;
			  }
			precision_number = m;
			format = ++f;
		      }
		  }

		if (precision_number)
		  {
		    /* Numbered argument.  */

		    /* Numbered and unnumbered specifications are exclusive.  */
		    if (spec.unnumbered_arg_count > 0)
		      {
			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
			FDI_SET (format - 1, FMTDIR_ERROR);
			goto bad_format;
		      }

		    if (spec.allocated == numbered_arg_count)
		      {
			spec.allocated = 2 * spec.allocated + 1;
			numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
		      }
		    numbered[numbered_arg_count].number = precision_number;
		    numbered[numbered_arg_count].type = FAT_INTEGER;
		    numbered_arg_count++;
		  }
		else
		  {
		    /* Unnumbered argument.  */

		    /* Numbered and unnumbered specifications are exclusive.  */
		    if (numbered_arg_count > 0)
		      {
			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
			FDI_SET (format - 1, FMTDIR_ERROR);
			goto bad_format;
		      }

		    if (spec.allocated == spec.unnumbered_arg_count)
		      {
			spec.allocated = 2 * spec.allocated + 1;
			spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
		      }
		    spec.unnumbered[spec.unnumbered_arg_count].type = FAT_INTEGER;
		    spec.unnumbered_arg_count++;
		  }
	      }
	    else if (isdigit (*format))
	      {
		do format++; while (isdigit (*format));
	      }
	  }

	if (*format == '<')
	  {
	    spec.sysdep_directives =
	      (const char **)
	      xrealloc (spec.sysdep_directives,
			2 * (spec.sysdep_directives_count + 1)
			* sizeof (const char *));
	    spec.sysdep_directives[2 * spec.sysdep_directives_count] = format;

	    format++;
	    /* Parse ISO C 99 section 7.8.1 format string directive.
	       Syntax:
	       P R I { d | i | o | u | x | X }
	       { { | LEAST | FAST } { 8 | 16 | 32 | 64 } | MAX | PTR }  */
	    if (*format != 'P')
	      {
		*invalid_reason = INVALID_C99_MACRO (spec.directives);
		FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
		goto bad_format;
	      }
	    format++;
	    if (*format != 'R')
	      {
		*invalid_reason = INVALID_C99_MACRO (spec.directives);
		FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
		goto bad_format;
	      }
	    format++;
	    if (*format != 'I')
	      {
		*invalid_reason = INVALID_C99_MACRO (spec.directives);
		FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
		goto bad_format;
	      }
	    format++;

	    switch (*format)
	      {
	      case 'i': case 'd':
		type = FAT_INTEGER;
		break;
	      case 'u': case 'o': case 'x': case 'X':
		type = FAT_INTEGER | FAT_UNSIGNED;
		break;
	      default:
		*invalid_reason = INVALID_C99_MACRO (spec.directives);
		FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
		goto bad_format;
	      }
	    format++;

	    if (format[0] == 'M' && format[1] == 'A' && format[2] == 'X')
	      {
		type |= FAT_SIZE_INTMAX_T;
		format += 3;
	      }
	    else if (format[0] == 'P' && format[1] == 'T' && format[2] == 'R')
	      {
		type |= FAT_SIZE_INTPTR_T;
		format += 3;
	      }
	    else
	      {
		if (format[0] == 'L' && format[1] == 'E' && format[2] == 'A'
		    && format[3] == 'S' && format[4] == 'T')
		  {
		    format += 5;
		    if (format[0] == '8')
		      {
			type |= FAT_SIZE_LEAST8_T;
			format++;
		      }
		    else if (format[0] == '1' && format[1] == '6')
		      {
			type |= FAT_SIZE_LEAST16_T;
			format += 2;
		      }
		    else if (format[0] == '3' && format[1] == '2')
		      {
			type |= FAT_SIZE_LEAST32_T;
			format += 2;
		      }
		    else if (format[0] == '6' && format[1] == '4')
		      {
			type |= FAT_SIZE_LEAST64_T;
			format += 2;
		      }
		    else
		      {
			*invalid_reason = INVALID_C99_MACRO (spec.directives);
			FDI_SET (*format == '\0' ? format - 1 : format,
				 FMTDIR_ERROR);
			goto bad_format;
		      }
		  }
		else if (format[0] == 'F' && format[1] == 'A'
			 && format[2] == 'S' && format[3] == 'T')
		  {
		    format += 4;
		    if (format[0] == '8')
		      {
			type |= FAT_SIZE_FAST8_T;
			format++;
		      }
		    else if (format[0] == '1' && format[1] == '6')
		      {
			type |= FAT_SIZE_FAST16_T;
			format += 2;
		      }
		    else if (format[0] == '3' && format[1] == '2')
		      {
			type |= FAT_SIZE_FAST32_T;
			format += 2;
		      }
		    else if (format[0] == '6' && format[1] == '4')
		      {
			type |= FAT_SIZE_FAST64_T;
			format += 2;
		      }
		    else
		      {
			*invalid_reason = INVALID_C99_MACRO (spec.directives);
			FDI_SET (*format == '\0' ? format - 1 : format,
				 FMTDIR_ERROR);
			goto bad_format;
		      }
		  }
		else
		  {
		    if (format[0] == '8')
		      {
			type |= FAT_SIZE_8_T;
			format++;
		      }
		    else if (format[0] == '1' && format[1] == '6')
		      {
			type |= FAT_SIZE_16_T;
			format += 2;
		      }
		    else if (format[0] == '3' && format[1] == '2')
		      {
			type |= FAT_SIZE_32_T;
			format += 2;
		      }
		    else if (format[0] == '6' && format[1] == '4')
		      {
			type |= FAT_SIZE_64_T;
			format += 2;
		      }
		    else
		      {
			*invalid_reason = INVALID_C99_MACRO (spec.directives);
			FDI_SET (*format == '\0' ? format - 1 : format,
				 FMTDIR_ERROR);
			goto bad_format;
		      }
		  }
	      }

	    if (*format != '>')
	      {
		*invalid_reason =
		  xasprintf (_("In the directive number %u, the token after '<' is not followed by '>'."), spec.directives);
		FDI_SET (*format == '\0' ? format - 1 : format, FMTDIR_ERROR);
		goto bad_format;
	      }

	    spec.sysdep_directives[2 * spec.sysdep_directives_count + 1] = format + 1;
	    spec.sysdep_directives_count++;
	  }
	else
	  {
	    /* Parse size.  */
	    size = 0;
	    for (;; format++)
	      {
		if (*format == 'h')
		  {
		    if (size & (FAT_SIZE_SHORT | FAT_SIZE_CHAR))
		      size = FAT_SIZE_CHAR;
		    else
		      size = FAT_SIZE_SHORT;
		  }
		else if (*format == 'l')
		  {
		    if (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG))
		      size = FAT_SIZE_LONGLONG;
		    else
		      size = FAT_SIZE_LONG;
		  }
		else if (*format == 'L')
		  size = FAT_SIZE_LONGLONG;
		else if (*format == 'q')
		  /* Old BSD 4.4 convention.  */
		  size = FAT_SIZE_LONGLONG;
		else if (*format == 'j')
		  size = FAT_SIZE_INTMAX_T;
		else if (*format == 'z' || *format == '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).  */
		  size = FAT_SIZE_SIZE_T;
		else if (*format == 't')
		  size = FAT_SIZE_PTRDIFF_T;
		else
		  break;
	      }

	    switch (*format)
	      {
	      case '%':
		/* Programmers writing _("%2%") most often will not want to
		   use this string as a c-format string, but rather as a
		   literal or as a different kind of format string.  */
		if (format[-1] != '%')
		  spec.unlikely_intentional = true;
		type = FAT_NONE;
		break;
	      case 'm': /* glibc extension */
		type = FAT_NONE;
		break;
	      case 'c':
		type = FAT_CHAR;
		type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
			 ? FAT_WIDE : 0);
		break;
	      case 'C': /* obsolete */
		type = FAT_CHAR | FAT_WIDE;
		break;
	      case 's':
		type = FAT_STRING;
		type |= (size & (FAT_SIZE_LONG | FAT_SIZE_LONGLONG)
			 ? FAT_WIDE : 0);
		break;
	      case 'S': /* obsolete */
		type = FAT_STRING | FAT_WIDE;
		break;
	      case 'i': case 'd':
		type = FAT_INTEGER;
		type |= (size & FAT_SIZE_MASK);
		break;
	      case 'u': case 'o': case 'x': case 'X':
		type = FAT_INTEGER | FAT_UNSIGNED;
		type |= (size & FAT_SIZE_MASK);
		break;
	      case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
	      case 'a': case 'A':
		type = FAT_DOUBLE;
		type |= (size & FAT_SIZE_LONGLONG);
		break;
	      case '@':
		if (objc_extensions)
		  {
		    type = FAT_OBJC_OBJECT;
		    break;
		  }
		goto other;
	      case 'p':
		type = FAT_POINTER;
		break;
	      case 'n':
		type = FAT_COUNT_POINTER;
		type |= (size & FAT_SIZE_MASK);
		break;
	      other:
	      default:
		if (*format == '\0')
		  {
		    *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
		    FDI_SET (format - 1, FMTDIR_ERROR);
		  }
		else
		  {
		    *invalid_reason =
		      INVALID_CONVERSION_SPECIFIER (spec.directives, *format);
		    FDI_SET (format, FMTDIR_ERROR);
		  }
		goto bad_format;
	      }
	  }

	if (type != FAT_NONE)
	  {
	    if (number)
	      {
		/* Numbered argument.  */

		/* Numbered and unnumbered specifications are exclusive.  */
		if (spec.unnumbered_arg_count > 0)
		  {
		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
		    FDI_SET (format, FMTDIR_ERROR);
		    goto bad_format;
		  }

		if (spec.allocated == numbered_arg_count)
		  {
		    spec.allocated = 2 * spec.allocated + 1;
		    numbered = (struct numbered_arg *) xrealloc (numbered, spec.allocated * sizeof (struct numbered_arg));
		  }
		numbered[numbered_arg_count].number = number;
		numbered[numbered_arg_count].type = type;
		numbered_arg_count++;
	      }
	    else
	      {
		/* Unnumbered argument.  */

		/* Numbered and unnumbered specifications are exclusive.  */
		if (numbered_arg_count > 0)
		  {
		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
		    FDI_SET (format, FMTDIR_ERROR);
		    goto bad_format;
		  }

		if (spec.allocated == spec.unnumbered_arg_count)
		  {
		    spec.allocated = 2 * spec.allocated + 1;
		    spec.unnumbered = (struct unnumbered_arg *) xrealloc (spec.unnumbered, spec.allocated * sizeof (struct unnumbered_arg));
		  }
		spec.unnumbered[spec.unnumbered_arg_count].type = type;
		spec.unnumbered_arg_count++;
	      }
	  }

	FDI_SET (format, FMTDIR_END);

	format++;
      }

  /* Sort the numbered argument array, and eliminate duplicates.  */
  if (numbered_arg_count > 1)
    {
      unsigned int i, j;
      bool err;

      qsort (numbered, numbered_arg_count,
	     sizeof (struct numbered_arg), numbered_arg_compare);

      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
      err = false;
      for (i = j = 0; i < numbered_arg_count; i++)
	if (j > 0 && numbered[i].number == numbered[j-1].number)
	  {
	    format_arg_type_t type1 = numbered[i].type;
	    format_arg_type_t type2 = numbered[j-1].type;
	    format_arg_type_t type_both;

	    if (type1 == type2)
	      type_both = type1;
	    else
	      {
		/* Incompatible types.  */
		type_both = FAT_NONE;
		if (!err)
		  *invalid_reason =
		    INVALID_INCOMPATIBLE_ARG_TYPES (numbered[i].number);
		err = true;
	      }

	    numbered[j-1].type = type_both;
	  }
	else
	  {
	    if (j < i)
	      {
		numbered[j].number = numbered[i].number;
		numbered[j].type = numbered[i].type;
	      }
	    j++;
	  }
      numbered_arg_count = j;
      if (err)
	/* *invalid_reason has already been set above.  */
	goto bad_format;
    }

  /* Verify that the format strings uses all arguments up to the highest
     numbered one.  */
  if (numbered_arg_count > 0)
    {
      unsigned int i;

      for (i = 0; i < numbered_arg_count; i++)
	if (numbered[i].number != i + 1)
	  {
	    *invalid_reason =
	      xasprintf (_("The string refers to argument number %u but ignores argument number %u."), numbered[i].number, i + 1);
	    goto bad_format;
	  }

      /* So now the numbered arguments array is equivalent to a sequence
	 of unnumbered arguments.  */
      spec.unnumbered_arg_count = numbered_arg_count;
      spec.allocated = spec.unnumbered_arg_count;
      spec.unnumbered = XNMALLOC (spec.allocated, struct unnumbered_arg);
      for (i = 0; i < spec.unnumbered_arg_count; i++)
	spec.unnumbered[i].type = numbered[i].type;
      free (numbered);
      numbered_arg_count = 0;
    }
Example #4
0
static void *
format_parse (const char *format, bool translated, char *fdi,
	      char **invalid_reason)
{
  const char *const format_start = format;
  struct spec spec;
  unsigned int unnumbered_arg_count;
  struct spec *result;

  spec.directives = 0;
  spec.numbered_arg_count = 0;
  spec.allocated = 0;
  spec.numbered = NULL;
  spec.uses_err_no = false;
  unnumbered_arg_count = 0;

  for (; *format != '\0';)
    if (*format++ == '%')
      {
	/* A directive.  */
	FDI_SET (format - 1, FMTDIR_START);
	spec.directives++;

	if (*format == '%' || *format == '<' || *format == '>'
	    || *format == '\'')
	  ;
	else if (*format == 'm')
	  spec.uses_err_no = true;
	else
	  {
	    unsigned int number = 0;
	    unsigned int flag_q = 0;
	    unsigned int flag_l = 0;
	    unsigned int flag_w = 0;
	    unsigned int flag_plus = 0;
	    unsigned int flag_sharp = 0;
	    format_arg_type_t size;
	    format_arg_type_t type;

	    if (isdigit (*format))
	      {
		const char *f = format;
		unsigned int m = 0;

		do
		  {
		    m = 10 * m + (*f - '0');
		    f++;
		  }
		while (isdigit (*f));

		if (*f == '$')
		  {
		    if (m == 0)
		      {
			*invalid_reason = INVALID_ARGNO_0 (spec.directives);
			FDI_SET (f, FMTDIR_ERROR);
			goto bad_format;
		      }
		    number = m;
		    format = ++f;
		  }
	      }

	    /* Parse flags and size.  */
	    for (;; format++)
	      {
		switch (*format)
		  {
		  case 'q':
		    if (flag_q > 0)
		      goto invalid_flags;
		    flag_q = 1;
		    continue;
		  case 'l':
		    if (flag_l > 1 || flag_w)
		      goto invalid_flags;
		    flag_l++;
		    continue;
		  case 'w':
		    if (flag_w > 0 || flag_l)
		      goto invalid_flags;
		    flag_w = 1;
		    continue;
		  case '+':
		    if (flag_plus > 0)
		      goto invalid_flags;
		    flag_plus = 1;
		    continue;
		  case '#':
		    if (flag_sharp > 0)
		      goto invalid_flags;
		    flag_sharp = 1;
		    continue;
		  invalid_flags:
		    *invalid_reason = xasprintf (_("In the directive number %u, the flags combination is invalid."), spec.directives);
		    FDI_SET (format, FMTDIR_ERROR);
		    goto bad_format;
		  default:
		    break;
		  }
		break;
	      }
	    size = (flag_l == 2 ? FAT_SIZE_LONGLONG :
		    flag_l == 1 ? FAT_SIZE_LONG :
		    flag_w ? FAT_SIZE_WIDE :
		    0);

	    if (*format == 'c')
	      type = FAT_CHAR;
	    else if (*format == 's')
	      type = FAT_STRING;
	    else if (*format == '.')
	      {
		format++;

		if (isdigit (*format))
		  {
		    do
		      format++;
		    while (isdigit (*format));

		    if (*format != 's')
		      {
			if (*format == '\0')
			  {
			    *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
			    FDI_SET (format - 1, FMTDIR_ERROR);
			  }
			else
			  {
			    *invalid_reason =
			      xasprintf (_("In the directive number %u, a precision is not allowed before '%c'."), spec.directives, *format);
			    FDI_SET (format, FMTDIR_ERROR);
			  }
			goto bad_format;
		      }

		    type = FAT_STRING;
		  }
		else if (*format == '*')
		  {
		    unsigned int precision_number = 0;

		    format++;

		    if (isdigit (*format))
		      {
			const char *f = format;
			unsigned int m = 0;

			do
			  {
			    m = 10 * m + (*f - '0');
			    f++;
			  }
			while (isdigit (*f));

			if (*f == '$')
			  {
			    if (m == 0)
			      {
				*invalid_reason = INVALID_WIDTH_ARGNO_0 (spec.directives);
				FDI_SET (f, FMTDIR_ERROR);
				goto bad_format;
			      }
			    if (unnumbered_arg_count > 0 || number == 0)
			      {
				*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
				FDI_SET (f, FMTDIR_ERROR);
				goto bad_format;
			      }
			    if (m != number - 1)
			      {
				*invalid_reason = xasprintf (_("In the directive number %u, the argument number for the precision must be equal to %u."), spec.directives, number - 1);
				FDI_SET (f, FMTDIR_ERROR);
				goto bad_format;
			      }
			    precision_number = m;
			    format = ++f;
			  }
		      }

		    if (precision_number)
		      {
			/* Numbered argument.  */

			/* Numbered and unnumbered specifications are exclusive.  */
			if (unnumbered_arg_count > 0)
			  {
			    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
			    FDI_SET (format - 1, FMTDIR_ERROR);
			    goto bad_format;
			  }

			if (spec.allocated == spec.numbered_arg_count)
			  {
			    spec.allocated = 2 * spec.allocated + 1;
			    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
			  }
			spec.numbered[spec.numbered_arg_count].number = precision_number;
			spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
			spec.numbered_arg_count++;
		      }
		    else
		      {
			/* Unnumbered argument.  */

			/* Numbered and unnumbered specifications are exclusive.  */
			if (spec.numbered_arg_count > 0)
			  {
			    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
			    FDI_SET (format - 1, FMTDIR_ERROR);
			    goto bad_format;
			  }

			if (spec.allocated == unnumbered_arg_count)
			  {
			    spec.allocated = 2 * spec.allocated + 1;
			    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
			  }
			spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
			spec.numbered[unnumbered_arg_count].type = FAT_INTEGER;
			unnumbered_arg_count++;
		      }

		    if (*format == 's')
		      type = FAT_STRING;
		    else
		      {
			if (*format == '\0')
			  {
			    *invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
			    FDI_SET (format - 1, FMTDIR_ERROR);
			  }
			else
			  {
			    *invalid_reason =
			      xasprintf (_("In the directive number %u, a precision specification is not allowed before '%c'."), spec.directives, *format);
			    FDI_SET (format, FMTDIR_ERROR);
			  }
			goto bad_format;
		      }
		  }
		else
		  {
		    *invalid_reason = xasprintf (_("In the directive number %u, the precision specification is invalid."), spec.directives);
		    FDI_SET (*format == '\0' ? format - 1 : format,
			     FMTDIR_ERROR);
		    goto bad_format;
		  }
	      }
	    else if (*format == 'i' || *format == 'd')
	      type = FAT_INTEGER | size;
	    else if (*format == 'o' || *format == 'u' || *format == 'x')
	      type = FAT_INTEGER | FAT_UNSIGNED | size;
	    else if (*format == 'p')
	      type = FAT_POINTER;
	    else if (*format == 'H')
	      type = FAT_LOCATION;
	    else if (*format == 'J')
	      type = FAT_TREE | FAT_TREE_DECL;
	    else if (*format == 'K')
	      type = FAT_TREE | FAT_TREE_STATEMENT;
	    else
	      {
		if (*format == 'D')
		  type = FAT_TREE | FAT_TREE_DECL;
		else if (*format == 'F')
		  type = FAT_TREE | FAT_TREE_FUNCDECL;
		else if (*format == 'T')
		  type = FAT_TREE | FAT_TREE_TYPE;
		else if (*format == 'E')
		  type = FAT_TREE | FAT_TREE_EXPRESSION;
		else if (*format == 'A')
		  type = FAT_TREE | FAT_TREE_ARGUMENT;
		else if (*format == 'C')
		  type = FAT_TREE_CODE;
		else if (*format == 'L')
		  type = FAT_LANGUAGES;
		else if (*format == 'O')
		  type = FAT_TREE_CODE | FAT_TREE_CODE_BINOP;
		else if (*format == 'P')
		  type = FAT_INTEGER | FAT_FUNCPARAM;
		else if (*format == 'Q')
		  type = FAT_TREE_CODE | FAT_TREE_CODE_ASSOP;
		else if (*format == 'V')
		  type = FAT_TREE | FAT_TREE_CV;
		else
		  {
		    if (*format == '\0')
		      {
			*invalid_reason = INVALID_UNTERMINATED_DIRECTIVE ();
			FDI_SET (format - 1, FMTDIR_ERROR);
		      }
		    else
		      {
			*invalid_reason =
			  (*format == 'c'
			   || *format == 's'
			   || *format == 'i' || *format == 'd'
			   || *format == 'o' || *format == 'u' || *format == 'x'
			   || *format == 'H'
			   ? xasprintf (_("In the directive number %u, flags are not allowed before '%c'."), spec.directives, *format)
			   : INVALID_CONVERSION_SPECIFIER (spec.directives,
							   *format));
			FDI_SET (format, FMTDIR_ERROR);
		      }
		    goto bad_format;
		  }
	      }

	    if (number)
	      {
		/* Numbered argument.  */

		/* Numbered and unnumbered specifications are exclusive.  */
		if (unnumbered_arg_count > 0)
		  {
		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
		    FDI_SET (format, FMTDIR_ERROR);
		    goto bad_format;
		  }

		if (spec.allocated == spec.numbered_arg_count)
		  {
		    spec.allocated = 2 * spec.allocated + 1;
		    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
		  }
		spec.numbered[spec.numbered_arg_count].number = number;
		spec.numbered[spec.numbered_arg_count].type = type;
		spec.numbered_arg_count++;
	      }
	    else
	      {
		/* Unnumbered argument.  */

		/* Numbered and unnumbered specifications are exclusive.  */
		if (spec.numbered_arg_count > 0)
		  {
		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
		    FDI_SET (format, FMTDIR_ERROR);
		    goto bad_format;
		  }

		if (spec.allocated == unnumbered_arg_count)
		  {
		    spec.allocated = 2 * spec.allocated + 1;
		    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
		  }
		spec.numbered[unnumbered_arg_count].number = unnumbered_arg_count + 1;
		spec.numbered[unnumbered_arg_count].type = type;
		unnumbered_arg_count++;
	      }
	  }

	FDI_SET (format, FMTDIR_END);

	format++;
      }

  /* Convert the unnumbered argument array to numbered arguments.  */
  if (unnumbered_arg_count > 0)
    spec.numbered_arg_count = unnumbered_arg_count;
  /* Sort the numbered argument array, and eliminate duplicates.  */
  else if (spec.numbered_arg_count > 1)
    {
      unsigned int i, j;
      bool err;

      qsort (spec.numbered, spec.numbered_arg_count,
	     sizeof (struct numbered_arg), numbered_arg_compare);

      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
      err = false;
      for (i = j = 0; i < spec.numbered_arg_count; i++)
	if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
	  {
	    format_arg_type_t type1 = spec.numbered[i].type;
	    format_arg_type_t type2 = spec.numbered[j-1].type;
	    format_arg_type_t type_both;

	    if (type1 == type2)
	      type_both = type1;
	    else
	      {
		/* Incompatible types.  */
		type_both = FAT_NONE;
		if (!err)
		  *invalid_reason =
		    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
		err = true;
	      }

	    spec.numbered[j-1].type = type_both;
	  }
	else
	  {
	    if (j < i)
	      {
		spec.numbered[j].number = spec.numbered[i].number;
		spec.numbered[j].type = spec.numbered[i].type;
	      }
	    j++;
	  }
      spec.numbered_arg_count = j;
      if (err)
	/* *invalid_reason has already been set above.  */
	goto bad_format;
    }

  result = XMALLOC (struct spec);
  *result = spec;
  return result;

 bad_format:
  if (spec.numbered != NULL)
    free (spec.numbered);
  return NULL;
}
Example #5
0
static void *
format_parse (const char *format, bool translated, char **invalid_reason)
{
  struct spec spec;
  struct spec *result;
  bool seen_numbered_arg;
  bool seen_unnumbered_arg;
  unsigned int number;

  spec.directives = 0;
  spec.numbered_arg_count = 0;
  spec.allocated = 0;
  spec.numbered = NULL;
  seen_numbered_arg = false;
  seen_unnumbered_arg = false;
  number = 1;

  for (; *format != '\0';)
    if (*format++ == '%')
      {
	/* A directive.  */
	spec.directives++;

	if (*format != '%')
	  {
	    bool is_numbered_arg;
	    bool short_flag;
	    enum format_arg_type type;

	    is_numbered_arg = false;
	    if (isdigit (*format))
	      {
		const char *f = format;
		unsigned int m = 0;

		do
		  {
		    m = 10 * m + (*f - '0');
		    f++;
		  }
		while (isdigit (*f));

		if (*f == '$')
		  {
		    if (m == 0)
		      {
			*invalid_reason = INVALID_ARGNO_0 (spec.directives);
			goto bad_format;
		      }
		    number = m;
		    format = ++f;

		    /* Numbered and unnumbered specifications are exclusive.  */
		    if (seen_unnumbered_arg)
		      {
			*invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
			goto bad_format;
		      }
		    is_numbered_arg = true;
		    seen_numbered_arg = true;
		  }
	      }

	    /* Numbered and unnumbered specifications are exclusive.  */
	    if (!is_numbered_arg)
	      {
		if (seen_numbered_arg)
		  {
		    *invalid_reason = INVALID_MIXES_NUMBERED_UNNUMBERED ();
		    goto bad_format;
		  }
		seen_unnumbered_arg = true;
	      }

	    /* Parse flags.  */
	    while (*format == ' ' || *format == '+' || *format == '-'
		   || *format == '#' || *format == '0')
	      format++;

	    /* Parse width.  */
	    if (*format == '*')
	      {
		format++;

		if (spec.allocated == spec.numbered_arg_count)
		  {
		    spec.allocated = 2 * spec.allocated + 1;
		    spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
		  }
		spec.numbered[spec.numbered_arg_count].number = number;
		spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
		spec.numbered_arg_count++;

		number++;
	      }
	    else if (isdigit (*format))
	      {
		do format++; while (isdigit (*format));
	      }

	    /* Parse precision.  */
	    if (*format == '.')
	      {
		format++;

		if (*format == '*')
		  {
		    format++;

		    if (spec.allocated == spec.numbered_arg_count)
		      {
			spec.allocated = 2 * spec.allocated + 1;
			spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
		      }
		    spec.numbered[spec.numbered_arg_count].number = number;
		    spec.numbered[spec.numbered_arg_count].type = FAT_INTEGER;
		    spec.numbered_arg_count++;

		    number++;
		  }
		else if (isdigit (*format))
		  {
		    do format++; while (isdigit (*format));
		  }
	      }

	    /* Parse optional size specification.  */
	    short_flag = false;
	    if (*format == 'h')
	      short_flag = true, format++;
	    else if (*format == 'l')
	      format++;

	    switch (*format)
	      {
	      case 'c':
		type = FAT_CHARACTER;
		break;
	      case 's':
		type = FAT_STRING;
		break;
	      case 'i': case 'd':
		type = (short_flag ? FAT_SHORT_INTEGER : FAT_INTEGER);
		break;
	      case 'u': case 'o': case 'x': case 'X':
		type = (short_flag ? FAT_SHORT_UNSIGNED_INTEGER : FAT_UNSIGNED_INTEGER);
		break;
	      case 'e': case 'E': case 'f': case 'g': case 'G':
		type = FAT_FLOAT;
		break;
	      default:
		*invalid_reason =
		  (*format == '\0'
		   ? INVALID_UNTERMINATED_DIRECTIVE ()
		   : INVALID_CONVERSION_SPECIFIER (spec.directives, *format));
		goto bad_format;
	      }

	    if (spec.allocated == spec.numbered_arg_count)
	      {
		spec.allocated = 2 * spec.allocated + 1;
		spec.numbered = (struct numbered_arg *) xrealloc (spec.numbered, spec.allocated * sizeof (struct numbered_arg));
	      }
	    spec.numbered[spec.numbered_arg_count].number = number;
	    spec.numbered[spec.numbered_arg_count].type = type;
	    spec.numbered_arg_count++;

	    number++;
	  }

	format++;
      }

  /* Sort the numbered argument array, and eliminate duplicates.  */
  if (spec.numbered_arg_count > 1)
    {
      unsigned int i, j;
      bool err;

      qsort (spec.numbered, spec.numbered_arg_count,
	     sizeof (struct numbered_arg), numbered_arg_compare);

      /* Remove duplicates: Copy from i to j, keeping 0 <= j <= i.  */
      err = false;
      for (i = j = 0; i < spec.numbered_arg_count; i++)
	if (j > 0 && spec.numbered[i].number == spec.numbered[j-1].number)
	  {
	    enum format_arg_type type1 = spec.numbered[i].type;
	    enum format_arg_type type2 = spec.numbered[j-1].type;
	    enum format_arg_type type_both;

	    if (type1 == type2)
	      type_both = type1;
	    else
	      {
		/* Incompatible types.  */
		type_both = FAT_NONE;
		if (!err)
		  *invalid_reason =
		    INVALID_INCOMPATIBLE_ARG_TYPES (spec.numbered[i].number);
		err = true;
	      }

	    spec.numbered[j-1].type = type_both;
	  }
	else
	  {
	    if (j < i)
	      {
		spec.numbered[j].number = spec.numbered[i].number;
		spec.numbered[j].type = spec.numbered[i].type;
	      }
	    j++;
	  }
      spec.numbered_arg_count = j;
      if (err)
	/* *invalid_reason has already been set above.  */
	goto bad_format;
    }

  result = (struct spec *) xmalloc (sizeof (struct spec));
  *result = spec;
  return result;

 bad_format:
  if (spec.numbered != NULL)
    free (spec.numbered);
  return NULL;
}