master
   1/*
   2 This Software is provided under the Zope Public License (ZPL) Version 2.1.
   3
   4 Copyright (c) 2011 by the mingw-w64 project
   5
   6 See the AUTHORS file for the list of contributors to the mingw-w64 project.
   7
   8 This license has been certified as open source. It has also been designated
   9 as GPL compatible by the Free Software Foundation (FSF).
  10
  11 Redistribution and use in source and binary forms, with or without
  12 modification, are permitted provided that the following conditions are met:
  13
  14   1. Redistributions in source code must retain the accompanying copyright
  15      notice, this list of conditions, and the following disclaimer.
  16   2. Redistributions in binary form must reproduce the accompanying
  17      copyright notice, this list of conditions, and the following disclaimer
  18      in the documentation and/or other materials provided with the
  19      distribution.
  20   3. Names of the copyright holders must not be used to endorse or promote
  21      products derived from this software without prior written permission
  22      from the copyright holders.
  23   4. The right to distribute this software or to use it for any purpose does
  24      not give you the right to use Servicemarks (sm) or Trademarks (tm) of
  25      the copyright holders.  Use of them is covered by separate agreement
  26      with the copyright holders.
  27   5. If any files are modified, you must cause the modified files to carry
  28      prominent notices stating that you changed the files and the date of
  29      any change.
  30
  31 Disclaimer
  32
  33 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
  34 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  36 EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
  37 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  38 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
  39 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  40 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  41 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  42 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43*/
  44
  45#define __LARGE_MBSTATE_T
  46
  47#include <limits.h>
  48#include <stddef.h>
  49#include <stdarg.h>
  50#include <stdio.h>
  51#include <stdint.h>
  52#include <stdlib.h>
  53#include <string.h>
  54#include <wchar.h>
  55#include <ctype.h>
  56#include <wctype.h>
  57#include <locale.h>
  58#include <errno.h>
  59
  60#include "mingw_sformat.h"
  61
  62/* Helper flags for conversion.  */
  63#define IS_C		0x0001
  64#define IS_S		0x0002
  65#define IS_L		0x0004
  66#define IS_LL		0x0008
  67#define IS_SIGNED_NUM	0x0010
  68#define IS_POINTER	0x0020
  69#define IS_HEX_FLOAT	0x0040
  70#define IS_SUPPRESSED	0x0080
  71#define USE_GROUP	0x0100
  72#define USE_GNU_ALLOC	0x0200
  73#define USE_POSIX_ALLOC	0x0400
  74
  75#define IS_ALLOC_USED	(USE_GNU_ALLOC | USE_POSIX_ALLOC)
  76
  77static void *
  78get_va_nth (va_list argp, unsigned int n)
  79{
  80  va_list ap;
  81  if (!n) abort ();
  82  va_copy (ap, argp);
  83  while (--n > 0)
  84    (void) va_arg(ap, void *);
  85  return va_arg (ap, void *);
  86}
  87
  88static void
  89optimize_alloc (char **p, char *end, size_t alloc_sz)
  90{
  91  size_t need_sz;
  92  char *h;
  93
  94  if (!p || !*p)
  95    return;
  96
  97  need_sz = end - *p;
  98  if (need_sz == alloc_sz)
  99    return;
 100
 101  if ((h = (char *) realloc (*p, need_sz)) != NULL)
 102    *p = h;
 103}
 104
 105static void
 106back_ch (int c, _IFP *s, size_t *rin, int not_eof)
 107{
 108  if (!not_eof && c == EOF)
 109    return;
 110  if (s->is_string == 0)
 111    {
 112      FILE *fp = s->fp;
 113      ungetc (c, fp);
 114      rin[0] -= 1;
 115      return;
 116    }
 117  rin[0] -= 1;
 118  s->bch[s->back_top] = c;
 119  s->back_top += 1;
 120}
 121
 122static int
 123in_ch (_IFP *s, size_t *rin)
 124{
 125  int r;
 126  if (s->back_top)
 127  {
 128    s->back_top -= 1;
 129    r = s->bch[s->back_top];
 130    rin[0] += 1;
 131  }
 132  else if (s->seen_eof)
 133  {
 134    return EOF;
 135  }
 136  else if (s->is_string)
 137  {
 138    const char *ps = s->str;
 139    r = ((int) *ps) & 0xff;
 140    ps++;
 141    if (r != 0)
 142    {
 143      rin[0] += 1;
 144      s->str = ps;
 145      return r;
 146    }
 147    s->seen_eof = 1;
 148    return EOF;
 149  }
 150  else
 151  {
 152    FILE *fp = (FILE *) s->fp;
 153    r = getc (fp);
 154    if (r != EOF)
 155      rin[0] += 1;
 156    else s->seen_eof = 1;
 157  }
 158  return r;
 159}
 160
 161static int
 162match_string (_IFP *s, size_t *rin, int *c, const char *str)
 163{
 164  int ch = *c;
 165
 166  if (*str == 0)
 167    return 1;
 168
 169  if (*str != (char) tolower (ch))
 170    return 0;
 171  ++str;
 172  while (*str != 0)
 173  {
 174    if ((ch = in_ch (s, rin)) == EOF)
 175    {
 176      c[0] = ch;
 177      return 0;
 178    }
 179
 180    if (*str != (char) tolower (ch))
 181    {
 182      c[0] = ch;
 183      return 0;
 184    }
 185    ++str;
 186  }
 187  c[0] = ch;
 188  return 1;
 189}
 190
 191struct gcollect
 192{
 193  size_t count;
 194  struct gcollect *next;
 195  char **ptrs[32];
 196};
 197
 198static void
 199release_ptrs (struct gcollect **pt, char **wbuf)
 200{
 201  struct gcollect *pf;
 202  size_t cnt;
 203
 204  if (wbuf)
 205    {
 206      free (*wbuf);
 207      *wbuf = NULL;
 208    }
 209  if (!pt || (pf = *pt) == NULL)
 210    return;
 211  while (pf != NULL)
 212    {
 213      struct gcollect *pf_sv = pf;
 214      for (cnt = 0; cnt < pf->count; ++cnt)
 215	{
 216	  free (*pf->ptrs[cnt]);
 217	  *pf->ptrs[cnt] = NULL;
 218	}
 219      pf = pf->next;
 220      free (pf_sv);
 221    }
 222  *pt = NULL;
 223}
 224
 225static int
 226cleanup_return (int rval, struct gcollect **pfree, char **strp, char **wbuf)
 227{
 228  if (rval == EOF)
 229      release_ptrs (pfree, wbuf);
 230  else
 231    {
 232      if (pfree)
 233        {
 234          struct gcollect *pf = *pfree, *pf_sv;
 235          while (pf != NULL)
 236            {
 237              pf_sv = pf;
 238              pf = pf->next;
 239              free (pf_sv);
 240            }
 241          *pfree = NULL;
 242        }
 243      if (strp != NULL)
 244	{
 245	  free (*strp);
 246	  *strp = NULL;
 247	}
 248      if (wbuf)
 249	{
 250	  free (*wbuf);
 251	  *wbuf = NULL;
 252	}
 253    }
 254  return rval;
 255}
 256
 257static struct gcollect *
 258resize_gcollect (struct gcollect *pf)
 259{
 260  struct gcollect *np;
 261  if (pf && pf->count < 32)
 262    return pf;
 263  np = malloc (sizeof (struct gcollect));
 264  np->count = 0;
 265  np->next = pf;
 266  return np;
 267}
 268
 269static char *
 270resize_wbuf (size_t wpsz, size_t *wbuf_max_sz, char *old)
 271{
 272  char *wbuf;
 273  size_t nsz;
 274  if (*wbuf_max_sz != wpsz)
 275    return old;
 276  nsz = (256 > (2 * wbuf_max_sz[0]) ? 256 : (2 * wbuf_max_sz[0]));
 277  if (!old)
 278    wbuf = (char *) malloc (nsz);
 279  else
 280    wbuf = (char *) realloc (old, nsz);
 281  if (!wbuf)
 282  {
 283    if (old)
 284      free (old);
 285  }
 286  else
 287    *wbuf_max_sz = nsz;
 288  return wbuf;
 289}
 290
 291int
 292__cdecl
 293__mingw_sformat (_IFP *s, const char *format, va_list argp)
 294{
 295  const char *f = format;
 296  struct gcollect *gcollect = NULL;
 297  size_t read_in = 0, wbuf_max_sz = 0, cnt;
 298  ssize_t str_sz = 0;
 299  char *str = NULL, **pstr = NULL, *wbuf = NULL;
 300  wchar_t *wstr = NULL;
 301  int rval = 0, c = 0, ignore_ws = 0;
 302  va_list arg;
 303  unsigned char fc;
 304  unsigned int npos;
 305  int width, flags, base = 0, errno_sv;
 306  size_t wbuf_cur_sz, read_in_sv, new_sz, n;
 307  char seen_dot, seen_exp, is_neg, not_in;
 308  char *tmp_wbuf_ptr, buf[MB_LEN_MAX];
 309  const char *lc_decimal_point, *lc_thousands_sep;
 310  mbstate_t state = {0}, cstate;
 311  union {
 312    unsigned long long ull;
 313    unsigned long ul;
 314    long long ll;
 315    long l;
 316  } cv_val;
 317
 318  arg = argp;
 319
 320  if (!s || s->fp == NULL || !format)
 321    {
 322      errno = EINVAL;
 323      return EOF;
 324    }
 325
 326  lc_decimal_point = localeconv()->decimal_point;
 327  lc_thousands_sep = localeconv()->thousands_sep;
 328  if (lc_thousands_sep != NULL && *lc_thousands_sep == 0)
 329    lc_thousands_sep = NULL;
 330
 331  while (*f != 0)
 332    {
 333      if (!isascii ((unsigned char) *f))
 334	{
 335	  int len;
 336
 337	  if ((len = mbrlen (f, strlen (f), &state)) > 0)
 338	    {
 339	      do
 340		{
 341		  if ((c = in_ch (s, &read_in)) == EOF || c != (unsigned char) *f++)
 342		    {
 343		      back_ch (c, s, &read_in, 1);
 344		      return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 345		    }
 346		}
 347	      while (--len > 0);
 348
 349	      continue;
 350	    }
 351	}
 352
 353      fc = *f++;
 354      if (fc != '%')
 355        {
 356          if (isspace (fc))
 357            ignore_ws = 1;
 358          else
 359	    {
 360	      if ((c = in_ch (s, &read_in)) == EOF)
 361		return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 362
 363	      if (ignore_ws)
 364		{
 365		  ignore_ws = 0;
 366		  if (isspace (c))
 367		    {
 368		      do
 369			{
 370			  if ((c = in_ch (s, &read_in)) == EOF)
 371			    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 372			}
 373		      while (isspace (c));
 374		    }
 375		}
 376
 377	      if (c != fc)
 378		{
 379		  back_ch (c, s, &read_in, 0);
 380		  return cleanup_return (rval, &gcollect, pstr, &wbuf);
 381		}
 382	    }
 383
 384	  continue;
 385	}
 386
 387      width = flags = 0;
 388      npos = 0;
 389      wbuf_cur_sz = 0;
 390
 391      if (isdigit ((unsigned char) *f))
 392	{
 393	  const char *svf = f;
 394	  npos = (unsigned char) *f++ - '0';
 395	  while (isdigit ((unsigned char) *f))
 396	    npos = npos * 10 + ((unsigned char) *f++ - '0');
 397	  if (*f != '$')
 398	    {
 399	      npos = 0;
 400	      f = svf;
 401	    }
 402	  else
 403	    f++;
 404	}
 405
 406      do
 407	{
 408	  if (*f == '*')
 409	    flags |= IS_SUPPRESSED;
 410	  else if (*f == '\'')
 411	    {
 412	      if (lc_thousands_sep)
 413		flags |= USE_GROUP;
 414	    }
 415	  else if (*f == 'I')
 416	    {
 417	      /* we don't support locale's digits (i18N), but ignore it for now silently.  */
 418	      ;
 419#ifdef _WIN32
 420              if (f[1] == '6' && f[2] == '4')
 421                {
 422		  flags |= IS_LL | IS_L;
 423		  f += 2;
 424		}
 425	      else if (f[1] == '3' && f[2] == '2')
 426		{
 427		  flags |= IS_L;
 428		  f += 2;
 429		}
 430	      else
 431	        {
 432#ifdef _WIN64
 433		  flags |= IS_LL | IS_L;
 434#else
 435		  flags |= IS_L;
 436#endif
 437		}
 438#endif
 439	    }
 440	  else
 441	    break;
 442	  ++f;
 443        }
 444      while (1);
 445
 446      while (isdigit ((unsigned char) *f))
 447	width = width * 10 + ((unsigned char) *f++ - '0');
 448
 449      if (!width)
 450	width = -1;
 451
 452      switch (*f)
 453	{
 454	case 'h':
 455	  ++f;
 456	  flags |= (*f == 'h' ? IS_C : IS_S);
 457	  if (*f == 'h')
 458	    ++f;
 459	  break;
 460	case 'l':
 461	  ++f;
 462	  flags |= (*f == 'l' ? IS_LL : 0) | IS_L;
 463	  if (*f == 'l')
 464	    ++f;
 465	  break;
 466	case 'q': case 'L':
 467	  ++f;
 468	  flags |= IS_LL | IS_L;
 469	  break;
 470	case 'a':
 471	  if (f[1] != 's' && f[1] != 'S' && f[1] != '[')
 472	    break;
 473	  ++f;
 474	  flags |= USE_GNU_ALLOC;
 475	  break;
 476	case 'm':
 477	  flags |= USE_POSIX_ALLOC;
 478	  ++f;
 479	  if (*f == 'l')
 480	    {
 481	      flags |= IS_L;
 482	      f++;
 483	    }
 484	  break;
 485	case 'z':
 486#ifdef _WIN64
 487	  flags |= IS_LL | IS_L;
 488#else
 489	  flags |= IS_L;
 490#endif
 491	  ++f;
 492	  break;
 493	case 'j':
 494	  if (sizeof (uintmax_t) > sizeof (unsigned long))
 495	    flags |= IS_LL;
 496	  else if (sizeof (uintmax_t) > sizeof (unsigned int))
 497	    flags |= IS_L;
 498	  ++f;
 499	  break;
 500	case 't':
 501#ifdef _WIN64
 502	  flags |= IS_LL;
 503#else
 504	  flags |= IS_L;
 505#endif
 506	  ++f;
 507	  break;
 508	case 0:
 509	  return cleanup_return (rval, &gcollect, pstr, &wbuf);
 510	default:
 511	  break;
 512	}
 513
 514      if (*f == 0)
 515	return cleanup_return (rval, &gcollect, pstr, &wbuf);
 516
 517      fc = *f++;
 518      if (ignore_ws || (fc != '[' && fc != 'c' && fc != 'C' && fc != 'n'))
 519	{
 520	  errno_sv = errno;
 521	  errno = 0;
 522	  do
 523	    {
 524	      if ((c == EOF || (c = in_ch (s, &read_in)) == EOF)
 525	          && errno == EINTR)
 526		return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 527	    }
 528	  while (isspace (c));
 529
 530	  ignore_ws = 0;
 531	  errno = errno_sv;
 532	  back_ch (c, s, &read_in, 0);
 533	}
 534
 535      switch (fc)
 536        {
 537        case 'c':
 538          if ((flags & IS_L) != 0)
 539            fc = 'C';
 540          break;
 541        case 's':
 542          if ((flags & IS_L) != 0)
 543            fc = 'S';
 544          break;
 545        }
 546
 547      switch (fc)
 548	{
 549	case '%':
 550	  if ((c = in_ch (s, &read_in)) == EOF)
 551	    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 552	  if (c != fc)
 553	    {
 554	      back_ch (c, s, &read_in, 1);
 555	      return cleanup_return (rval, &gcollect, pstr, &wbuf);
 556	    }
 557	  break;
 558
 559	case 'n':
 560	  if ((flags & IS_SUPPRESSED) == 0)
 561	    {
 562	      if ((flags & IS_LL) != 0)
 563		*(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = read_in;
 564	      else if ((flags & IS_L) != 0)
 565		*(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = read_in;
 566	      else if ((flags & IS_S) != 0)
 567		*(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = read_in;
 568	      else if ((flags & IS_C) != 0)
 569	        *(npos != 0 ? (char *) get_va_nth (argp, npos) : va_arg (arg, char *)) = read_in;
 570	      else
 571		*(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = read_in;
 572	    }
 573	  break;
 574
 575	case 'c':
 576	  if (width == -1)
 577	    width = 1;
 578
 579	  if ((flags & IS_SUPPRESSED) == 0)
 580	    {
 581	      if ((flags & IS_ALLOC_USED) != 0)
 582		{
 583		  if (npos != 0)
 584		    pstr = (char **) get_va_nth (argp, npos);
 585		  else
 586		    pstr = va_arg (arg, char **);
 587
 588		  if (!pstr)
 589		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 590
 591		  str_sz = (width > 1024 ? 1024 : width);
 592		  if ((str = *pstr = (char *) malloc (str_sz)) == NULL)
 593		    return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
 594
 595		  gcollect = resize_gcollect (gcollect);
 596		  gcollect->ptrs[gcollect->count++] = pstr;
 597		}
 598	      else
 599		{
 600		  if (npos != 0)
 601		    str = (char *) get_va_nth (argp, npos);
 602		  else
 603		    str = va_arg (arg, char *);
 604		  if (!str)
 605		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 606		}
 607	    }
 608
 609	  if ((c = in_ch (s, &read_in)) == EOF)
 610	    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 611
 612	  if ((flags & IS_SUPPRESSED) == 0)
 613	    {
 614	      do
 615		{
 616		  if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz))
 617		    {
 618		      new_sz = str_sz + (str_sz >= width ? width - 1 : str_sz);
 619		      while ((str = (char *) realloc (*pstr, new_sz)) == NULL
 620			     && new_sz > (size_t) (str_sz + 1))
 621			new_sz = str_sz + 1;
 622		      if (!str)
 623			{
 624			  release_ptrs (&gcollect, &wbuf);
 625			  return EOF;
 626			}
 627		      *pstr = str;
 628		      str += str_sz;
 629		      str_sz = new_sz;
 630		    }
 631		  *str++ = c;
 632		}
 633	      while (--width > 0 && (c = in_ch (s, &read_in)) != EOF);
 634	    }
 635	  else
 636	    while (--width > 0 && (c = in_ch (s, &read_in)) != EOF);
 637
 638	  if ((flags & IS_SUPPRESSED) == 0)
 639	    {
 640	      optimize_alloc (pstr, str, str_sz);
 641	      pstr = NULL;
 642	      ++rval;
 643	    }
 644
 645	  break;
 646
 647	case 'C':
 648	  if (width == -1)
 649	    width = 1;
 650
 651	  if ((flags & IS_SUPPRESSED) == 0)
 652	    {
 653	      if ((flags & IS_ALLOC_USED) != 0)
 654		{
 655		  if (npos != 0)
 656		    pstr = (char **) get_va_nth (argp, npos);
 657		  else
 658		    pstr = va_arg (arg, char **);
 659
 660		  if (!pstr)
 661		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 662		  str_sz = (width > 1024 ? 1024 : width);
 663		  *pstr = (char *) malloc (str_sz * sizeof (wchar_t));
 664		  if ((wstr = (wchar_t *) *pstr) == NULL)
 665		    return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
 666		  gcollect = resize_gcollect (gcollect);
 667		  gcollect->ptrs[gcollect->count++] = pstr;
 668		}
 669	      else
 670		{
 671		  if (npos != 0)
 672		    wstr = (wchar_t *) get_va_nth (argp, npos);
 673		  else
 674		    wstr = va_arg (arg, wchar_t *);
 675		  if (!wstr)
 676		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 677		}
 678	    }
 679
 680	  if ((c = in_ch (s, &read_in)) == EOF)
 681	    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 682
 683	  cstate = (mbstate_t){0};
 684
 685	  do
 686	    {
 687	      buf[0] = c;
 688
 689	      if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0
 690		  && wstr == ((wchar_t *) *pstr + str_sz))
 691		{
 692		  new_sz = str_sz + (str_sz > width ? width - 1 : str_sz);
 693
 694		  while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL
 695			 && new_sz > (size_t) (str_sz + 1))
 696		    new_sz = str_sz + 1;
 697		  if (!wstr)
 698		    {
 699		      release_ptrs (&gcollect, &wbuf);
 700		      return EOF;
 701		    }
 702		  *pstr = (char *) wstr;
 703		  wstr += str_sz;
 704		  str_sz = new_sz;
 705		}
 706
 707	      while (1)
 708		{
 709		  n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate);
 710
 711		  if (n == (size_t) -2)
 712		    {
 713		      if ((c = in_ch (s, &read_in)) == EOF)
 714			{
 715			  errno = EILSEQ;
 716			  return cleanup_return (rval, &gcollect, pstr, &wbuf);
 717			}
 718
 719		      buf[0] = c;
 720		      continue;
 721		    }
 722
 723		  if (n != 1)
 724		    {
 725			errno = EILSEQ;
 726			return cleanup_return (rval, &gcollect, pstr, &wbuf);
 727		    }
 728		  break;
 729		}
 730
 731	      ++wstr;
 732	    }
 733	  while (--width > 0 && (c = in_ch (s, &read_in)) != EOF);
 734
 735	  if ((flags & IS_SUPPRESSED) == 0)
 736	    {
 737	      optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t));
 738	      pstr = NULL;
 739	      ++rval;
 740	    }
 741	  break;
 742
 743	case 's':
 744	  if ((flags & IS_SUPPRESSED) == 0)
 745	    {
 746	      if ((flags & IS_ALLOC_USED) != 0)
 747		{
 748		 if (npos != 0)
 749		   pstr = (char **) get_va_nth (argp, npos);
 750		 else
 751		   pstr = va_arg (arg, char **);
 752
 753		  if (!pstr)
 754		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 755
 756		  str_sz = 100;
 757		  if ((str = *pstr = (char *) malloc (100)) == NULL)
 758		    return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
 759		  gcollect = resize_gcollect (gcollect);
 760		  gcollect->ptrs[gcollect->count++] = pstr;
 761		}
 762	      else
 763		{
 764		  if (npos != 0)
 765		    str = (char *) get_va_nth (argp, npos);
 766		  else
 767		    str = va_arg (arg, char *);
 768		  if (!str)
 769		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 770		}
 771	    }
 772
 773	  if ((c = in_ch (s, &read_in)) == EOF)
 774	    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 775
 776	  do
 777	    {
 778	      if (isspace (c))
 779		{
 780		  back_ch (c, s, &read_in, 1);
 781		  break;
 782		}
 783
 784	      if ((flags & IS_SUPPRESSED) == 0)
 785		{
 786		  *str++ = c;
 787		  if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz))
 788		    {
 789		      new_sz = str_sz * 2;
 790
 791		      while ((str = (char *) realloc (*pstr, new_sz)) == NULL
 792			     && new_sz > (size_t) (str_sz + 1))
 793			new_sz = str_sz + 1;
 794		      if (!str)
 795			{
 796			  if ((flags & USE_POSIX_ALLOC) == 0)
 797			    {
 798			      (*pstr)[str_sz - 1] = 0;
 799			      pstr = NULL;
 800			      ++rval;
 801			    }
 802			  return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
 803			}
 804		      *pstr = str;
 805		      str += str_sz;
 806		      str_sz = new_sz;
 807		    }
 808		}
 809	    }
 810	  while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF);
 811
 812	  if ((flags & IS_SUPPRESSED) == 0)
 813	    {
 814	      *str++ = 0;
 815	      optimize_alloc (pstr, str, str_sz);
 816	      pstr = NULL;
 817	      ++rval;
 818	    }
 819	  break;
 820
 821	case 'S':
 822	  if ((flags & IS_SUPPRESSED) == 0)
 823	    {
 824	      if ((flags & IS_ALLOC_USED) != 0)
 825		{
 826		  if (npos != 0)
 827		    pstr = (char **) get_va_nth (argp, npos);
 828		  else
 829		    pstr = va_arg (arg, char **);
 830
 831		  if (!pstr)
 832		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 833
 834		  str_sz = 100;
 835		  *pstr = (char *) malloc (100 * sizeof (wchar_t));
 836		  if ((wstr = (wchar_t *) *pstr) == NULL)
 837		    return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
 838		  gcollect = resize_gcollect (gcollect);
 839		  gcollect->ptrs[gcollect->count++] = pstr;
 840		}
 841	      else
 842		{
 843		  if (npos != 0)
 844		    wstr = (wchar_t *) get_va_nth (argp, npos);
 845		  else
 846		    wstr = va_arg (arg, wchar_t *);
 847		  if (!wstr)
 848		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
 849		}
 850	    }
 851
 852	  if ((c = in_ch (s, &read_in)) == EOF)
 853	    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 854
 855	  cstate = (mbstate_t){0};
 856
 857	  do
 858	    {
 859	      if (isspace (c))
 860		{
 861		  back_ch (c, s, &read_in, 1);
 862		  break;
 863		}
 864
 865	      buf[0] = c;
 866
 867	      while (1)
 868		{
 869		  n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate);
 870
 871		  if (n == (size_t) -2)
 872		    {
 873		      if ((c = in_ch (s, &read_in)) == EOF)
 874			{
 875			  errno = EILSEQ;
 876			  return cleanup_return (rval, &gcollect, pstr, &wbuf);
 877			}
 878
 879		      buf[0] = c;
 880		      continue;
 881		    }
 882
 883		  if (n != 1)
 884		    {
 885		      errno = EILSEQ;
 886		      return cleanup_return (rval, &gcollect, pstr, &wbuf);
 887		    }
 888
 889		  ++wstr;
 890		  break;
 891		}
 892
 893	      if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0
 894		  && wstr == ((wchar_t *) *pstr + str_sz))
 895		{
 896		  new_sz = str_sz * 2;
 897		  while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL
 898			 && new_sz > (size_t) (str_sz + 1))
 899		    new_sz = str_sz + 1;
 900		  if (!wstr)
 901		    {
 902		      if ((flags & USE_POSIX_ALLOC) == 0)
 903			{
 904			  ((wchar_t *) (*pstr))[str_sz - 1] = 0;
 905			  pstr = NULL;
 906			  ++rval;
 907			}
 908		      return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
 909		    }
 910		  *pstr = (char *) wstr;
 911		  wstr += str_sz;
 912		  str_sz = new_sz;
 913		}
 914	    }
 915	  while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF);
 916
 917	  if ((flags & IS_SUPPRESSED) == 0)
 918	    {
 919	      *wstr++ = 0;
 920	      optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t));
 921	      pstr = NULL;
 922	      ++rval;
 923	    }
 924	  break;
 925
 926	case 'd': case 'i':
 927	case 'o': case 'p':
 928	case 'u':
 929	case 'x': case 'X':
 930	  switch (fc)
 931	    {
 932	    case 'd':
 933	      flags |= IS_SIGNED_NUM;
 934	      base = 10;
 935	      break;
 936	    case 'i':
 937	      flags |= IS_SIGNED_NUM;
 938	      base = 0;
 939	      break;
 940	    case 'o':
 941	      base = 8;
 942	      break;
 943	    case 'p':
 944	      base = 16;
 945	      flags &= ~(IS_S | IS_LL | IS_L);
 946    #ifdef _WIN64
 947	      flags |= IS_LL;
 948    #endif
 949	      flags |= IS_L | IS_POINTER;
 950	      break;
 951	    case 'u':
 952	      base = 10;
 953	      break;
 954	    case 'x': case 'X':
 955	      base = 16;
 956	      break;
 957	    }
 958
 959	  if ((c = in_ch (s, &read_in)) == EOF)
 960	    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
 961	  if (c == '+' || c == '-')
 962	    {
 963	      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
 964	      wbuf[wbuf_cur_sz++] = c;
 965	      if (width > 0)
 966		--width;
 967	      c = in_ch (s, &read_in);
 968	    }
 969	  if (width != 0 && c == '0')
 970	    {
 971	      if (width > 0)
 972		--width;
 973
 974	      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
 975	      wbuf[wbuf_cur_sz++] = c;
 976	      c = in_ch (s, &read_in);
 977
 978	      if (width != 0 && tolower (c) == 'x')
 979		{
 980		  if (!base)
 981		    base = 16;
 982		  if (base == 16)
 983		    {
 984		      if (width > 0)
 985			--width;
 986		      c = in_ch (s, &read_in);
 987		    }
 988		}
 989	      else if (!base)
 990		base = 8;
 991	    }
 992
 993	  if (!base)
 994	    base = 10;
 995
 996	  while (c != EOF && width != 0)
 997	    {
 998	      if (base == 16)
 999		{
1000		  if (!isxdigit (c))
1001		    break;
1002		}
1003	      else if (!isdigit (c) || (int) (c - '0') >= base)
1004		{
1005		  const char *p = lc_thousands_sep;
1006		  int remain;
1007
1008		  if (base != 10 || (flags & USE_GROUP) == 0)
1009		    break;
1010		  remain = width > 0 ? width : INT_MAX;
1011		  while ((unsigned char) *p == c && remain >= 0)
1012		    {
1013		      /* As our conversion routines aren't supporting thousands
1014			 separators, we are filtering them here.  */
1015
1016		      ++p;
1017		      if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF)
1018			break;
1019		      --remain;
1020		    }
1021
1022		  if (*p != 0)
1023		    {
1024		      if (p > lc_thousands_sep)
1025			{
1026			  back_ch (c, s, &read_in, 0);
1027			  while (--p > lc_thousands_sep)
1028			    back_ch ((unsigned char) *p, s, &read_in, 1);
1029			  c = (unsigned char) *p;
1030			}
1031		      break;
1032		    }
1033
1034		  if (width > 0)
1035		    width = remain;
1036		  --wbuf_cur_sz;
1037		}
1038	      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1039	      wbuf[wbuf_cur_sz++] = c;
1040	      if (width > 0)
1041		--width;
1042
1043	      c = in_ch (s, &read_in);
1044	    }
1045
1046	  if (!wbuf_cur_sz || (wbuf_cur_sz == 1 && (wbuf[0] == '+' || wbuf[0] == '-')))
1047	    {
1048	      if (!wbuf_cur_sz && (flags & IS_POINTER) != 0
1049	          && match_string (s, &read_in, &c, "(nil)"))
1050		{
1051		  wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1052		  wbuf[wbuf_cur_sz++] = '0';
1053		}
1054	      else
1055		{
1056		  back_ch (c, s, &read_in, 0);
1057		  return cleanup_return (rval, &gcollect, pstr, &wbuf);
1058		}
1059	    }
1060	  else
1061	    back_ch (c, s, &read_in, 0);
1062
1063	  wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1064	  wbuf[wbuf_cur_sz++] = 0;
1065
1066	  if ((flags & IS_LL))
1067	    {
1068	      if (flags & IS_SIGNED_NUM)
1069		cv_val.ll = strtoll (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/);
1070	      else
1071		cv_val.ull = strtoull (wbuf, &tmp_wbuf_ptr, base);
1072	    }
1073	  else
1074	    {
1075	      if (flags & IS_SIGNED_NUM)
1076		cv_val.l = strtol (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/);
1077	      else
1078		cv_val.ul = strtoul (wbuf, &tmp_wbuf_ptr, base);
1079	    }
1080	  if (wbuf == tmp_wbuf_ptr)
1081	    return cleanup_return (rval, &gcollect, pstr, &wbuf);
1082
1083	  if ((flags & IS_SUPPRESSED) == 0)
1084	    {
1085	      if ((flags & IS_SIGNED_NUM) != 0)
1086		{
1087		  if ((flags & IS_LL) != 0)
1088		    *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = cv_val.ll;
1089		  else if ((flags & IS_L) != 0)
1090		    *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = cv_val.l;
1091		  else if ((flags & IS_S) != 0)
1092		    *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = (short) cv_val.l;
1093		  else if ((flags & IS_C) != 0)
1094		    *(npos != 0 ? (signed char *) get_va_nth (argp, npos) : va_arg (arg, signed char *)) = (signed char) cv_val.ul;
1095		  else
1096		    *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = (int) cv_val.l;
1097		}
1098	      else
1099		{
1100		  if ((flags & IS_LL) != 0)
1101		    *(npos != 0 ? (unsigned long long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long long *)) = cv_val.ull;
1102		  else if ((flags & IS_L) != 0)
1103		    *(npos != 0 ? (unsigned long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long *)) = cv_val.ul;
1104		  else if ((flags & IS_S) != 0)
1105		    *(npos != 0 ? (unsigned short *) get_va_nth (argp, npos) : va_arg (arg, unsigned short *))
1106		      = (unsigned short) cv_val.ul;
1107		  else if ((flags & IS_C) != 0)
1108		    *(npos != 0 ? (unsigned char *) get_va_nth (argp, npos) : va_arg (arg, unsigned char *)) = (unsigned char) cv_val.ul;
1109		  else
1110		    *(npos != 0 ? (unsigned int *) get_va_nth (argp, npos) : va_arg (arg, unsigned int *)) = (unsigned int) cv_val.ul;
1111		}
1112	      ++rval;
1113	    }
1114	  break;
1115
1116	case 'e': case 'E':
1117	case 'f': case 'F':
1118	case 'g': case 'G':
1119	case 'a': case 'A':
1120	  if (width > 0)
1121	    --width;
1122	  if ((c = in_ch (s, &read_in)) == EOF)
1123	    return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
1124
1125	  seen_dot = seen_exp = 0;
1126	  is_neg = (c == '-' ? 1 : 0);
1127
1128	  if (c == '-' || c == '+')
1129	    {
1130	      if (width == 0 || (c = in_ch (s, &read_in)) == EOF)
1131		return cleanup_return (rval, &gcollect, pstr, &wbuf);
1132	      if (width > 0)
1133		--width;
1134	    }
1135
1136	  if (tolower (c) == 'n')
1137	    {
1138	      const char *match_txt = "nan";
1139
1140	      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1141	      wbuf[wbuf_cur_sz++] = c;
1142
1143	      ++match_txt;
1144	      do
1145		{
1146		  if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0])
1147		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
1148
1149		  if (width > 0)
1150		    --width;
1151
1152		  wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1153		  wbuf[wbuf_cur_sz++] = c;
1154		  ++match_txt;
1155		}
1156	      while (*match_txt != 0);
1157	    }
1158	  else if (tolower (c) == 'i')
1159	    {
1160	      const char *match_txt = "inf";
1161
1162	      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1163	      wbuf[wbuf_cur_sz++] = c;
1164
1165	      ++match_txt;
1166	      do
1167		{
1168		  if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0])
1169		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
1170		  if (width > 0)
1171		    --width;
1172
1173		  wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1174		  wbuf[wbuf_cur_sz++] = c;
1175		  ++match_txt;
1176		}
1177	      while (*match_txt != 0);
1178
1179	      if (width != 0 && (c = in_ch (s, &read_in)) != EOF && tolower (c) == 'i')
1180		{
1181		  match_txt = "inity";
1182
1183		  if (width > 0)
1184		    --width;
1185
1186		  wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1187		  wbuf[wbuf_cur_sz++] = c;
1188		  ++match_txt;
1189
1190		  do
1191		    {
1192		      if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0])
1193			return cleanup_return (rval, &gcollect, pstr, &wbuf);
1194		      if (width > 0)
1195			--width;
1196
1197		      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1198		      wbuf[wbuf_cur_sz++] = c;
1199		      ++match_txt;
1200		    }
1201		  while (*match_txt != 0);
1202		}
1203	      else if (width != 0 && c != EOF)
1204	        back_ch (c, s, &read_in, 0);
1205	    }
1206	  else
1207	    {
1208	      not_in = 'e';
1209	      if (width != 0 && c == '0')
1210		{
1211		  wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1212		  wbuf[wbuf_cur_sz++] = c;
1213
1214		  c = in_ch (s, &read_in);
1215		  if (width > 0)
1216		    --width;
1217		  if (width != 0 && tolower (c) == 'x')
1218		    {
1219		      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1220		      wbuf[wbuf_cur_sz++] = c;
1221
1222		      flags |= IS_HEX_FLOAT;
1223		      not_in = 'p';
1224
1225		      flags &= ~USE_GROUP;
1226		      c = in_ch (s, &read_in);
1227		      if (width > 0)
1228			--width;
1229		    }
1230		}
1231
1232	      while (1)
1233		{
1234		  if (isdigit (c) || (!seen_exp && (flags & IS_HEX_FLOAT) != 0 && isxdigit (c))
1235		      || (seen_exp && wbuf[wbuf_cur_sz - 1] == not_in && (c == '-' || c == '+')))
1236		    {
1237		      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1238		      wbuf[wbuf_cur_sz++] = c;
1239		    }
1240		  else if (wbuf_cur_sz > 0 && !seen_exp && (char) tolower (c) == not_in)
1241		    {
1242		      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1243		      wbuf[wbuf_cur_sz++] = not_in;
1244		      seen_exp = seen_dot = 1;
1245		    }
1246		  else
1247		    {
1248		      const char *p = lc_decimal_point;
1249		      int remain = width > 0 ? width : INT_MAX;
1250
1251		      if (! seen_dot)
1252			{
1253			  while ((unsigned char) *p == c && remain >= 0)
1254			    {
1255			      ++p;
1256			      if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF)
1257				break;
1258			      --remain;
1259			    }
1260			}
1261
1262		      if (*p == 0)
1263			{
1264			  for (p = lc_decimal_point; *p != 0; ++p)
1265			    {
1266			      wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1267			      wbuf[wbuf_cur_sz++] = (unsigned char) *p;
1268			    }
1269			  if (width > 0)
1270			    width = remain;
1271			  seen_dot = 1;
1272			}
1273		      else
1274			{
1275			  const char *pp = lc_thousands_sep;
1276
1277			  if (!seen_dot && (flags & USE_GROUP) != 0)
1278			    {
1279			      while ((pp - lc_thousands_sep) < (p - lc_decimal_point)
1280				     && *pp == lc_decimal_point[(pp - lc_thousands_sep)])
1281				++pp;
1282			      if ((pp - lc_thousands_sep) == (p - lc_decimal_point))
1283				{
1284				  while ((unsigned char) *pp == c && remain >= 0)
1285				    {
1286				      ++pp;
1287				      if (*pp == 0 || !remain || (c = in_ch (s, &read_in)) == EOF)
1288					break;
1289				      --remain;
1290				    }
1291				}
1292			    }
1293
1294			  if (pp != NULL && *pp == 0)
1295			    {
1296			      /* As our conversion routines aren't supporting thousands
1297				 separators, we are filtering them here.  */
1298			      if (width > 0)
1299				width = remain;
1300			    }
1301			  else
1302			    {
1303			      back_ch (c, s, &read_in, 0);
1304			      break;
1305			    }
1306			}
1307		    }
1308
1309		  if (width == 0 || (c = in_ch (s, &read_in)) == EOF)
1310		    break;
1311
1312		  if (width > 0)
1313		    --width;
1314		}
1315
1316	      if (!wbuf_cur_sz || ((flags & IS_HEX_FLOAT) != 0 && wbuf_cur_sz == 2))
1317		return cleanup_return (rval, &gcollect, pstr, &wbuf);
1318	    }
1319
1320	  wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf);
1321	  wbuf[wbuf_cur_sz++] = 0;
1322
1323	  if ((flags & IS_LL) != 0)
1324	    {
1325	      long double ld;
1326	      ld = __mingw_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/);
1327	      if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf)
1328	        *(npos != 0 ? (long double *) get_va_nth (argp, npos) : va_arg (arg, long double *)) = is_neg ? -ld : ld;
1329	    }
1330	  else if ((flags & IS_L) != 0)
1331	    {
1332	      double d;
1333	      d = (double) __mingw_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/);
1334	      if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf)
1335		*(npos != 0 ? (double *) get_va_nth (argp, npos) : va_arg (arg, double *)) = is_neg ? -d : d;
1336	    }
1337	  else
1338	    {
1339	      float d = __mingw_strtof (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/);
1340	      if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf)
1341		*(npos != 0 ? (float *) get_va_nth (argp, npos) : va_arg (arg, float *)) = is_neg ? -d : d;
1342	    }
1343
1344	  if (wbuf == tmp_wbuf_ptr)
1345	    return cleanup_return (rval, &gcollect, pstr, &wbuf);
1346
1347	  if ((flags & IS_SUPPRESSED) == 0)
1348	    ++rval;
1349	  break;
1350
1351	case '[':
1352	  if ((flags & IS_L) != 0)
1353	    {
1354	      if ((flags & IS_SUPPRESSED) == 0)
1355		{
1356		  if ((flags & IS_ALLOC_USED) != 0)
1357		    {
1358		      if (npos != 0)
1359			pstr = (char **) get_va_nth (argp, npos);
1360		      else
1361			pstr = va_arg (arg, char **);
1362
1363		      if (!pstr)
1364			return cleanup_return (rval, &gcollect, pstr, &wbuf);
1365
1366		      str_sz = 100;
1367		      *pstr = (char *) malloc (100 * sizeof (wchar_t));
1368
1369		      if ((wstr = (wchar_t *) *pstr) == NULL)
1370			return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
1371		      gcollect = resize_gcollect (gcollect);
1372		      gcollect->ptrs[gcollect->count++] = pstr;
1373		    }
1374		  else
1375		    {
1376		      if (npos != 0)
1377			wstr = (wchar_t *) get_va_nth (argp, npos);
1378		      else
1379			wstr = va_arg (arg, wchar_t *);
1380		      if (!wstr)
1381			return cleanup_return (rval, &gcollect, pstr, &wbuf);
1382		    }
1383		}
1384	    }
1385	  else if ((flags & IS_SUPPRESSED) == 0)
1386	    {
1387	      if ((flags & IS_ALLOC_USED) != 0)
1388		{
1389		  if (npos != 0)
1390		    pstr = (char **) get_va_nth (argp, npos);
1391		  else
1392		    pstr = va_arg (arg, char **);
1393
1394		  if (!pstr)
1395		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
1396
1397		  str_sz = 100;
1398		  if ((str = *pstr = (char *) malloc (100)) == NULL)
1399		    return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf);
1400
1401		  gcollect = resize_gcollect (gcollect);
1402		  gcollect->ptrs[gcollect->count++] = pstr;
1403		}
1404	      else
1405		{
1406		  if (npos != 0)
1407		    str = (char *) get_va_nth (argp, npos);
1408		  else
1409		    str = va_arg (arg, char *);
1410		  if (!str)
1411		    return cleanup_return (rval, &gcollect, pstr, &wbuf);
1412		}
1413	    }
1414
1415	  not_in = (*f == '^' ? 1 : 0);
1416	  if (*f == '^')
1417	    f++;
1418
1419	  if (width < 0)
1420	    width = INT_MAX;
1421
1422	  if (wbuf_max_sz < 256)
1423	    {
1424	      wbuf_max_sz = 256;
1425	      if (wbuf)
1426	        free (wbuf);
1427	      wbuf = (char *) malloc (wbuf_max_sz);
1428	    }
1429	  memset (wbuf, 0, 256);
1430
1431	  fc = *f;
1432	  if (fc == ']' || fc == '-')
1433	    {
1434	      wbuf[fc] = 1;
1435	      ++f;
1436	    }
1437
1438	  while ((fc = *f++) != 0 && fc != ']')
1439	    {
1440	      if (fc == '-' && *f != 0 && *f != ']' && (unsigned char) f[-2] <= (unsigned char) *f)
1441		{
1442		  for (fc = (unsigned char) f[-2]; fc < (unsigned char) *f; ++fc)
1443		    wbuf[fc] = 1;
1444		}
1445	      else
1446		wbuf[fc] = 1;
1447	    }
1448
1449	  if (!fc)
1450	    return cleanup_return (rval, &gcollect, pstr, &wbuf);
1451
1452	  if ((flags & IS_L) != 0)
1453	    {
1454	      read_in_sv = read_in;
1455	      cnt = 0;
1456
1457	      if ((c = in_ch (s, &read_in)) == EOF)
1458		return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
1459
1460              cstate = (mbstate_t){0};
1461
1462	      do
1463		{
1464		  if (wbuf[c] == not_in)
1465		    {
1466		      back_ch (c, s, &read_in, 1);
1467		      break;
1468		    }
1469
1470		  if ((flags & IS_SUPPRESSED) == 0)
1471		    {
1472		      buf[0] = c;
1473		      n = mbrtowc (wstr, buf, 1, &cstate);
1474
1475		      if (n == (size_t) -2)
1476			{
1477			  ++cnt;
1478			  continue;
1479			}
1480		      cnt = 0;
1481
1482		      ++wstr;
1483		      if ((flags & IS_ALLOC_USED) != 0 && wstr == ((wchar_t *) *pstr + str_sz))
1484			{
1485			  new_sz = str_sz * 2;
1486			  while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL
1487			  	 && new_sz > (size_t) (str_sz + 1))
1488			    new_sz = str_sz + 1;
1489			  if (!wstr)
1490			    {
1491			      if ((flags & USE_POSIX_ALLOC) == 0)
1492				{
1493				  ((wchar_t *) (*pstr))[str_sz - 1] = 0;
1494				  pstr = NULL;
1495				  ++rval;
1496				}
1497			      else
1498				rval = EOF;
1499			      return cleanup_return (rval, &gcollect, pstr, &wbuf);
1500			    }
1501			  *pstr = (char *) wstr;
1502			  wstr += str_sz;
1503			  str_sz = new_sz;
1504			}
1505		    }
1506
1507		  if (--width <= 0)
1508		    break;
1509		}
1510	      while ((c = in_ch (s, &read_in)) != EOF);
1511
1512	      if (cnt != 0)
1513		{
1514		  errno = EILSEQ;
1515		  return cleanup_return (rval, &gcollect, pstr, &wbuf);
1516		}
1517
1518	      if (read_in_sv == read_in)
1519		return cleanup_return (rval, &gcollect, pstr, &wbuf);
1520
1521
1522	      if ((flags & IS_SUPPRESSED) == 0)
1523		{
1524		  *wstr++ = 0;
1525		  optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t));
1526		  pstr = NULL;
1527		  ++rval;
1528		}
1529	    }
1530	  else
1531	    {
1532	      read_in_sv = read_in;
1533
1534	      if ((c = in_ch (s, &read_in)) == EOF)
1535		return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf);
1536
1537	      do
1538		{
1539		  if (wbuf[c] == not_in)
1540		    {
1541		      back_ch (c, s, &read_in, 1);
1542		      break;
1543		    }
1544
1545		  if ((flags & IS_SUPPRESSED) == 0)
1546		    {
1547		      *str++ = c;
1548		      if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz))
1549			{
1550			  new_sz = str_sz * 2;
1551
1552			  while ((str = (char *) realloc (*pstr, new_sz)) == NULL
1553			         && new_sz > (size_t) (str_sz + 1))
1554			    new_sz = str_sz + 1;
1555			  if (!str)
1556			    {
1557			      if ((flags & USE_POSIX_ALLOC) == 0)
1558				{
1559				  (*pstr)[str_sz - 1] = 0;
1560				  pstr = NULL;
1561				  ++rval;
1562				}
1563			      else
1564			        rval = EOF;
1565			      return cleanup_return (rval, &gcollect, pstr, &wbuf);
1566			    }
1567			  *pstr = str;
1568			  str += str_sz;
1569			  str_sz = new_sz;
1570			}
1571		    }
1572		}
1573	      while (--width > 0 && (c = in_ch (s, &read_in)) != EOF);
1574
1575	      if (read_in_sv == read_in)
1576		return cleanup_return (rval, &gcollect, pstr, &wbuf);
1577
1578	      if ((flags & IS_SUPPRESSED) == 0)
1579		{
1580		  *str++ = 0;
1581		  optimize_alloc (pstr, str, str_sz);
1582		  pstr = NULL;
1583		  ++rval;
1584		}
1585	    }
1586	  break;
1587
1588	default:
1589	  return cleanup_return (rval, &gcollect, pstr, &wbuf);
1590	}
1591    }
1592
1593  if (ignore_ws)
1594    {
1595      while (isspace ((c = in_ch (s, &read_in))));
1596      back_ch (c, s, &read_in, 0);
1597    }
1598
1599  return cleanup_return (rval, &gcollect, pstr, &wbuf);
1600}