os/ossrv/genericopenlibs/cstdlib/LSTDIO/VFSCANF.C
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* VFSCANF.C
     2  * 
     3  * Portions Copyright (c) 1990-2004 Nokia Corporation and/or its subsidiary(-ies).
     4  * All rights reserved.
     5  */
     6 
     7 /* No user fns here. Pesch 15apr92. */
     8 
     9 /*
    10  * Copyright (c) 1990 The Regents of the University of California.
    11  * All rights reserved.
    12  *
    13  * Redistribution and use in source and binary forms are permitted
    14  * provided that the above copyright notice and this paragraph are
    15  * duplicated in all such forms and that any documentation,
    16  * advertising materials, and other materials related to such
    17  * distribution and use acknowledge that the software was developed
    18  * by the University of California, Berkeley.  The name of the
    19  * University may not be used to endorse or promote products derived
    20  * from this software without specific prior written permission.
    21  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
    22  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
    23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    24  */
    25 
    26 #include <_ansi.h>
    27 #include <ctype.h>
    28 #include <stdio.h>
    29 #include <stdlib.h>
    30 #include <stdarg.h>
    31 #include "LOCAL.H"
    32 
    33 #include "FLOATIO.H"
    34 #define	BUF	(MAXEXP+MAXFRACT+3)	/* 3 = sign + decimal point + NUL */
    35 
    36 /*
    37  * Flags used during conversion.
    38  */
    39 
    40 #define	LONG		0x01	/* l: long or double */
    41 #define	LONGDBL		0x02	/* L: long double; unimplemented */
    42 #define	SHORT		0x04	/* h: short */
    43 #define	SUPPRESS	0x08	/* suppress assignment */
    44 #define	POINTER		0x10	/* weird %p pointer (`fake hex') */
    45 #define	NOSKIP		0x20	/* do not skip blanks */
    46 
    47 /*
    48  * The following are used in numeric conversions only:
    49  * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
    50  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
    51  */
    52 
    53 #define	SIGNOK		0x40	/* +/- is (still) legal */
    54 #define	NDIGITS		0x80	/* no digits detected */
    55 
    56 #define	DPTOK		0x100	/* (float) decimal point is still legal */
    57 #define	EXPOK		0x200	/* (float) exponent (e+3, etc) still legal */
    58 
    59 #define	PFXOK		0x100	/* 0x prefix is (still) legal */
    60 #define	NZDIGITS	0x200	/* no zero digits detected */
    61 
    62 /*
    63  * Conversion types.
    64  */
    65 
    66 #define	CT_CHAR		0	/* %c conversion */
    67 #define	CT_CCL		1	/* %[...] conversion */
    68 #define	CT_STRING	2	/* %s conversion */
    69 #define	CT_INT		3	/* integer, i.e., strtol or strtoul */
    70 #define	CT_FLOAT	4	/* floating, i.e., strtod */
    71 
    72 #define u_char char
    73 #define u_long unsigned long
    74 
    75 /*static*/ u_char *__sccl (register char *tab,register u_char *fmt);
    76 
    77 /*
    78  * vfscanf
    79  */
    80 
    81 #define BufferEmpty (fp->_r <= 0 && __srefill(fp))
    82 
    83 int
    84 __svfscanf (register FILE *fp,char const *fmt0, va_list ap)
    85 {
    86   register u_char *fmt = (u_char *) fmt0;
    87   register int c;		/* character from format, or conversion */
    88   register size_t width;	/* field width, or 0 */
    89   register char *p;		/* points into all kinds of strings */
    90   register int n;		/* handy integer */
    91   register int flags;		/* flags as defined above */
    92   register char *p0;		/* saves original value of p when necessary */
    93   int nassigned;		/* number of fields assigned */
    94   int nread;			/* number of characters consumed from fp */
    95   int base = 0;			/* base argument to strtol/strtoul */
    96 
    97   u_long (*ccfn) (const char *_n, char **_end_PTR, int _base) = 0;	/* conversion function (strtol/strtoul) */
    98   char ccltab[256];		/* character class table for %[...] */
    99   char buf[BUF];		/* buffer for numeric conversions */
   100 
   101   short *sp;
   102   int *ip;
   103   float *flp;
   104   double *dp;
   105   long *lp;
   106 
   107   /* `basefix' is used to avoid `if' tests in the integer scanner */
   108   static const short basefix[17] =
   109     {10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
   110 
   111   nassigned = 0;
   112   nread = 0;
   113   for (;;)
   114     {
   115       c = *fmt++;
   116       if (c == 0)
   117 	return nassigned;
   118       if (isspace (c))
   119 	{
   120 	  for (;;)
   121 	    {
   122 	      if (BufferEmpty)
   123 		return nassigned;
   124 	      if (!isspace (*fp->_p))
   125 		break;
   126 	      nread++, fp->_r--, fp->_p++;
   127 	    }
   128 	  continue;
   129 	}
   130       if (c != '%')
   131 	goto literal;
   132       width = 0;
   133       flags = 0;
   134 
   135       /*
   136        * switch on the format.  continue if done; break once format
   137        * type is derived.
   138        */
   139 
   140     again:
   141       c = *fmt++;
   142 
   143       switch (c)
   144 	{
   145 	case '%':
   146 	literal:
   147 	  if (BufferEmpty)
   148 	    goto input_failure;
   149 	  if (*fp->_p != c)
   150 	    goto match_failure;
   151 	  fp->_r--, fp->_p++;
   152 	  nread++;
   153 	  continue;
   154 
   155 	case '*':
   156 	  flags |= SUPPRESS;
   157 	  goto again;
   158 	case 'l':
   159 	  flags |= LONG;
   160 	  goto again;
   161 	case 'L':
   162 	  /* not supported flags |= LONGDBL; */
   163 	  goto again;
   164 	case 'h':
   165 	  flags |= SHORT;
   166 	  goto again;
   167 
   168 	case '0':
   169 	case '1':
   170 	case '2':
   171 	case '3':
   172 	case '4':
   173 	case '5':
   174 	case '6':
   175 	case '7':
   176 	case '8':
   177 	case '9':
   178 	  width = width * 10 + c - '0';
   179 	  goto again;
   180 
   181 	  /*
   182 	   * Conversions. Those marked `compat' are for
   183 	   * 4.[123]BSD compatibility.
   184 	   *
   185 	   * (According to ANSI, E and X formats are supposed to
   186 	   * the same as e and x.  Sorry about that.)
   187 	   */
   188 
   189 	case 'D':		/* compat */
   190 	  flags |= LONG;
   191 	  /* FALLTHROUGH */
   192 	case 'd':
   193 	  c = CT_INT;
   194 	  ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol;
   195 	  base = 10;
   196 	  break;
   197 
   198 	case 'i':
   199 	  c = CT_INT;
   200 	  ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol;
   201 	  base = 0;
   202 	  break;
   203 
   204 	case 'O':		/* compat */
   205 	  flags |= LONG;
   206 	  /* FALLTHROUGH */
   207 	case 'o':
   208 	  c = CT_INT;
   209 	  ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
   210 	  base = 8;
   211 	  break;
   212 
   213 	case 'u':
   214 	  c = CT_INT;
   215 	  ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
   216 	  base = 10;
   217 	  break;
   218 
   219 	case 'X':		/* compat   XXX */
   220 	case 'x':
   221 	  flags |= PFXOK;	/* enable 0x prefixing */
   222 	  c = CT_INT;
   223 	  ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
   224 	  base = 16;
   225 	  break;
   226 
   227 	case 'E':		/* compat   XXX */
   228 	case 'G':		/* compat   XXX */
   229 /* ANSI says that E,G and X behave the same way as e,g,x */
   230 	  /* FALLTHROUGH */
   231 	case 'e':
   232 	case 'f':
   233 	case 'g':
   234 	  c = CT_FLOAT;
   235 	  break;
   236 
   237 	case 's':
   238 	  c = CT_STRING;
   239 	  break;
   240 
   241 	case '[':
   242 	  fmt = __sccl (ccltab, fmt);
   243 	  flags |= NOSKIP;
   244 	  c = CT_CCL;
   245 	  break;
   246 
   247 	case 'c':
   248 	  flags |= NOSKIP;
   249 	  c = CT_CHAR;
   250 	  break;
   251 
   252 	case 'p':		/* pointer format is like hex */
   253 	  flags |= POINTER | PFXOK;
   254 	  c = CT_INT;
   255 	  ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul;
   256 	  base = 16;
   257 	  break;
   258 
   259 	case 'n':
   260 	  if (flags & SUPPRESS)	/* ??? */
   261 	    continue;
   262 	  if (flags & SHORT)
   263 	    {
   264 	      sp = va_arg (ap, short *);
   265 	      *sp = (short)nread;
   266 	    }
   267 	  else if (flags & LONG)
   268 	    {
   269 	      lp = va_arg (ap, long *);
   270 	      *lp = nread;
   271 	    }
   272 	  else
   273 	    {
   274 	      ip = va_arg (ap, int *);
   275 	      *ip = nread;
   276 	    }
   277 	  continue;
   278 
   279 	  /*
   280 	   * Disgusting backwards compatibility hacks.	XXX
   281 	   */
   282 	case '\0':		/* compat */
   283 	  return EOF;
   284 
   285 	default:		/* compat */
   286 	  if (isupper (c))
   287 	    flags |= LONG;
   288 	  c = CT_INT;
   289 	  ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol;
   290 	  base = 10;
   291 	  break;
   292 	}
   293 
   294       /*
   295        * We have a conversion that requires input.
   296        */
   297       if (BufferEmpty)
   298 	goto input_failure;
   299 
   300       /*
   301        * Consume leading white space, except for formats that
   302        * suppress this.
   303        */
   304       if ((flags & NOSKIP) == 0)
   305 	{
   306 	  while (isspace (*fp->_p))
   307 	    {
   308 	      nread++;
   309 	      if (--fp->_r > 0)
   310 		fp->_p++;
   311 	      else
   312 	      if (__srefill (fp))
   313 		goto input_failure;
   314 	    }
   315 	  /*
   316 	   * Note that there is at least one character in the
   317 	   * buffer, so conversions that do not set NOSKIP ca
   318 	   * no longer result in an input failure.
   319 	   */
   320 	}
   321 
   322       /*
   323        * Do the conversion.
   324        */
   325       switch (c)
   326 	{
   327 
   328 	case CT_CHAR:
   329 	  /* scan arbitrary characters (sets NOSKIP) */
   330 	  if (width == 0)
   331 	    width = 1;
   332 	  if (flags & SUPPRESS)
   333 	    {
   334 	      size_t sum = 0;
   335 
   336 	      for (;;)
   337 		{
   338 		  if ((n = fp->_r) < (int)width)
   339 		    {
   340 		      sum += n;
   341 		      width -= n;
   342 		      fp->_p += n;
   343 		      if (__srefill (fp))
   344 			{
   345 			  if (sum == 0)
   346 			    goto input_failure;
   347 			  break;
   348 			}
   349 		    }
   350 		  else
   351 		    {
   352 		      sum += width;
   353 		      fp->_r -= width;
   354 		      fp->_p += width;
   355 		      break;
   356 		    }
   357 		}
   358 	      nread += sum;
   359 	    }
   360 	  else
   361 	    {
   362 	      size_t r = fread ((void*) va_arg (ap, char *), 1, width, fp);
   363 
   364 	      if (r == 0)
   365 		goto input_failure;
   366 	      nread += r;
   367 	      nassigned++;
   368 	    }
   369 	  break;
   370 
   371 	case CT_CCL:
   372 	  /* scan a (nonempty) character class (sets NOSKIP) */
   373 	  if (width == 0)
   374 	    width = (unsigned long)(~0);		/* `infinity' */
   375 	  /* take only those things in the class */
   376 	  if (flags & SUPPRESS)
   377 	    {
   378 	      n = 0;
   379 	      while (ccltab[*fp->_p])
   380 		{
   381 		  n++, fp->_r--, fp->_p++;
   382 		  if (--width == 0)
   383 		    break;
   384 		  if (BufferEmpty)
   385 		    {
   386 		      if (n == 0)
   387 			goto input_failure;
   388 		      break;
   389 		    }
   390 		}
   391 	      if (n == 0)
   392 		goto match_failure;
   393 	    }
   394 	  else
   395 	    {
   396 	      p0 = p = va_arg (ap, char *);
   397 	      while (ccltab[*fp->_p])
   398 		{
   399 		  fp->_r--;
   400 		  *p++ = *fp->_p++;
   401 		  if (--width == 0)
   402 		    break;
   403 		  if (BufferEmpty)
   404 		    {
   405 		      if (p == p0)
   406 			goto input_failure;
   407 		      break;
   408 		    }
   409 		}
   410 	      n = p - p0;
   411 	      if (n == 0)
   412 		goto match_failure;
   413 	      *p = 0;
   414 	      nassigned++;
   415 	    }
   416 	  nread += n;
   417 	  break;
   418 
   419 	case CT_STRING:
   420 	  /* like CCL, but zero-length string OK, & no NOSKIP */
   421 	  if (width == 0)
   422 	    width = (unsigned long)(~0);
   423 	  if (flags & SUPPRESS)
   424 	    {
   425 	      n = 0;
   426 	      while (!isspace (*fp->_p))
   427 		{
   428 		  n++, fp->_r--, fp->_p++;
   429 		  if (--width == 0)
   430 		    break;
   431 		  if (BufferEmpty)
   432 		    break;
   433 		}
   434 	      nread += n;
   435 	    }
   436 	  else
   437 	    {
   438 	      p0 = p = va_arg (ap, char *);
   439 	      while (!isspace (*fp->_p))
   440 		{
   441 		  fp->_r--;
   442 		  *p++ = *fp->_p++;
   443 		  if (--width == 0)
   444 		    break;
   445 		  if (BufferEmpty)
   446 		    break;
   447 		}
   448 	      *p = 0;
   449 	      nread += p - p0;
   450 	      nassigned++;
   451 	    }
   452 	  continue;
   453 
   454 	case CT_INT:
   455 	  /* scan an integer as if by strtol/strtoul */
   456 #ifdef hardway
   457 	  if (width == 0 || width > sizeof (buf) - 1)
   458 	    width = sizeof (buf) - 1;
   459 #else
   460 	  /* size_t is unsigned, hence this optimisation */
   461 	  if (--width > sizeof (buf) - 2)
   462 	    width = sizeof (buf) - 2;
   463 	  width++;
   464 #endif
   465 	  flags |= SIGNOK | NDIGITS | NZDIGITS;
   466 	  for (p = buf; width; width--)
   467 	    {
   468 	      c = *fp->_p;
   469 	      /*
   470 	       * Switch on the character; `goto ok' if we
   471 	       * accept it as a part of number.
   472 	       */
   473 	      switch (c)
   474 		{
   475 		  /*
   476 		   * The digit 0 is always legal, but is special.
   477 		   * For %i conversions, if no digits (zero or nonzero)
   478 		   * have been scanned (only signs), we will have base==0.
   479 		   * In that case, we should set it to 8 and enable 0x
   480 		   * prefixing. Also, if we have not scanned zero digits
   481 		   * before this, do not turn off prefixing (someone else
   482 		   * will turn it off if we have scanned any nonzero digits).
   483 		   */
   484 		case '0':
   485 		  if (base == 0)
   486 		    {
   487 		      base = 8;
   488 		      flags |= PFXOK;
   489 		    }
   490 		  if (flags & NZDIGITS)
   491 		    flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
   492 		  else
   493 		    flags &= ~(SIGNOK | PFXOK | NDIGITS);
   494 		  goto ok;
   495 
   496 		  /* 1 through 7 always legal */
   497 		case '1':
   498 		case '2':
   499 		case '3':
   500 		case '4':
   501 		case '5':
   502 		case '6':
   503 		case '7':
   504 		  base = basefix[base];
   505 		  flags &= ~(SIGNOK | PFXOK | NDIGITS);
   506 		  goto ok;
   507 
   508 		  /* digits 8 and 9 ok iff decimal or hex */
   509 		case '8':
   510 		case '9':
   511 		  base = basefix[base];
   512 		  if (base <= 8)
   513 		    break;	/* not legal here */
   514 		  flags &= ~(SIGNOK | PFXOK | NDIGITS);
   515 		  goto ok;
   516 
   517 		  /* letters ok iff hex */
   518 		case 'A':
   519 		case 'B':
   520 		case 'C':
   521 		case 'D':
   522 		case 'E':
   523 		case 'F':
   524 		case 'a':
   525 		case 'b':
   526 		case 'c':
   527 		case 'd':
   528 		case 'e':
   529 		case 'f':
   530 		  /* no need to fix base here */
   531 		  if (base <= 10)
   532 		    break;	/* not legal here */
   533 		  flags &= ~(SIGNOK | PFXOK | NDIGITS);
   534 		  goto ok;
   535 
   536 		  /* sign ok only as first character */
   537 		case '+':
   538 		case '-':
   539 		  if (flags & SIGNOK)
   540 		    {
   541 		      flags &= ~SIGNOK;
   542 		      goto ok;
   543 		    }
   544 		  break;
   545 
   546 		  /* x ok iff flag still set & 2nd char */
   547 		case 'x':
   548 		case 'X':
   549 		  if (flags & PFXOK && p == buf + 1)
   550 		    {
   551 		      base = 16;/* if %i */
   552 		      flags &= ~PFXOK;
   553 		      goto ok;
   554 		    }
   555 		  break;
   556 		}
   557 
   558 	      /*
   559 	       * If we got here, c is not a legal character
   560 	       * for a number.  Stop accumulating digits.
   561 	       */
   562 	      break;
   563 	    ok:
   564 	      /*
   565 	       * c is legal: store it and look at the next.
   566 	       */
   567 	      *p++ = (char)c;
   568 	      if (--fp->_r > 0)
   569 		fp->_p++;
   570 	      else
   571 	      if (__srefill (fp))
   572 		break;		/* EOF */
   573 	    }
   574 	  /*
   575 	   * If we had only a sign, it is no good; push back the sign.
   576 	   * If the number ends in `x', it was [sign] '0' 'x', so push back
   577 	   * the x and treat it as [sign] '0'.
   578 	   */
   579 	  if (flags & NDIGITS)
   580 	    {
   581 	      if (p > buf)
   582 		(void) ungetc (*(u_char *)-- p, fp);
   583 	      goto match_failure;
   584 	    }
   585 	  c = ((u_char *) p)[-1];
   586 	  if (c == 'x' || c == 'X')
   587 	    {
   588 	      --p;
   589 	      /*(void)*/ ungetc (c, fp);
   590 	    }
   591 	  if ((flags & SUPPRESS) == 0)
   592 	    {
   593 	      u_long res;
   594 
   595 	      *p = 0;
   596 	      res = (*ccfn) (buf, (char **) NULL, base);
   597 	      if (flags & POINTER)
   598 		*(va_arg (ap, void* *)) = (void*) res;
   599 	      else if (flags & SHORT)
   600 		{
   601 		  sp = va_arg (ap, short *);
   602 		  *sp = (short)res;
   603 		}
   604 	      else if (flags & LONG)
   605 		{
   606 		  lp = va_arg (ap, long *);
   607 		  *lp = res;
   608 		}
   609 	      else
   610 		{
   611 		  ip = va_arg (ap, int *);
   612 		  *ip = res;
   613 		}
   614 	      nassigned++;
   615 	    }
   616 	  nread += p - buf;
   617 	  break;
   618 
   619 	case CT_FLOAT:
   620 	  /* scan a floating point number as if by strtod */
   621 #ifdef hardway
   622 	  if (width == 0 || width > sizeof (buf) - 1)
   623 	    width = sizeof (buf) - 1;
   624 #else
   625 	  /* size_t is unsigned, hence this optimisation */
   626 	  if (--width > sizeof (buf) - 2)
   627 	    width = sizeof (buf) - 2;
   628 	  width++;
   629 #endif
   630 	  flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
   631 	  for (p = buf; width; width--)
   632 	    {
   633 	      c = *fp->_p;
   634 	      /*
   635 	       * This code mimicks the integer conversion
   636 	       * code, but is much simpler.
   637 	       */
   638 	      switch (c)
   639 		{
   640 
   641 		case '0':
   642 		case '1':
   643 		case '2':
   644 		case '3':
   645 		case '4':
   646 		case '5':
   647 		case '6':
   648 		case '7':
   649 		case '8':
   650 		case '9':
   651 		  flags &= ~(SIGNOK | NDIGITS);
   652 		  goto fok;
   653 
   654 		case '+':
   655 		case '-':
   656 		  if (flags & SIGNOK)
   657 		    {
   658 		      flags &= ~SIGNOK;
   659 		      goto fok;
   660 		    }
   661 		  break;
   662 		case '.':
   663 		  if (flags & DPTOK)
   664 		    {
   665 		      flags &= ~(SIGNOK | DPTOK);
   666 		      goto fok;
   667 		    }
   668 		  break;
   669 		case 'e':
   670 		case 'E':
   671 		  /* no exponent without some digits */
   672 		  if ((flags & (NDIGITS | EXPOK)) == EXPOK)
   673 		    {
   674 		      flags =
   675 			(flags & ~(EXPOK | DPTOK)) |
   676 			SIGNOK | NDIGITS;
   677 		      goto fok;
   678 		    }
   679 		  break;
   680 		}
   681 	      break;
   682 	    fok:
   683 	      *p++ = (char)c;
   684 	      if (--fp->_r > 0)
   685 		fp->_p++;
   686 	      else
   687 	      if (__srefill (fp))
   688 		break;		/* EOF */
   689 	    }
   690 	  /*
   691 	   * If no digits, might be missing exponent digits
   692 	   * (just give back the exponent) or might be missing
   693 	   * regular digits, but had sign and/or decimal point.
   694 	   */
   695 	  if (flags & NDIGITS)
   696 	    {
   697 	      if (flags & EXPOK)
   698 		{
   699 		  /* no digits at all */
   700 		  while (p > buf)
   701 		    ungetc (*(u_char *)-- p, fp);
   702 		  goto match_failure;
   703 		}
   704 	      /* just a bad exponent (e and maybe sign) */
   705 	      c = *(u_char *)-- p;
   706 	      if (c != 'e' && c != 'E')
   707 		{
   708 		  (void) ungetc (c, fp);	/* sign */
   709 		  c = *(u_char *)-- p;
   710 		}
   711 	      (void) ungetc (c, fp);
   712 	    }
   713 	  if ((flags & SUPPRESS) == 0)
   714 	    {
   715 	      double res;
   716 
   717 	      *p = 0;
   718 	      res = atof (buf);
   719 	      if (flags & LONG)
   720 		{
   721 		  dp = va_arg (ap, double *);
   722 		  *dp = res;
   723 		}
   724 	      else
   725 		{
   726 		  flp = va_arg (ap, float *);
   727 		  *flp = (float)res;
   728 		}
   729 	      nassigned++;
   730 	    }
   731 	  nread += p - buf;
   732 	  break;
   733 	}
   734     }
   735 input_failure:
   736   return nassigned ? nassigned : -1;
   737 match_failure:
   738   return nassigned;
   739 }
   740 
   741 /*
   742  * Fill in the given table from the scanset at the given format
   743  * (just after `[').  Return a pointer to the character past the
   744  * closing `]'.  The table has a 1 wherever characters should be
   745  * considered part of the scanset.
   746  */
   747 
   748 /*static*/
   749 u_char *
   750 __sccl (register char *tab,register u_char *fmt)
   751 {
   752   register int c, n, v;
   753 
   754   /* first `clear' the whole table */
   755   c = *fmt++;			/* first char hat => negated scanset */
   756   if (c == '^')
   757     {
   758       v = 1;			/* default => accept */
   759       c = *fmt++;		/* get new first char */
   760     }
   761   else
   762     v = 0;			/* default => reject */
   763   /* should probably use memset here */
   764   for (n = 0; n < 256; n++)
   765     tab[n] = (char)v;
   766   if (c == 0)
   767     return fmt - 1;		/* format ended before closing ] */
   768 
   769   /*
   770    * Now set the entries corresponding to the actual scanset to the
   771    * opposite of the above.
   772    *
   773    * The first character may be ']' (or '-') without being special; the
   774    * last character may be '-'.
   775    */
   776 
   777   v = 1 - v;
   778   for (;;)
   779     {
   780       tab[c] = (char)v;		/* take character c */
   781     doswitch:
   782       n = *fmt++;		/* and examine the next */
   783       switch (n)
   784 	{
   785 
   786 	case 0:		/* format ended too soon */
   787 	  return fmt - 1;
   788 
   789 	case '-':
   790 	  /*
   791 	   * A scanset of the form [01+-] is defined as `the digit 0, the
   792 	   * digit 1, the character +, the character -', but the effect of a
   793 	   * scanset such as [a-zA-Z0-9] is implementation defined.  The V7
   794 	   * Unix scanf treats `a-z' as `the letters a through z', but treats
   795 	   * `a-a' as `the letter a, the character -, and the letter a'.
   796 	   *
   797 	   * For compatibility, the `-' is not considerd to define a range if
   798 	   * the character following it is either a close bracket (required by
   799 	   * ANSI) or is not numerically greater than the character we just
   800 	   * stored in the table (c).
   801 	   */
   802 	  n = *fmt;
   803 	  if (n == ']' || n < c)
   804 	    {
   805 	      c = '-';
   806 	      break;		/* resume the for(;;) */
   807 	    }
   808 	  fmt++;
   809 	  do
   810 	    {			/* fill in the range */
   811 	      tab[++c] = (char)v;
   812 	    }
   813 	  while (c < n);
   814 #if 1			/* XXX another disgusting compatibility hack */
   815 	  /*
   816 	   * Alas, the V7 Unix scanf also treats formats such
   817 	   * as [a-c-e] as `the letters a through e'. This too
   818 	   * is permitted by the standard....
   819 	   */
   820 	  goto doswitch;
   821 #else
   822 	  c = *fmt++;
   823 	  if (c == 0)
   824 	    return fmt - 1;
   825 	  if (c == ']')
   826 	    return fmt;
   827 
   828 	  break;
   829 #endif
   830 
   831 	case ']':		/* end of scanset */
   832 	  return fmt;
   833 
   834 	default:		/* just another character */
   835 	  c = n;
   836 	  break;
   837 	}
   838     }
   839   /* NOTREACHED */
   840 }