Mercurial > flash_v2
diff packages/language/c/libc/current/src/stdio/input/vfscanf.cxx @ 0:3111d98ba7b3 ecos-v1_1-release
Initial commit of eCos version 1.1
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 11:16:07 +0000 |
| parents | |
| children | 443894e2e912 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/language/c/libc/current/src/stdio/input/vfscanf.cxx @@ -0,0 +1,973 @@ +//======================================================================== +// +// vfscanf.cxx +// +// I/O routines for vfscanf() for use with ANSI C library +// +//======================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Cygnus eCos Public License +// Version 1.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://sourceware.cygnus.com/ecos +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Cygnus Operating System, released +// September 30, 1998. +// +// The Initial Developer of the Original Code is Cygnus. Portions created +// by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//======================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): jlarmour@cygnus.co.uk +// Contributors: jlarmour@cygnus.co.uk +// Date: 1998-02-13 +// Purpose: +// Description: +// Usage: +// +//####DESCRIPTIONEND#### +// +//======================================================================== +// +// This code is based on original code with the following copyright: +// +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +// CONFIGURATION + +#include <pkgconf/libc.h> // Configuration header + +// Include the C library? And do we want the stdio stuff? And we have to +// have ungetc for this to work +#if defined(CYGPKG_LIBC) && defined(CYGPKG_LIBC_STDIO) && \ + defined(CYGFUN_LIBC_STDIO_ungetc) + + +// INCLUDES + +#include <cyg/infra/cyg_type.h> // Common type definitions and support +#include <stdarg.h> // Variable argument definitions +#include <stdio.h> // Standard header for all stdio files +#include <ctype.h> // isspace() and isupper() +#include <stdlib.h> // standard utility functions e.g. strtol +#include "clibincl/stream.hxx" // C library streams +#include "clibincl/stdiosupp.hxx" // Support for stdio functions + +// EXPORTED SYMBOLS + +externC int +vfscanf (FILE *fp, const char *fmt0, va_list ap) + CYGPRI_LIBC_WEAK_ALIAS("_vfscanf"); + + +#ifdef CYGSEM_LIBC_STDIO_SCANF_FLOATING_POINT + +#include <float.h> + +# define MAXFRACT DBL_DIG +# define MAXEXP DBL_MAX_10_EXP + +# define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */ + +#else // ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT + +# define BUF 40 + +#endif // ifdef CYGSEM_LIBC_STDIO_PRINTF_FLOATING_POINT + +/* + * Flags used during conversion. + */ + +#define LONG 0x01 /* l: long or double */ +#define LONGDBL 0x02 /* L: long double; unimplemented */ +#define SHORT 0x04 /* h: short */ +#define SUPPRESS 0x08 /* suppress assignment */ +#define POINTER 0x10 /* weird %p pointer (`fake hex') */ +#define NOSKIP 0x20 /* do not skip blanks */ + +/* + * The following are used in numeric conversions only: + * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; + * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. + */ + +#define SIGNOK 0x40 /* +/- is (still) legal */ +#define NDIGITS 0x80 /* no digits detected */ + +#define DPTOK 0x100 /* (float) decimal point is still legal */ +#define EXPOK 0x200 /* (float) exponent (e+3, etc) still + legal */ + +#define PFXOK 0x100 /* 0x prefix is (still) legal */ +#define NZDIGITS 0x200 /* no zero digits detected */ + +/* + * Conversion types. + */ + +#define CT_CHAR 0 /* %c conversion */ +#define CT_CCL 1 /* %[...] conversion */ +#define CT_STRING 2 /* %s conversion */ +#define CT_INT 3 /* integer, i.e., strtol or strtoul */ +#define CT_FLOAT 4 /* floating, i.e., strtod */ + +#if 0 +#define u_char unsigned char +#endif +#define u_char char +#define u_long unsigned long + +typedef unsigned long (*strtoul_t)(const char *, char **endptr, int base); + +static u_char * +__sccl (char *tab, u_char *fmt); + + +/* + * vfscanf + */ + +#define _CAST_VOID + +#define CURR_POS (file->peek_byte( (cyg_uint8 *)&curr_byte),&curr_byte ) + +#define INC_CURR_POS ( file->read_byte( (cyg_uint8 *)&curr_byte ) ) + +#define MOVE_CURR_POS(x) (file->set_position( (x), SEEK_CUR )) + +#define SPACE_LEFT (file->bytes_available_to_read()) + +#define REFILL (file->refill_read_buffer()) + +#define BufferEmpty ( !SPACE_LEFT && \ + (REFILL, (!SPACE_LEFT)) ) + +externC int +_vfscanf (FILE *fp, const char *fmt0, va_list ap) +{ + u_char *fmt = (u_char *) fmt0; + int c; /* character from format, or conversion */ + size_t width; /* field width, or 0 */ + char *p; /* points into all kinds of strings */ + int n; /* handy integer */ + int flags; /* flags as defined above */ + char *p0; /* saves original value of p when necessary */ + int nassigned; /* number of fields assigned */ + int nread; /* number of characters consumed from fp */ + int base = 0; /* base argument to strtol/strtoul */ + + strtoul_t ccfn = NULL; /* conversion function (strtol/strtoul) */ + char ccltab[256]; /* character class table for %[...] */ + char buf[BUF]; /* buffer for numeric conversions */ + + Cyg_StdioStream *file = (Cyg_StdioStream *)fp; + char curr_byte; + + short *sp; + int *ip; + float *flp; + long double *ldp; + double *dp; + long *lp; + + /* `basefix' is used to avoid `if' tests in the integer scanner */ + static const short basefix[17] = + {10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + + nassigned = 0; + nread = 0; + for (;;) + { + c = *fmt++; + if (c == 0) + return nassigned; + if (isspace (c)) + { + for (;;) + { + if (BufferEmpty) + return nassigned; + if (!isspace (*CURR_POS)) + break; + nread++, INC_CURR_POS; + } + continue; + } + if (c != '%') + goto literal; + width = 0; + flags = 0; + + /* + * switch on the format. continue if done; break once format + * type is derived. + */ + +again: + c = *fmt++; + + switch (c) + { + case '%': +literal: + if (BufferEmpty) + goto input_failure; + if (*CURR_POS != c) + goto match_failure; + INC_CURR_POS; + nread++; + continue; + + case '*': + flags |= SUPPRESS; + goto again; + case 'l': + flags |= LONG; + goto again; + case 'L': + flags |= LONGDBL; + goto again; + case 'h': + flags |= SHORT; + goto again; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + width = width * 10 + c - '0'; + goto again; + + /* + * Conversions. Those marked `compat' are for + * 4.[123]BSD compatibility. + * + * (According to ANSI, E and X formats are supposed to + * the same as e and x. Sorry about that.) + */ + + case 'D': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'd': + c = CT_INT; + ccfn = (strtoul_t)strtol; + base = 10; + break; + + case 'i': + c = CT_INT; + ccfn = (strtoul_t)strtol; + base = 0; + break; + + case 'O': /* compat */ + flags |= LONG; + /* FALLTHROUGH */ + case 'o': + c = CT_INT; + ccfn = strtoul; + base = 8; + break; + + case 'u': + c = CT_INT; + ccfn = strtoul; + base = 10; + break; + + case 'X': /* compat XXX */ + case 'x': + flags |= PFXOK; /* enable 0x prefixing */ + c = CT_INT; + ccfn = strtoul; + base = 16; + break; + + case 'E': /* compat XXX */ + case 'G': /* compat XXX */ +/* ANSI says that E,G and X behave the same way as e,g,x */ + /* FALLTHROUGH */ + case 'e': + case 'f': + case 'g': + c = CT_FLOAT; + break; + + case 's': + c = CT_STRING; + break; + + case '[': + fmt = __sccl (ccltab, fmt); + flags |= NOSKIP; + c = CT_CCL; + break; + + case 'c': + flags |= NOSKIP; + c = CT_CHAR; + break; + + case 'p': /* pointer format is like hex */ + flags |= POINTER | PFXOK; + c = CT_INT; + ccfn = strtoul; + base = 16; + break; + + case 'n': + if (flags & SUPPRESS) /* ??? */ + continue; + if (flags & SHORT) + { + sp = va_arg (ap, short *); + *sp = nread; + } + else if (flags & LONG) + { + lp = va_arg (ap, long *); + *lp = nread; + } + else + { + ip = va_arg (ap, int *); + *ip = nread; + } + continue; + + /* + * Disgusting backwards compatibility hacks. XXX + */ + case '\0': /* compat */ + return EOF; + + default: /* compat */ + if (isupper (c)) + flags |= LONG; + c = CT_INT; + ccfn = (strtoul_t)strtol; + base = 10; + break; + } + + /* + * We have a conversion that requires input. + */ + if (BufferEmpty) + goto input_failure; + + /* + * Consume leading white space, except for formats that + * suppress this. + */ + if ((flags & NOSKIP) == 0) + { + while (isspace (*CURR_POS)) + { + nread++; + INC_CURR_POS; + if (SPACE_LEFT == 0) +#ifndef CYGNUS_NEC + if (REFILL) +#endif + goto input_failure; + } + /* + * Note that there is at least one character in the + * buffer, so conversions that do not set NOSKIP ca + * no longer result in an input failure. + */ + } + + /* + * Do the conversion. + */ + switch (c) + { + + case CT_CHAR: + /* scan arbitrary characters (sets NOSKIP) */ + if (width == 0) + width = 1; + if (flags & SUPPRESS) + { + size_t sum = 0; + + for (;;) + { + if ((n = SPACE_LEFT) < (signed)width) + { + sum += n; + width -= n; + MOVE_CURR_POS(n-1); + INC_CURR_POS; +#ifndef CYGNUS_NEC + if (REFILL) + { +#endif + if (sum == 0) + goto input_failure; + break; +#ifndef CYGNUS_NEC + } +#endif + } + else + { + sum += width; + MOVE_CURR_POS(width - 1); + INC_CURR_POS; + break; + } + } + nread += sum; + } + else + { + /* Kludge city for the moment */ + char *dest = va_arg (ap, char *); + int n = width; + if (SPACE_LEFT == 0) +#ifndef CYGNUS_NEC + if (REFILL) +#endif + goto input_failure; + + while (n && !BufferEmpty) + { + *dest++ = *CURR_POS; + INC_CURR_POS; + n--; + nread++; + } + nassigned++; + } + break; + + case CT_CCL: + /* scan a (nonempty) character class (sets NOSKIP) */ + if (width == 0) + width = ~0; /* `infinity' */ + /* take only those things in the class */ + if (flags & SUPPRESS) + { + n = 0; + while (ccltab[*CURR_POS]) + { + n++, INC_CURR_POS; + if (--width == 0) + break; + if (BufferEmpty) + { + if (n == 0) + goto input_failure; + break; + } + } + if (n == 0) + goto match_failure; + } + else + { + p0 = p = va_arg (ap, char *); + while (ccltab[*CURR_POS]) + { + *p++ = *CURR_POS; + INC_CURR_POS; + if (--width == 0) + break; + if (BufferEmpty) + { + if (p == p0) + goto input_failure; + break; + } + } + n = p - p0; + if (n == 0) + goto match_failure; + *p = 0; + nassigned++; + } + nread += n; + break; + + case CT_STRING: + /* like CCL, but zero-length string OK, & no NOSKIP */ + if (width == 0) + width = ~0; + if (flags & SUPPRESS) + { + n = 0; + while (!isspace (*CURR_POS)) + { + n++, INC_CURR_POS; + if (--width == 0) + break; + if (BufferEmpty) + break; + } + nread += n; + } + else + { + p0 = p = va_arg (ap, char *); + while (!isspace (*CURR_POS)) + { + *p++ = *CURR_POS; + INC_CURR_POS; + if (--width == 0) + break; + if (BufferEmpty) + break; + } + *p = 0; + nread += p - p0; + nassigned++; + } + continue; + + case CT_INT: + /* scan an integer as if by strtol/strtoul */ +#ifdef hardway + if (width == 0 || width > sizeof (buf) - 1) + width = sizeof (buf) - 1; +#else + /* size_t is unsigned, hence this optimisation */ + if (--width > sizeof (buf) - 2) + width = sizeof (buf) - 2; + width++; +#endif + flags |= SIGNOK | NDIGITS | NZDIGITS; + for (p = buf; width; width--) + { + c = *CURR_POS; + /* + * Switch on the character; `goto ok' if we + * accept it as a part of number. + */ + switch (c) + { + /* + * The digit 0 is always legal, but is special. + * For %i conversions, if no digits (zero or nonzero) + * have been scanned (only signs), we will have base==0 + * In that case, we should set it to 8 and enable 0x + * prefixing. Also, if we have not scanned zero digits + * before this, do not turn off prefixing (someone else + * will turn it off if we have scanned any nonzero + * digits). + */ + case '0': + if (base == 0) + { + base = 8; + flags |= PFXOK; + } + if (flags & NZDIGITS) + flags &= ~(SIGNOK | NZDIGITS | NDIGITS); + else + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* 1 through 7 always legal */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + base = basefix[base]; + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* digits 8 and 9 ok iff decimal or hex */ + case '8': + case '9': + base = basefix[base]; + if (base <= 8) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* letters ok iff hex */ + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + /* no need to fix base here */ + if (base <= 10) + break; /* not legal here */ + flags &= ~(SIGNOK | PFXOK | NDIGITS); + goto ok; + + /* sign ok only as first character */ + case '+': + case '-': + if (flags & SIGNOK) + { + flags &= ~SIGNOK; + goto ok; + } + break; + + /* x ok iff flag still set & 2nd char */ + case 'x': + case 'X': + if (flags & PFXOK && p == buf + 1) + { + base = 16;/* if %i */ + flags &= ~PFXOK; + goto ok; + } + break; + } + + /* + * If we got here, c is not a legal character + * for a number. Stop accumulating digits. + */ + break; + ok: + /* + * c is legal: store it and look at the next. + */ + *p++ = c; + INC_CURR_POS; + if (SPACE_LEFT == 0) +#ifndef CYGNUS_NEC + if (REFILL) +#endif + break; /* EOF */ + } + /* + * If we had only a sign, it is no good; push back the sign. + * If the number ends in `x', it was [sign] '0' 'x', so push + * back the x and treat it as [sign] '0'. + */ + if (flags & NDIGITS) + { + if (p > buf) + _CAST_VOID _ungetc (*(u_char *)-- p, fp); + goto match_failure; + } + c = ((u_char *) p)[-1]; + if (c == 'x' || c == 'X') + { + --p; + /*(void)*/ _ungetc (c, fp); + } + if ((flags & SUPPRESS) == 0) + { + u_long res; + + *p = 0; + res = (*ccfn) (buf, (char **) NULL, base); + if (flags & POINTER) + *(va_arg (ap, char **)) = (char *) (CYG_ADDRESS) res; + else if (flags & SHORT) + { + sp = va_arg (ap, short *); + *sp = res; + } + else if (flags & LONG) + { + lp = va_arg (ap, long *); + *lp = res; + } + else + { + ip = va_arg (ap, int *); + *ip = res; + } + nassigned++; + } + nread += p - buf; + break; + + case CT_FLOAT: + /* scan a floating point number as if by strtod */ + +#ifdef hardway + if (width == 0 || width > sizeof (buf) - 1) + width = sizeof (buf) - 1; +#else + /* size_t is unsigned, hence this optimisation */ + if (--width > sizeof (buf) - 2) + width = sizeof (buf) - 2; + width++; +#endif // ifdef hardway + + flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; + for (p = buf; width; width--) + { + c = *CURR_POS; + /* + * This code mimicks the integer conversion + * code, but is much simpler. + */ + switch (c) + { + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + flags &= ~(SIGNOK | NDIGITS); + goto fok; + + case '+': + case '-': + if (flags & SIGNOK) + { + flags &= ~SIGNOK; + goto fok; + } + break; + case '.': + if (flags & DPTOK) + { + flags &= ~(SIGNOK | DPTOK); + goto fok; + } + break; + case 'e': + case 'E': + /* no exponent without some digits */ + if ((flags & (NDIGITS | EXPOK)) == EXPOK) + { + flags = + (flags & ~(EXPOK | DPTOK)) | + SIGNOK | NDIGITS; + goto fok; + } + break; + } + break; + fok: + *p++ = c; + INC_CURR_POS; + if (SPACE_LEFT == 0) +#ifndef CYGNUS_NEC + if (REFILL) +#endif + break; /* EOF */ + } + /* + * If no digits, might be missing exponent digits + * (just give back the exponent) or might be missing + * regular digits, but had sign and/or decimal point. + */ + if (flags & NDIGITS) + { + if (flags & EXPOK) + { + /* no digits at all */ + while (p > buf) + _ungetc (*(u_char *)-- p, fp); + goto match_failure; + } + /* just a bad exponent (e and maybe sign) */ + c = *(u_char *)-- p; + if (c != 'e' && c != 'E') + { + _CAST_VOID _ungetc (c, fp); /* sign */ + c = *(u_char *)-- p; + } + _CAST_VOID _ungetc (c, fp); + } + if ((flags & SUPPRESS) == 0) + { + double res; + + *p = 0; +#ifdef CYGSEM_LIBC_STDIO_SCANF_FLOATING_POINT + res = atof (buf); +#else + res = 0.0; +#endif + if (flags & LONG) + { + dp = va_arg (ap, double *); + *dp = res; + } + else if (flags & LONGDBL) + { + ldp = va_arg (ap, long double *); + *ldp = res; + } + else + { + flp = va_arg (ap, float *); + *flp = res; + } + nassigned++; + } + nread += p - buf; + break; + } + } + +input_failure: + return nassigned ? nassigned : -1; +match_failure: + return nassigned; +} // _vfscanf() + +/* + * Fill in the given table from the scanset at the given format + * (just after `['). Return a pointer to the character past the + * closing `]'. The table has a 1 wherever characters should be + * considered part of the scanset. + */ + +/*static*/ +u_char * +__sccl (char *tab, u_char *fmt) +{ + int c, n, v; + + /* first `clear' the whole table */ + c = *fmt++; /* first char hat => negated scanset */ + if (c == '^') + { + v = 1; /* default => accept */ + c = *fmt++; /* get new first char */ + } + else + v = 0; /* default => reject */ + /* should probably use memset here */ + for (n = 0; n < 256; n++) + tab[n] = v; + if (c == 0) + return fmt - 1; /* format ended before closing ] */ + + /* + * Now set the entries corresponding to the actual scanset to the + * opposite of the above. + * + * The first character may be ']' (or '-') without being special; the + * last character may be '-'. + */ + + v = 1 - v; + for (;;) + { + tab[c] = v; /* take character c */ + doswitch: + n = *fmt++; /* and examine the next */ + switch (n) + { + + case 0: /* format ended too soon */ + return fmt - 1; + + case '-': + /* + * A scanset of the form [01+-] is defined as `the digit 0, the + * digit 1, the character +, the character -', but the effect + * of a scanset such as [a-zA-Z0-9] is implementation defined. + * The V7 Unix scanf treats `a-z' as `the letters a through z', + * but treats `a-a' as `the letter a, the character -, and the + * letter a'. + * + * For compatibility, the `-' is not considerd to define a + * range if the character following it is either a close + * bracket (required by ANSI) or is not numerically greater + * than the character we just stored in the table (c). + */ + n = *fmt; + if (n == ']' || n < c) + { + c = '-'; + break; /* resume the for(;;) */ + } + fmt++; + do + { /* fill in the range */ + tab[++c] = v; + } + while (c < n); +#if 1 /* XXX another disgusting compatibility hack */ + /* + * Alas, the V7 Unix scanf also treats formats such + * as [a-c-e] as `the letters a through e'. This too + * is permitted by the standard.... + */ + goto doswitch; +#else + c = *fmt++; + if (c == 0) + return fmt - 1; + if (c == ']') + return fmt; +#endif + + break; + + + case ']': /* end of scanset */ + return fmt; + + default: /* just another character */ + c = n; + break; + } + } + /* NOTREACHED */ +} // __sccl() + +#endif // if defined(CYGPKG_LIBC) && defined(CYGPKG_LIBC_STDIO) && + // defined(CYGFUN_LIBC_STDIO_ungetc) + +// EOF vfscanf.cxx
