/* * Write `count' objects (each size `size') from memory to the given file. * Return the number of whole objects written. */ size_t fwrite(const void *buf, size_t size, size_t count, FILE *fp) { size_t n; struct __suio uio; struct __siov iov; int ret; /* * ANSI and SUSv2 require a return value of 0 if size or count are 0. */ if ((n = count * size) == 0) return (0); iov.iov_base = (void *)buf; uio.uio_resid = iov.iov_len = n; uio.uio_iov = &iov; uio.uio_iovcnt = 1; /* * The usual case is success (__sfvwrite returns 0); * skip the divide if this happens, since divides are * generally slow and since this occurs whenever size==0. */ FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); ret = __sfvwrite(fp, &uio); FUNLOCKFILE(fp); if (ret == 0) return (count); return ((n - uio.uio_resid) / size); }
/* * Write the given string to the given file. */ int fputs(const char *s, FILE *fp) { struct __suio uio; struct __siov iov; int r; _DIAGASSERT(s != NULL); _DIAGASSERT(fp != NULL); if(fp == NULL) { errno = EINVAL; return (EOF); } if (s == NULL) s = "(null)"; iov.iov_base = __UNCONST(s); uio.uio_resid = (int)(iov.iov_len = strlen(s)); uio.uio_iov = &iov; uio.uio_iovcnt = 1; FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); r = __sfvwrite(fp, &uio); FUNLOCKFILE(fp); return r; }
wint_t __ungetwc(wint_t wc, FILE *fp, locale_t locale) { struct wchar_io_data *wcio; char buf[MB_LEN_MAX]; size_t len; struct xlocale_ctype *l = XLOCALE_CTYPE(locale); if (wc == WEOF) return (WEOF); _SET_ORIENTATION(fp, 1); wcio = WCIO_GET(fp); if (wcio == 0) { errno = ENOMEM; /* XXX */ return (WEOF); } len = l->__wcrtomb(buf, wc, &wcio->wcio_mbstate_in); if (len == (size_t)-1) return (WEOF); while (len--) { if (__ungetc(buf[len], fp) == EOF) return (WEOF); } __sclearerr(fp); return wc; }
/* * Read at most n-1 characters from the given file. * Stop when a newline has been read, or the count runs out. * Return first argument, or NULL if no characters were read. * Do not return NULL if n == 1. */ char * fgets(char *buf, int n, FILE *fp) { size_t len; char *s; unsigned char *p, *t; if (n <= 0) /* sanity check */ return (NULL); FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); s = buf; n--; /* leave space for NUL */ while (n != 0) { /* * If the buffer is empty, refill it. */ if (fp->_r <= 0) { if (__srefill(fp)) { /* EOF/error: stop with partial or no line */ if (s == buf) { FUNLOCKFILE(fp); return (NULL); } break; } } len = fp->_r; p = fp->_p; /* * Scan through at most n bytes of the current buffer, * looking for '\n'. If found, copy up to and including * newline, and stop. Otherwise, copy entire chunk * and loop. */ if ((int)len > n) len = n; t = memchr((void *)p, '\n', len); if (t != NULL) { len = ++t - p; fp->_r -= len; fp->_p = t; (void)memcpy((void *)s, (void *)p, len); s[len] = '\0'; FUNLOCKFILE(fp); return (buf); } fp->_r -= len; fp->_p += len; (void)memcpy((void *)s, (void *)p, len); s += len; n -= len; } *s = '\0'; FUNLOCKFILE(fp); return (buf); }
int putc_unlocked(int c, FILE *fp) { if (cantwrite(fp)) { errno = EBADF; return (EOF); } _SET_ORIENTATION(fp, -1); return (__sputc(c, fp)); }
/* * Handle getc() when the buffer ran out: * Refill, then return the first character * in the newly-filled buffer. */ int __srget(FILE *fp) { _SET_ORIENTATION(fp, -1); if (__srefill(fp) == 0) { fp->_r--; return (*fp->_p++); } return (EOF); }
wint_t __fgetwc_unlock(FILE *fp) { struct wchar_io_data *wcio; mbstate_t *st; wchar_t wc; size_t size; _DIAGASSERT(fp != NULL); if(fp == NULL) { errno = ENOSTR; return WEOF; } _SET_ORIENTATION(fp, 1); wcio = WCIO_GET(fp); if (wcio == 0) { errno = ENOMEM; return WEOF; } /* if there're ungetwc'ed wchars, use them */ if (wcio->wcio_ungetwc_inbuf) { wc = wcio->wcio_ungetwc_buf[--wcio->wcio_ungetwc_inbuf]; return wc; } st = &wcio->wcio_mbstate_in; do { char c; int ch = __sgetc(fp); if (ch == EOF) { return WEOF; } c = (char)ch; size = mbrtowc(&wc, &c, 1, st); if (size == (size_t)-1) { errno = EILSEQ; fp->_flags |= __SERR; return WEOF; } } while (size == (size_t)-2); _DIAGASSERT(size == 1); return wc; }
/* * Handle getc() when the buffer ran out: * Refill, then return the first character * in the newly-filled buffer. */ int __srget(FILE *fp) { _DIAGASSERT(fp != NULL); if(fp != NULL) { _SET_ORIENTATION(fp, -1); if (__srefill(fp) == 0) { fp->_r--; return (*fp->_p++); } } return (EOF); }
/* * Write the given character into the (probably full) buffer for * the given file. Flush the buffer out if it is or becomes full, * or if c=='\n' and the file is line buffered. */ int __swbuf(int c, FILE *fp) { int n; //_DIAGASSERT(fp != NULL); if(fp == NULL) { errno = EINVAL; return (EOF); } _SET_ORIENTATION(fp, -1); /* * In case we cannot write, or longjmp takes us out early, * make sure _w is 0 (if fully- or un-buffered) or -_bf._size * (if line buffered) so that we will get called again. * If we did not do this, a sufficient number of putc() * calls might wrap _w from negative to positive. */ fp->_w = fp->_lbfsize; if (cantwrite(fp)) { errno = EBADF; return (EOF); } c = (unsigned char)c; /* * If it is completely full, flush it out. Then, in any case, * stuff c into the buffer. If this causes the buffer to fill * completely, or if c is '\n' and the file is line buffered, * flush it (perhaps a second time). The second flush will always * happen on unbuffered streams, where _bf._size==1; fflush() * guarantees that putc() will always call wbuf() by setting _w * to 0, so we need not do anything else. */ n = (int)(fp->_p - fp->_bf._base); if (n >= fp->_bf._size) { if (fflush(fp)) return (EOF); n = 0; } fp->_w--; *fp->_p++ = (unsigned char)c; if (++n == fp->_bf._size || (fp->_flags & __SLBF && c == '\n')) if (fflush(fp)) return (EOF); return (c); }
FILE * open_wmemstream(wchar_t **pbuf, size_t *psize) { struct state *st; FILE *fp; if (pbuf == NULL || psize == NULL) { errno = EINVAL; return (NULL); } if ((st = malloc(sizeof(*st))) == NULL) return (NULL); if ((fp = __sfp()) == NULL) { free(st); return (NULL); } st->size = BUFSIZ * sizeof(wchar_t); if ((st->string = calloc(1, st->size)) == NULL) { free(st); fp->_flags = 0; return (NULL); } *st->string = L'\0'; st->pos = 0; st->len = 0; st->pbuf = pbuf; st->psize = psize; bzero(&st->mbs, sizeof(st->mbs)); *pbuf = st->string; *psize = st->len; fp->_flags = __SWR; fp->_file = -1; fp->_cookie = st; fp->_read = NULL; fp->_write = wmemstream_write; fp->_seek = wmemstream_seek; fp->_close = wmemstream_close; _SET_ORIENTATION(fp, 1); return (fp); }
wint_t __fputwc_unlock(wchar_t wc, FILE *fp) { struct wchar_io_data *wcio; mbstate_t *st; size_t size; char buf[MB_LEN_MAX]; struct __suio uio; struct __siov iov; _DIAGASSERT(fp != NULL); if(fp == NULL) { errno = EINVAL; return (WEOF); } /* LINTED we don't play with buf */ iov.iov_base = (void *)buf; uio.uio_iov = &iov; uio.uio_iovcnt = 1; _SET_ORIENTATION(fp, 1); wcio = WCIO_GET(fp); if (wcio == 0) { errno = ENOMEM; return WEOF; } wcio->wcio_ungetwc_inbuf = 0; st = &wcio->wcio_mbstate_out; size = wcrtomb(buf, wc, st); if (size == (size_t)-1) { errno = EILSEQ; return WEOF; } _DIAGASSERT(size != 0); uio.uio_resid = (int)(iov.iov_len = size); if (__sfvwrite(fp, &uio)) { return WEOF; } return (wint_t)wc; }
/* * Write the given string to the given file. */ int fputs(const char *s, FILE *fp) { struct __suio uio; struct __siov iov; int ret; iov.iov_base = (void *)s; iov.iov_len = uio.uio_resid = strlen(s); uio.uio_iov = &iov; uio.uio_iovcnt = 1; FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); ret = __sfvwrite(fp, &uio); FUNLOCKFILE(fp); return (ret); }
/* * Write the given string to stdout, appending a newline. */ int puts(const char *s) { size_t c = strlen(s); struct __suio uio; struct __siov iov[2]; int ret; iov[0].iov_base = (void *)s; iov[0].iov_len = c; iov[1].iov_base = "\n"; iov[1].iov_len = 1; uio.uio_resid = c + 1; uio.uio_iov = &iov[0]; uio.uio_iovcnt = 2; FLOCKFILE(stdout); _SET_ORIENTATION(stdout, -1); ret = __sfvwrite(stdout, &uio); FUNLOCKFILE(stdout); return (ret ? EOF : '\n'); }
size_t __sfread(void *buf, size_t size, size_t count, FILE *fp) { size_t resid; char *p; int r; size_t total; /* * ANSI and SUSv2 require a return value of 0 if size or count are 0. */ if ((resid = count * size) == 0) return (0); /*FLOCKFILE(fp);*/ _SET_ORIENTATION(fp, -1); if (fp->_r < 0) fp->_r = 0; total = resid; p = buf; while (resid > (r = fp->_r)) { (void)memcpy((void *)p, (void *)fp->_p, (size_t)r); fp->_p += r; /* fp->_r = 0 ... done in __srefill */ p += r; resid -= r; if (__srefill(fp)) { /* no more input: return partial result */ /*FUNLOCKFILE(fp);*/ return ((total - resid) / size); } } (void)memcpy((void *)p, (void *)fp->_p, resid); fp->_r -= resid; fp->_p += resid; /*FUNLOCKFILE(fp);*/ return (count); }
wint_t ungetwc(wint_t wc, FILE *fp) { struct wchar_io_data *wcio; _DIAGASSERT(fp); if (wc == WEOF) return WEOF; FLOCKFILE(fp); _SET_ORIENTATION(fp, 1); /* * XXX since we have no way to transform a wchar string to * a char string in reverse order, we can't use ungetc. */ /* XXX should we flush ungetc buffer? */ wcio = WCIO_GET(fp); if (wcio == 0) { FUNLOCKFILE(fp); errno = ENOMEM; /* XXX */ return WEOF; } if (wcio->wcio_ungetwc_inbuf >= WCIO_UNGETWC_BUFSIZE) { FUNLOCKFILE(fp); return WEOF; } wcio->wcio_ungetwc_buf[wcio->wcio_ungetwc_inbuf++] = (wchar_t)wc; __sclearerr(fp); FUNLOCKFILE(fp); return wc; }
/* * __svfscanf_unlocked - non-MT-safe version of __svfscanf */ int __svfscanf_unlocked(FILE *fp, const char *fmt0, va_list ap) { const u_char *fmt = (const 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 */ size_t n; /* handy size_t */ int flags; /* flags as defined above */ char *p0; /* saves original value of p when necessary */ int nassigned; /* number of fields assigned */ int nconversions; /* number of conversions */ int nread; /* number of characters consumed from fp */ int base; /* base argument to conversion function */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric and mb conversions */ wchar_t *wcp; /* handy wide character pointer */ size_t nconv; /* length of multibyte sequence converted */ static const mbstate_t initial = { 0 }; mbstate_t mbs; /* `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 }; _DIAGASSERT(fp != NULL); _DIAGASSERT(fmt0 != NULL); if(fp == NULL) { errno = EINVAL; return (EOF); } _SET_ORIENTATION(fp, -1); //Print(L"%a( %d, \"%a\", ...)\n", __func__, fp->_file, fmt0); nassigned = 0; nconversions = 0; nread = 0; base = 0; for (;;) { c = (unsigned char)*fmt++; if (c == 0) return (nassigned); if (isspace(c)) { while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) nread++, fp->_r--, fp->_p++; continue; } //Print(L"%a: %d\n", __func__, __LINE__); if (c != '%') goto literal; width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; //Print(L"%a: %d\n", __func__, __LINE__); switch (c) { case '%': literal: //Print(L"%a: %d\n", __func__, __LINE__); if (fp->_r <= 0 && __srefill(fp)) goto input_failure; if (*fp->_p != c) goto match_failure; fp->_r--, fp->_p++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'j': flags |= INTMAXT; goto again; case 'l': if (flags & LONG) { flags &= ~LONG; flags |= LONGLONG; } else flags |= LONG; goto again; case 'q': flags |= LONGLONG; /* not quite */ goto again; case 't': flags |= PTRDIFFT; goto again; case 'z': flags |= SIZET; goto again; case 'L': flags |= LONGDBL; goto again; case 'h': if (flags & SHORT) { flags &= ~SHORT; flags |= SHORTSHORT; } else 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. */ case 'd': c = CT_INT; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; #ifndef NO_FLOATING_POINT case 'A': case 'E': case 'F': case 'G': case 'a': case 'e': case 'f': case 'g': c = CT_FLOAT; break; #endif case 'S': flags |= LONG; /* FALLTHROUGH */ case 's': c = CT_STRING; break; case '[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case 'C': flags |= LONG; /* FALLTHROUGH */ case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; /* assumes sizeof(uintmax_t) */ flags |= UNSIGNED; /* >= sizeof(uintptr_t) */ base = 16; break; case 'n': nconversions++; if (flags & SUPPRESS) /* ??? */ continue; if (flags & SHORTSHORT) *va_arg(ap, char *) = (char)nread; else if (flags & SHORT) *va_arg(ap, short *) = (short)nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else if (flags & LONGLONG) *va_arg(ap, long long *) = nread; else if (flags & INTMAXT) *va_arg(ap, intmax_t *) = nread; else if (flags & SIZET) *va_arg(ap, size_t *) = nread; else if (flags & PTRDIFFT) *va_arg(ap, ptrdiff_t *) = nread; else *va_arg(ap, int *) = nread; continue; default: goto match_failure; /* * Disgusting backwards compatibility hack. XXX */ case '\0': /* compat */ return (EOF); } //Print(L"%a: %d\n", __func__, __LINE__); /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) { //Print(L"%a: %d\n", __func__, __LINE__); goto input_failure; } /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*fp->_p)) { nread++; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) { //Print(L"%a: %d\n", __func__, __LINE__); 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. */ //Print(L"%a: %d\n", __func__, __LINE__); switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) width = 1; if (flags & LONG) { if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else wcp = NULL; n = 0; while (width != 0) { if (n == MB_CUR_MAX) { fp->_flags |= __SERR; goto input_failure; } buf[n++] = *fp->_p; fp->_p++; fp->_r--; mbs = initial; nconv = mbrtowc(wcp, buf, n, &mbs); if (nconv == (size_t)-1) { fp->_flags |= __SERR; goto input_failure; } if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0'; if (nconv != (size_t)-2) { nread += (int)n; width--; if (!(flags & SUPPRESS)) wcp++; n = 0; } if (fp->_r <= 0 && __srefill(fp)) { if (n != 0) { fp->_flags |= __SERR; goto input_failure; } break; } } if (!(flags & SUPPRESS)) nassigned++; } else if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = fp->_r) < width) { sum += n; width -= n; fp->_p += n; if (__srefill(fp)) { if (sum == 0) goto input_failure; break; } } else { sum += width; fp->_r -= (int)width; fp->_p += width; break; } } nread += (int)sum; } else { size_t r = fread(va_arg(ap, char *), 1, width, fp); if (r == 0) goto input_failure; nread += (int)r; nassigned++; } nconversions++; break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = (size_t)~0; /* `infinity' */ /* take only those things in the class */ if (flags & LONG) { wchar_t twc; int nchars; if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else
int __sungetc(int c, FILE *fp) { if (c == EOF) return (EOF); if (!__sdidinit) __sinit(); /*FLOCKFILE(fp);*/ _SET_ORIENTATION(fp, -1); if ((fp->_flags & __SRD) == 0) { /* * Not already reading: no good unless reading-and-writing. * Otherwise, flush any current write stuff. */ if ((fp->_flags & __SRW) == 0) { error: /*FUNLOCKFILE(fp);*/ return (EOF); } if (fp->_flags & __SWR) { if (__sflush(fp)) goto error; fp->_flags &= ~__SWR; fp->_w = 0; fp->_lbfsize = 0; } fp->_flags |= __SRD; } c = (unsigned char)c; /* * If we are in the middle of ungetc'ing, just continue. * This may require expanding the current ungetc buffer. */ if (HASUB(fp)) { if (fp->_r >= _UB(fp)._size && __submore(fp)) goto error; *--fp->_p = c; inc_ret: fp->_r++; /*FUNLOCKFILE(fp);*/ return (c); } fp->_flags &= ~__SEOF; /* * If we can handle this by simply backing up, do so, * but never replace the original character. * (This makes sscanf() work when scanning `const' data.) */ if (fp->_bf._base != NULL && fp->_p > fp->_bf._base && fp->_p[-1] == c) { fp->_p--; goto inc_ret; } /* * Create an ungetc buffer. * Initially, we will use the `reserve' buffer. */ fp->_ur = fp->_r; fp->_up = fp->_p; _UB(fp)._base = fp->_ubuf; _UB(fp)._size = sizeof(fp->_ubuf); fp->_ubuf[sizeof(fp->_ubuf) - 1] = c; fp->_p = &fp->_ubuf[sizeof(fp->_ubuf) - 1]; fp->_r = 1; /*FUNLOCKFILE(fp);*/ return (c); }
int __vfprintf(FILE *fp, const char *fmt0, __va_list ap) { char *fmt; /* format string */ int ch; /* character from fmt */ int n, n2; /* handy integers (short term usage) */ char *cp; /* handy char pointer (short term usage) */ struct __siov *iovp; /* for PRINT macro */ int flags; /* flags as above */ int ret; /* return value accumulator */ int width; /* width from format (%8d), or 0 */ int prec; /* precision from format; <0 for N/A */ char sign; /* sign prefix (' ', '+', '-', or \0) */ wchar_t wc; mbstate_t ps; #ifdef FLOATING_POINT /* * We can decompose the printed representation of floating * point numbers into several parts, some of which may be empty: * * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ * A B ---C--- D E F * * A: 'sign' holds this value if present; '\0' otherwise * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal * C: cp points to the string MMMNNN. Leading and trailing * zeros are not in the string and must be added. * D: expchar holds this character; '\0' if no exponent, e.g. %f * F: at least two digits for decimal, at least one digit for hex */ char *decimal_point = NULL; int signflag; /* true if float is negative */ union { /* floating point arguments %[aAeEfFgG] */ double dbl; long double ldbl; } fparg; int expt; /* integer value of exponent */ char expchar; /* exponent character: [eEpP\0] */ char *dtoaend; /* pointer to end of converted digits */ int expsize; /* character count for expstr */ int lead; /* sig figs before decimal or group sep */ int ndig; /* actual number of digits returned by dtoa */ char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ char *dtoaresult = NULL; #endif uintmax_t _umax; /* integer arguments %[diouxX] */ enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */ int dprec; /* a copy of prec if %[diouxX], 0 otherwise */ int realsz; /* field size expanded by dprec */ int size; /* size of converted field or string */ const char *xdigs; /* digits for %[xX] conversion */ #define NIOV 8 struct __suio uio; /* output information: summary */ struct __siov iov[NIOV];/* ... and individual io vectors */ char buf[BUF]; /* buffer with space for digits of uintmax_t */ char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ union arg *argtable; /* args, built due to positional arg */ union arg statargtable[STATIC_ARG_TBL_SIZE]; size_t argtablesiz; int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ #ifdef PRINTF_WIDE_CHAR char *convbuf; /* buffer for wide to multi-byte conversion */ #endif /* * Choose PADSIZE to trade efficiency vs. size. If larger printf * fields occur frequently, increase PADSIZE and make the initialisers * below longer. */ #define PADSIZE 16 /* pad chunk size */ static char blanks[PADSIZE] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; static char zeroes[PADSIZE] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; static const char xdigs_lower[16] = "0123456789abcdef"; static const char xdigs_upper[16] = "0123456789ABCDEF"; /* * BEWARE, these `goto error' on error, and PAD uses `n'. */ #define PRINT(ptr, len) do { \ iovp->iov_base = (ptr); \ iovp->iov_len = (len); \ uio.uio_resid += (len); \ iovp++; \ if (++uio.uio_iovcnt >= NIOV) { \ if (__sprint(fp, &uio)) \ goto error; \ iovp = iov; \ } \ } while (0) #define PAD(howmany, with) do { \ if ((n = (howmany)) > 0) { \ while (n > PADSIZE) { \ PRINT(with, PADSIZE); \ n -= PADSIZE; \ } \ PRINT(with, n); \ } \ } while (0) #define PRINTANDPAD(p, ep, len, with) do { \ n2 = (ep) - (p); \ if (n2 > (len)) \ n2 = (len); \ if (n2 > 0) \ PRINT((p), n2); \ PAD((len) - (n2 > 0 ? n2 : 0), (with)); \ } while(0) #define FLUSH() do { \ if (uio.uio_resid && __sprint(fp, &uio)) \ goto error; \ uio.uio_iovcnt = 0; \ iovp = iov; \ } while (0) /* * To extend shorts properly, we need both signed and unsigned * argument extraction methods. */ #define SARG() \ ((intmax_t)(flags&MAXINT ? GETARG(intmax_t) : \ flags&LLONGINT ? GETARG(long long) : \ flags&LONGINT ? GETARG(long) : \ flags&PTRINT ? GETARG(ptrdiff_t) : \ flags&SIZEINT ? GETARG(ssize_t) : \ flags&SHORTINT ? (short)GETARG(int) : \ flags&CHARINT ? (__signed char)GETARG(int) : \ GETARG(int))) #define UARG() \ ((uintmax_t)(flags&MAXINT ? GETARG(uintmax_t) : \ flags&LLONGINT ? GETARG(unsigned long long) : \ flags&LONGINT ? GETARG(unsigned long) : \ flags&PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \ flags&SIZEINT ? GETARG(size_t) : \ flags&SHORTINT ? (unsigned short)GETARG(int) : \ flags&CHARINT ? (unsigned char)GETARG(int) : \ GETARG(unsigned int))) /* * Append a digit to a value and check for overflow. */ #define APPEND_DIGIT(val, dig) do { \ if ((val) > INT_MAX / 10) \ goto overflow; \ (val) *= 10; \ if ((val) > INT_MAX - to_digit((dig))) \ goto overflow; \ (val) += to_digit((dig)); \ } while (0) /* * Get * arguments, including the form *nn$. Preserve the nextarg * that the argument can be gotten once the type is determined. */ #define GETASTER(val) \ n2 = 0; \ cp = fmt; \ while (is_digit(*cp)) { \ APPEND_DIGIT(n2, *cp); \ cp++; \ } \ if (*cp == '$') { \ int hold = nextarg; \ if (argtable == NULL) { \ argtable = statargtable; \ __find_arguments(fmt0, orgap, &argtable, &argtablesiz); \ } \ nextarg = n2; \ val = GETARG(int); \ nextarg = hold; \ fmt = ++cp; \ } else { \ val = GETARG(int); \ } /* * Get the argument indexed by nextarg. If the argument table is * built, use it to get the argument. If its not, get the next * argument (and arguments must be gotten sequentially). */ #define GETARG(type) \ ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ (nextarg++, va_arg(ap, type))) _SET_ORIENTATION(fp, -1); /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ if (cantwrite(fp)) { errno = EBADF; return (EOF); } /* optimise fprintf(stderr) (and other unbuffered Unix files) */ if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && fp->_file >= 0) return (__sbprintf(fp, fmt0, ap)); fmt = (char *)fmt0; argtable = NULL; nextarg = 1; va_copy(orgap, ap); uio.uio_iov = iovp = iov; uio.uio_resid = 0; uio.uio_iovcnt = 0; ret = 0; #ifdef PRINTF_WIDE_CHAR convbuf = NULL; #endif memset(&ps, 0, sizeof(ps)); /* * Scan the format for conversions (`%' character). */ for (;;) { cp = fmt; while ((n = mbrtowc(&wc, fmt, MB_CUR_MAX, &ps)) > 0) { fmt += n; if (wc == '%') { fmt--; break; } } if (fmt != cp) { ptrdiff_t m = fmt - cp; if (m < 0 || m > INT_MAX - ret) goto overflow; PRINT(cp, m); ret += m; } if (n <= 0) goto done; fmt++; /* skip over '%' */ flags = 0; dprec = 0; width = 0; prec = -1; sign = '\0'; ox[1] = '\0'; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': /* * ``If the space and + flags both appear, the space * flag will be ignored.'' * -- ANSI X3J11 */ if (!sign) sign = ' '; goto rflag; case '#': flags |= ALT; goto rflag; case '\'': /* grouping not implemented */ goto rflag; case '*': /* * ``A negative field width argument is taken as a * - flag followed by a positive field width.'' * -- ANSI X3J11 * They don't exclude field widths read from args. */ GETASTER(width); if (width >= 0) goto rflag; if (width == INT_MIN) goto overflow; width = -width; /* FALLTHROUGH */ case '-': flags |= LADJUST; goto rflag; case '+': sign = '+'; goto rflag; case '.': if ((ch = *fmt++) == '*') { GETASTER(n); prec = n < 0 ? -1 : n; goto rflag; } n = 0; while (is_digit(ch)) { APPEND_DIGIT(n, ch); ch = *fmt++; } if (ch == '$') { nextarg = n; if (argtable == NULL) { argtable = statargtable; __find_arguments(fmt0, orgap, &argtable, &argtablesiz); } goto rflag; } prec = n; goto reswitch; case '0': /* * ``Note that 0 is taken as a flag, not as the * beginning of a field width.'' * -- ANSI X3J11 */ flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { APPEND_DIGIT(n, ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { nextarg = n; if (argtable == NULL) { argtable = statargtable; __find_arguments(fmt0, orgap, &argtable, &argtablesiz); } goto rflag; } width = n; goto reswitch; #ifdef FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (*fmt == 'h') { fmt++; flags |= CHARINT; } else { flags |= SHORTINT; } goto rflag; case 'j': flags |= MAXINT; goto rflag; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONGINT; } else { flags |= LONGINT; } goto rflag; case 'q': flags |= LLONGINT; goto rflag; case 't': flags |= PTRINT; goto rflag; case 'z': flags |= SIZEINT; goto rflag; case 'c': #ifdef PRINTF_WIDE_CHAR if (flags & LONGINT) { mbstate_t mbs; size_t mbseqlen; memset(&mbs, 0, sizeof(mbs)); mbseqlen = wcrtomb(buf, (wchar_t)GETARG(wint_t), &mbs); if (mbseqlen == (size_t)-1) { fp->_flags |= __SERR; errno = EILSEQ; goto error; } cp = buf; size = (int)mbseqlen; } else { #endif *(cp = buf) = GETARG(int); size = 1; #ifdef PRINTF_WIDE_CHAR } #endif sign = '\0'; break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': _umax = SARG(); if ((intmax_t)_umax < 0) { _umax = -_umax; sign = '-'; } base = DEC; goto number; #ifdef FLOATING_POINT case 'a': case 'A': if (ch == 'a') { ox[1] = 'x'; xdigs = xdigs_lower; expchar = 'p'; } else { ox[1] = 'X'; xdigs = xdigs_upper; expchar = 'P'; } if (prec >= 0) prec++; if (dtoaresult) __freedtoa(dtoaresult); if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = cp = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } else { fparg.dbl = GETARG(double); dtoaresult = cp = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } if (prec < 0) prec = dtoaend - cp; if (expt == INT_MAX) ox[1] = '\0'; goto fp_common; case 'e': case 'E': expchar = ch; if (prec < 0) /* account for digit before decpt */ prec = DEFPREC + 1; else prec++; goto fp_begin; case 'f': case 'F': expchar = '\0'; goto fp_begin; case 'g': case 'G': expchar = ch - ('g' - 'e'); if (prec == 0) prec = 1; fp_begin: if (prec < 0) prec = DEFPREC; if (dtoaresult) __freedtoa(dtoaresult); if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = cp = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } else {
/* * Internal, unlocked version of vfscanf */ int __svfscanf(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; /* base argument to strtoimax/strtouimax */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ #ifdef SCANF_WIDE_CHAR wchar_t *wcp; /* handy wide character pointer */ size_t nconv; /* length of multibyte sequence converted */ mbstate_t mbs; #endif /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; _SET_ORIENTATION(fp, -1); nassigned = 0; nread = 0; base = 0; /* XXX just to keep gcc happy */ for (;;) { c = *fmt++; if (c == 0) return (nassigned); if (isspace(c)) { while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) nread++, fp->_r--, fp->_p++; 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 (fp->_r <= 0 && __srefill(fp)) goto input_failure; if (*fp->_p != c) goto match_failure; fp->_r--, fp->_p++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'j': flags |= MAXINT; goto again; case 'L': flags |= LONGDBL; goto again; case 'h': if (*fmt == 'h') { fmt++; flags |= SHORTSHORT; } else { flags |= SHORT; } goto again; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONG; } else { flags |= LONG; } goto again; case 'q': flags |= LLONG; /* deprecated */ goto again; case 't': flags |= PTRINT; goto again; case 'z': flags |= SIZEINT; 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; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; #ifdef FLOATING_POINT case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': c = CT_FLOAT; break; #endif 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; flags |= UNSIGNED; base = 16; break; case 'n': if (flags & SUPPRESS) continue; if (flags & SHORTSHORT) *va_arg(ap, signed char *) = nread; else if (flags & SHORT) *va_arg(ap, short *) = nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = nread; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = nread; else if (flags & LLONG) *va_arg(ap, long long *) = nread; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = nread; else *va_arg(ap, int *) = nread; continue; /* * Disgusting backwards compatibility hacks. XXX */ case '\0': /* compat */ return (EOF); default: /* compat */ if (isupper(c)) flags |= LONG; c = CT_INT; base = 10; break; } /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) goto input_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*fp->_p)) { nread++; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) 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; #ifdef SCANF_WIDE_CHAR if (flags & LONG) { if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else wcp = NULL; n = 0; while (width != 0) { if (n == MB_CUR_MAX) { fp->_flags |= __SERR; goto input_failure; } buf[n++] = *fp->_p; fp->_p++; fp->_r--; bzero(&mbs, sizeof(mbs)); nconv = mbrtowc(wcp, buf, n, &mbs); if (nconv == (size_t)-1) { fp->_flags |= __SERR; goto input_failure; } if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0'; if (nconv != (size_t)-2) { nread += n; width--; if (!(flags & SUPPRESS)) wcp++; n = 0; } if (fp->_r <= 0 && __srefill(fp)) { if (n != 0) { fp->_flags |= __SERR; goto input_failure; } break; } } if (!(flags & SUPPRESS)) nassigned++; } else #endif /* SCANF_WIDE_CHAR */ if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = fp->_r) < width) { sum += n; width -= n; fp->_p += n; if (__srefill(fp)) { if (sum == 0) goto input_failure; break; } } else { sum += width; fp->_r -= width; fp->_p += width; break; } } nread += sum; } else { size_t r = fread((void *)va_arg(ap, char *), 1, width, fp); if (r == 0) goto input_failure; nread += r; nassigned++; } break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = (size_t)~0; /* `infinity' */ #ifdef SCANF_WIDE_CHAR /* take only those things in the class */ if (flags & LONG) { wchar_t twc; int nchars; if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else
/* * vfscanf */ 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; /* base argument to strtoimax/strtouimax */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); nassigned = 0; nread = 0; base = 0; /* XXX just to keep gcc happy */ for (;;) { c = *fmt++; if (c == 0) { FUNLOCKFILE(fp); return (nassigned); } if (isspace(c)) { while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) nread++, fp->_r--, fp->_p++; 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 (fp->_r <= 0 && __srefill(fp)) goto input_failure; if (*fp->_p != c) goto match_failure; fp->_r--, fp->_p++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'j': flags |= MAXINT; goto again; case 'L': flags |= (*fmt == 'd') ? LLONG : (*fmt == 'i') ? LLONG : (*fmt == 'o') ? LLONG : (*fmt == 'u') ? LLONG : (*fmt == 'x') ? LLONG : LONGDBL; goto again; case 'h': if (*fmt == 'h') { fmt++; flags |= SHORTSHORT; } else { flags |= SHORT; } goto again; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONG; } else { flags |= LONG; } goto again; case 'q': flags |= LLONG; /* deprecated */ goto again; case 't': flags |= PTRINT; goto again; case 'z': flags |= SIZEINT; 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; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; #ifdef FLOATING_POINT case 'E': case 'G': case 'e': case 'f': case 'g': c = CT_FLOAT; break; #endif 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; flags |= UNSIGNED; base = 16; break; case 'n': if (flags & SUPPRESS) continue; if (flags & SHORTSHORT) *va_arg(ap, __signed char *) = nread; else if (flags & SHORT) *va_arg(ap, short *) = nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = nread; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = nread; else if (flags & LLONG) *va_arg(ap, long long *) = nread; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = nread; else *va_arg(ap, int *) = nread; continue; /* * Disgusting backwards compatibility hacks. XXX */ case '\0': /* compat */ FUNLOCKFILE(fp); return (EOF); default: /* compat */ if (isupper(c)) flags |= LONG; c = CT_INT; base = 10; break; } /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) goto input_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*fp->_p)) { nread++; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) 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 = fp->_r) < (int)width) { sum += n; width -= n; fp->_p += n; if (__srefill(fp)) { if (sum == 0) goto input_failure; break; } } else { sum += width; fp->_r -= width; fp->_p += width; break; } } nread += sum; } else { size_t r = fread((void *)va_arg(ap, char *), 1, width, fp); if (r == 0) goto input_failure; nread += r; nassigned++; } break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = (size_t)~0; /* `infinity' */ /* take only those things in the class */ if (flags & SUPPRESS) { n = 0; while (ccltab[*fp->_p]) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { if (n == 0) goto input_failure; break; } } if (n == 0) goto match_failure; } else { p0 = p = va_arg(ap, char *); while (ccltab[*fp->_p]) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { 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 = (size_t)~0; if (flags & SUPPRESS) { n = 0; while (!isspace(*fp->_p)) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } nread += n; } else { p0 = p = va_arg(ap, char *); while (!isspace(*fp->_p)) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } *p = '\0'; nread += p - p0; nassigned++; } continue; case CT_INT: /* scan an integer as if by strtoimax/strtoumax */ #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 = *fp->_p; /* * 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; flags |= HAVESIGN; goto ok; } break; /* * x ok iff flag still set and 2nd char (or * 3rd char if we have a sign). */ case 'x': case 'X': if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) { 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; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) 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) (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) { uintmax_t res; *p = '\0'; if (flags & UNSIGNED) res = strtoumax(buf, NULL, base); else res = strtoimax(buf, NULL, base); if (flags & POINTER) *va_arg(ap, void **) = (void *)(uintptr_t)res; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = res; else if (flags & LLONG) *va_arg(ap, long long *) = res; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = res; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = res; else if (flags & LONG) *va_arg(ap, long *) = res; else if (flags & SHORT) *va_arg(ap, short *) = res; else if (flags & SHORTSHORT) *va_arg(ap, __signed char *) = res; else *va_arg(ap, int *) = res; nassigned++; } nread += p - buf; break; #ifdef FLOATING_POINT 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 flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; for (p = buf; width; width--) { c = *fp->_p; /* * 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; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) 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') { (void) ungetc(c, fp);/* sign */ c = *(u_char *)--p; } (void) ungetc(c, fp); } if ((flags & SUPPRESS) == 0) { double res; *p = '\0'; res = strtod(buf, (char **) NULL); if (flags & LONGDBL) *va_arg(ap, long double *) = res; else if (flags & LONG) *va_arg(ap, double *) = res; else *va_arg(ap, float *) = res; nassigned++; } nread += p - buf; break; #endif /* FLOATING_POINT */ } } input_failure: if (nassigned == 0) nassigned = -1; match_failure: FUNLOCKFILE(fp); return (nassigned); }
/* * Non-MT-safe version */ int __vfprintf(FILE *fp, locale_t locale, const char *fmt0, __va_list ap) { char *fmt; /* format string */ int ch; /* character from fmt */ int n, n2; /* handy integers (short term usage) */ char *cp; /* handy char pointer (short term usage) */ int flags; /* flags as above */ int ret; /* return value accumulator */ int width; /* width from format (%8d), or 0 */ int prec; /* precision from format; <0 for N/A */ char sign; /* sign prefix (' ', '+', '-', or \0) */ struct grouping_state gs; /* thousands' grouping info */ #ifdef FLOATING_POINT /* * We can decompose the printed representation of floating * point numbers into several parts, some of which may be empty: * * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ * A B ---C--- D E F * * A: 'sign' holds this value if present; '\0' otherwise * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal * C: cp points to the string MMMNNN. Leading and trailing * zeros are not in the string and must be added. * D: expchar holds this character; '\0' if no exponent, e.g. %f * F: at least two digits for decimal, at least one digit for hex */ char *decimal_point = NULL; /* locale specific decimal point */ int decpt_len; /* length of decimal_point */ int signflag; /* true if float is negative */ union { /* floating point arguments %[aAeEfFgG] */ double dbl; long double ldbl; } fparg; int expt; /* integer value of exponent */ char expchar; /* exponent character: [eEpP\0] */ char *dtoaend; /* pointer to end of converted digits */ int expsize; /* character count for expstr */ int ndig; /* actual number of digits returned by dtoa */ char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */ char *dtoaresult = NULL; /* buffer allocated by dtoa */ #endif u_long ulval; /* integer arguments %[diouxX] */ uintmax_t ujval; /* %j, %ll, %q, %t, %z integers */ int base; /* base for [diouxX] conversion */ int dprec; /* a copy of prec if [diouxX], 0 otherwise */ int realsz; /* field size expanded by dprec, sign, etc */ int size; /* size of converted field or string */ const char *xdigs; /* digits for %[xX] conversion */ struct io_state io; /* I/O buffering state */ char buf[BUF]; /* buffer with space for digits of uintmax_t */ char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ union arg *argtable; /* args, built due to positional arg */ union arg statargtable [STATIC_ARG_TBL_SIZE]; int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ char *convbuf; /* wide to multibyte conversion result */ static const char xdigs_lower[16] = "0123456789abcdef"; static const char xdigs_upper[16] = "0123456789ABCDEF"; /* BEWARE, these `goto error' on error. */ #define PRINT(ptr, len) { \ if (io_print(&io, (ptr), (len), locale)) \ goto error; \ } #define PAD(howmany, with) { \ if (io_pad(&io, (howmany), (with), locale)) \ goto error; \ } #define PRINTANDPAD(p, ep, len, with) { \ if (io_printandpad(&io, (p), (ep), (len), (with), locale)) \ goto error; \ } #define FLUSH() { \ if (io_flush(&io, locale)) \ goto error; \ } /* * Get the argument indexed by nextarg. If the argument table is * built, use it to get the argument. If its not, get the next * argument (and arguments must be gotten sequentially). */ #define GETARG(type) \ ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ (nextarg++, va_arg(ap, type))) /* * To extend shorts properly, we need both signed and unsigned * argument extraction methods. */ #define SARG() \ (flags&LONGINT ? GETARG(long) : \ flags&SHORTINT ? (long)(short)GETARG(int) : \ flags&CHARINT ? (long)(signed char)GETARG(int) : \ (long)GETARG(int)) #define UARG() \ (flags&LONGINT ? GETARG(u_long) : \ flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ (u_long)GETARG(u_int)) #define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT) #define SJARG() \ (flags&INTMAXT ? GETARG(intmax_t) : \ flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ (intmax_t)GETARG(long long)) #define UJARG() \ (flags&INTMAXT ? GETARG(uintmax_t) : \ flags&SIZET ? (uintmax_t)GETARG(size_t) : \ flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ (uintmax_t)GETARG(unsigned long long)) /* * Append a digit to a value and check for overflow. */ #define APPEND_DIGIT(val, dig) do { \ if ((val) > INT_MAX / 10) \ goto overflow; \ (val) *= 10; \ if ((val) > INT_MAX - to_digit((dig))) \ goto overflow; \ (val) += to_digit((dig)); \ } while (0) /* * Get * arguments, including the form *nn$. Preserve the nextarg * that the argument can be gotten once the type is determined. */ #define GETASTER(val) \ n2 = 0; \ cp = fmt; \ while (is_digit(*cp)) { \ APPEND_DIGIT(n2, *cp); \ cp++; \ } \ if (*cp == '$') { \ int hold = nextarg; \ if (argtable == NULL) { \ argtable = statargtable; \ if (__find_arguments (fmt0, orgap, &argtable)) { \ ret = EOF; \ goto error; \ } \ } \ nextarg = n2; \ val = GETARG(int); \ nextarg = hold; \ fmt = ++cp; \ } else { \ val = GETARG(int); \ } _SET_ORIENTATION(fp, -1); /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ if (cantwrite(fp)) { errno = EBADF; return (EOF); } fmt = (char *)fmt0; argtable = NULL; nextarg = 1; va_copy(orgap, ap); io_init(&io, fp); ret = 0; #ifdef FLOATING_POINT decimal_point = localeconv_l(locale)->decimal_point; /* The overwhelmingly common case is decpt_len == 1. */ decpt_len = (decimal_point[1] == '\0' ? 1 : strlen(decimal_point)); #endif #ifdef PRINTF_WIDE_CHAR convbuf = NULL; #endif /* * Scan the format for conversions (`%' character). */ for (;;) { for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) /* void */; if (fmt != cp) { ptrdiff_t m = fmt - cp; if (m < 0 || m > INT_MAX - ret) goto overflow; PRINT(cp, m); ret += m; } if (ch == '\0') goto done; fmt++; /* skip over '%' */ flags = 0; dprec = 0; width = 0; prec = -1; gs.grouping = NULL; sign = '\0'; ox[1] = '\0'; rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': /* * ``If the space and + flags both appear, the space * flag will be ignored.'' * -- ANSI X3J11 */ if (!sign) sign = ' '; goto rflag; case '#': flags |= ALT; goto rflag; case '*': /* * ``A negative field width argument is taken as a * - flag followed by a positive field width.'' * -- ANSI X3J11 * They don't exclude field widths read from args. */ GETASTER(width); if (width >= 0) goto rflag; if (width == INT_MIN) goto overflow; width = -width; /* FALLTHROUGH */ case '-': flags |= LADJUST; goto rflag; case '+': sign = '+'; goto rflag; case '\'': flags |= GROUPING; goto rflag; case '.': if ((ch = *fmt++) == '*') { GETASTER(n); prec = n < 0 ? -1 : n; goto rflag; } n = 0; while (is_digit(ch)) { APPEND_DIGIT(n, ch); ch = *fmt++; } if (ch == '$') { nextarg = n; if (argtable == NULL) { argtable = statargtable; if (__find_arguments (fmt0, orgap, &argtable)) { ret = EOF; goto error; } } goto rflag; } prec = n; goto reswitch; case '0': /* * ``Note that 0 is taken as a flag, not as the * beginning of a field width.'' * -- ANSI X3J11 */ flags |= ZEROPAD; goto rflag; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; do { APPEND_DIGIT(n, ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { nextarg = n; if (argtable == NULL) { argtable = statargtable; if (__find_arguments (fmt0, orgap, &argtable)) { ret = EOF; goto error; } } goto rflag; } width = n; goto reswitch; #ifdef FLOATING_POINT case 'L': flags |= LONGDBL; goto rflag; #endif case 'h': if (flags & SHORTINT) { flags &= ~SHORTINT; flags |= CHARINT; } else { flags |= SHORTINT; } goto rflag; case 'j': flags |= INTMAXT; goto rflag; case 'l': if (flags & LONGINT) { flags &= ~LONGINT; flags |= LLONGINT; } else { flags |= LONGINT; } goto rflag; case 'q': flags |= LLONGINT; /* not necessarily */ goto rflag; case 't': flags |= PTRDIFFT; goto rflag; case 'z': flags |= SIZET; goto rflag; case 'C': flags |= LONGINT; /*FALLTHROUGH*/ case 'c': #ifdef PRINTF_WIDE_CHAR if (flags & LONGINT) { mbstate_t mbs; size_t mbseqlen; memset(&mbs, 0, sizeof(mbs)); mbseqlen = wcrtomb(buf, (wchar_t)GETARG(wint_t), &mbs); if (mbseqlen == (size_t)-1) { fp->_flags |= __SERR; errno = EILSEQ; goto error; } cp = buf; size = (int)mbseqlen; } else { #endif *(cp = buf) = GETARG(int); size = 1; #ifdef PRINTF_WIDE_CHAR } #endif sign = '\0'; break; case 'D': flags |= LONGINT; /*FALLTHROUGH*/ case 'd': case 'i': if (flags & INTMAX_SIZE) { ujval = SJARG(); if ((intmax_t)ujval < 0) { ujval = -ujval; sign = '-'; } } else { ulval = SARG(); if ((long)ulval < 0) { ulval = -ulval; sign = '-'; } } base = 10; goto number; #ifdef FLOATING_POINT case 'a': case 'A': if (ch == 'a') { ox[1] = 'x'; xdigs = xdigs_lower; expchar = 'p'; } else { ox[1] = 'X'; xdigs = xdigs_upper; expchar = 'P'; } if (prec >= 0) prec++; if (dtoaresult != NULL) __freedtoa(dtoaresult); if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = cp = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } else { fparg.dbl = GETARG(double); dtoaresult = cp = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } if (prec < 0) prec = dtoaend - cp; if (expt == INT_MAX) ox[1] = '\0'; goto fp_common; case 'e': case 'E': expchar = ch; if (prec < 0) /* account for digit before decpt */ prec = DEFPREC + 1; else prec++; goto fp_begin; case 'f': case 'F': expchar = '\0'; goto fp_begin; case 'g': case 'G': expchar = ch - ('g' - 'e'); if (prec == 0) prec = 1; fp_begin: if (prec < 0) prec = DEFPREC; if (dtoaresult != NULL) __freedtoa(dtoaresult); if (flags & LONGDBL) { fparg.ldbl = GETARG(long double); dtoaresult = cp = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; goto error; } } else {