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