11573Srgrimes/*- 21573Srgrimes * Copyright (c) 1990, 1993 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 5227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation 6227753Stheraven * All rights reserved. 7227753Stheraven * Portions of this software were developed by David Chisnall 8227753Stheraven * under sponsorship from the FreeBSD Foundation. 9227753Stheraven * 101573Srgrimes * This code is derived from software contributed to Berkeley by 111573Srgrimes * Chris Torek. 121573Srgrimes * 131573Srgrimes * Redistribution and use in source and binary forms, with or without 141573Srgrimes * modification, are permitted provided that the following conditions 151573Srgrimes * are met: 161573Srgrimes * 1. Redistributions of source code must retain the above copyright 171573Srgrimes * notice, this list of conditions and the following disclaimer. 181573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 191573Srgrimes * notice, this list of conditions and the following disclaimer in the 201573Srgrimes * documentation and/or other materials provided with the distribution. 21249808Semaste * 3. Neither the name of the University nor the names of its contributors 221573Srgrimes * may be used to endorse or promote products derived from this software 231573Srgrimes * without specific prior written permission. 241573Srgrimes * 251573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 261573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 271573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 281573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 291573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 301573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 311573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 321573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 331573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 341573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 351573Srgrimes * SUCH DAMAGE. 361573Srgrimes */ 371573Srgrimes 381573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 391573Srgrimesstatic char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93"; 401573Srgrimes#endif /* LIBC_SCCS and not lint */ 4192986Sobrien#include <sys/cdefs.h> 4292986Sobrien__FBSDID("$FreeBSD: stable/10/lib/libc/stdio/vfscanf.c 321074 2017-07-17 14:09:34Z kib $"); 431573Srgrimes 4471579Sdeischen#include "namespace.h" 4595137Sfenner#include <ctype.h> 4695137Sfenner#include <inttypes.h> 471573Srgrimes#include <stdio.h> 481573Srgrimes#include <stdlib.h> 4995137Sfenner#include <stddef.h> 501573Srgrimes#include <stdarg.h> 5121757Sache#include <string.h> 52103854Stjr#include <wchar.h> 53103854Stjr#include <wctype.h> 5471579Sdeischen#include "un-namespace.h" 5521757Sache 5621757Sache#include "collate.h" 5771579Sdeischen#include "libc_private.h" 581573Srgrimes#include "local.h" 59227753Stheraven#include "xlocale_private.h" 601573Srgrimes 61128819Sdas#ifndef NO_FLOATING_POINT 6272289Sache#include <locale.h> 6372289Sache#endif 6472289Sache 651573Srgrimes#define BUF 513 /* Maximum length of numeric string. */ 661573Srgrimes 671573Srgrimes/* 681573Srgrimes * Flags used during conversion. 691573Srgrimes */ 701573Srgrimes#define LONG 0x01 /* l: long or double */ 7131359Sbde#define LONGDBL 0x02 /* L: long double */ 721573Srgrimes#define SHORT 0x04 /* h: short */ 7395137Sfenner#define SUPPRESS 0x08 /* *: suppress assignment */ 7495137Sfenner#define POINTER 0x10 /* p: void * (as hex) */ 7595137Sfenner#define NOSKIP 0x20 /* [ or c: do not skip blanks */ 7695137Sfenner#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */ 7795137Sfenner#define INTMAXT 0x800 /* j: intmax_t */ 7895137Sfenner#define PTRDIFFT 0x1000 /* t: ptrdiff_t */ 7995137Sfenner#define SIZET 0x2000 /* z: size_t */ 8095137Sfenner#define SHORTSHORT 0x4000 /* hh: char */ 8195137Sfenner#define UNSIGNED 0x8000 /* %[oupxX] conversions */ 821573Srgrimes 831573Srgrimes/* 84116967Sdas * The following are used in integral conversions only: 85116967Sdas * SIGNOK, NDIGITS, PFXOK, and NZDIGITS 861573Srgrimes */ 871573Srgrimes#define SIGNOK 0x40 /* +/- is (still) legal */ 881573Srgrimes#define NDIGITS 0x80 /* no digits detected */ 891573Srgrimes#define PFXOK 0x100 /* 0x prefix is (still) legal */ 901573Srgrimes#define NZDIGITS 0x200 /* no zero digits detected */ 91125282Sdas#define HAVESIGN 0x10000 /* sign detected */ 921573Srgrimes 931573Srgrimes/* 941573Srgrimes * Conversion types. 951573Srgrimes */ 961573Srgrimes#define CT_CHAR 0 /* %c conversion */ 971573Srgrimes#define CT_CCL 1 /* %[...] conversion */ 981573Srgrimes#define CT_STRING 2 /* %s conversion */ 9995137Sfenner#define CT_INT 3 /* %[dioupxX] conversion */ 10095137Sfenner#define CT_FLOAT 4 /* %[efgEFG] conversion */ 1011573Srgrimes 10295137Sfennerstatic const u_char *__sccl(char *, const u_char *); 103157381Sphk#ifndef NO_FLOATING_POINT 104227753Stheravenstatic int parsefloat(FILE *, char *, char *, locale_t); 105157381Sphk#endif 1061573Srgrimes 107105098Stjr__weak_reference(__vfscanf, vfscanf); 108105098Stjr 1091573Srgrimes/* 110234585Sdas * Conversion functions are passed a pointer to this object instead of 111234585Sdas * a real parameter to indicate that the assignment-suppression (*) 112234585Sdas * flag was specified. We could use a NULL pointer to indicate this, 113234585Sdas * but that would mask bugs in applications that call scanf() with a 114234585Sdas * NULL pointer. 115234585Sdas */ 116234585Sdasstatic const int suppress; 117234585Sdas#define SUPPRESS_PTR ((void *)&suppress) 118234585Sdas 119234585Sdasstatic const mbstate_t initial_mbs; 120234585Sdas 121234585Sdas/* 122234585Sdas * The following conversion functions return the number of characters consumed, 123234585Sdas * or -1 on input failure. Character class conversion returns 0 on match 124234585Sdas * failure. 125234585Sdas */ 126234585Sdas 127234585Sdasstatic __inline int 128234836Sdumbbellconvert_char(FILE *fp, char * p, int width) 129234585Sdas{ 130234799Sdas int n; 131234585Sdas 132234585Sdas if (p == SUPPRESS_PTR) { 133234585Sdas size_t sum = 0; 134234585Sdas for (;;) { 135234585Sdas if ((n = fp->_r) < width) { 136234585Sdas sum += n; 137234585Sdas width -= n; 138234585Sdas fp->_p += n; 139234585Sdas if (__srefill(fp)) { 140234585Sdas if (sum == 0) 141234585Sdas return (-1); 142234585Sdas break; 143234585Sdas } 144234585Sdas } else { 145234585Sdas sum += width; 146234585Sdas fp->_r -= width; 147234585Sdas fp->_p += width; 148234585Sdas break; 149234585Sdas } 150234585Sdas } 151234799Sdas return (sum); 152234585Sdas } else { 153234585Sdas size_t r = __fread(p, 1, width, fp); 154234836Sdumbbell 155234585Sdas if (r == 0) 156234585Sdas return (-1); 157234799Sdas return (r); 158234585Sdas } 159234585Sdas} 160234585Sdas 161234585Sdasstatic __inline int 162234799Sdasconvert_wchar(FILE *fp, wchar_t *wcp, int width, locale_t locale) 163234585Sdas{ 164234585Sdas mbstate_t mbs; 165234585Sdas int n, nread; 166234799Sdas wint_t wi; 167234585Sdas 168234799Sdas mbs = initial_mbs; 169234585Sdas n = 0; 170234799Sdas while (width-- != 0 && 171234799Sdas (wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF) { 172234799Sdas if (wcp != SUPPRESS_PTR) 173234799Sdas *wcp++ = (wchar_t)wi; 174234799Sdas n += nread; 175234585Sdas } 176234799Sdas if (n == 0) 177234799Sdas return (-1); 178234799Sdas return (n); 179234585Sdas} 180234585Sdas 181234585Sdasstatic __inline int 182234836Sdumbbellconvert_ccl(FILE *fp, char * p, int width, const char *ccltab) 183234585Sdas{ 184234585Sdas char *p0; 185234585Sdas int n; 186234585Sdas 187234585Sdas if (p == SUPPRESS_PTR) { 188234585Sdas n = 0; 189234585Sdas while (ccltab[*fp->_p]) { 190234585Sdas n++, fp->_r--, fp->_p++; 191234585Sdas if (--width == 0) 192234585Sdas break; 193234585Sdas if (fp->_r <= 0 && __srefill(fp)) { 194234585Sdas if (n == 0) 195234585Sdas return (-1); 196234585Sdas break; 197234585Sdas } 198234585Sdas } 199234585Sdas } else { 200234585Sdas p0 = p; 201234585Sdas while (ccltab[*fp->_p]) { 202234585Sdas fp->_r--; 203234585Sdas *p++ = *fp->_p++; 204234585Sdas if (--width == 0) 205234585Sdas break; 206234585Sdas if (fp->_r <= 0 && __srefill(fp)) { 207234585Sdas if (p == p0) 208234585Sdas return (-1); 209234585Sdas break; 210234585Sdas } 211234585Sdas } 212234585Sdas n = p - p0; 213234585Sdas if (n == 0) 214234585Sdas return (0); 215234585Sdas *p = 0; 216234585Sdas } 217234585Sdas return (n); 218234585Sdas} 219234585Sdas 220234585Sdasstatic __inline int 221234799Sdasconvert_wccl(FILE *fp, wchar_t *wcp, int width, const char *ccltab, 222234799Sdas locale_t locale) 223234585Sdas{ 224234585Sdas mbstate_t mbs; 225234799Sdas wint_t wi; 226234799Sdas int n, nread; 227234585Sdas 228234799Sdas mbs = initial_mbs; 229234587Sdas n = 0; 230234799Sdas if (wcp == SUPPRESS_PTR) { 231234799Sdas while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF && 232234799Sdas width-- != 0 && ccltab[wctob(wi)]) 233234799Sdas n += nread; 234234799Sdas if (wi != WEOF) 235234799Sdas __ungetwc(wi, fp, __get_locale()); 236234799Sdas } else { 237234799Sdas while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF && 238234799Sdas width-- != 0 && ccltab[wctob(wi)]) { 239234799Sdas *wcp++ = (wchar_t)wi; 240234799Sdas n += nread; 241234585Sdas } 242234799Sdas if (wi != WEOF) 243234799Sdas __ungetwc(wi, fp, __get_locale()); 244234799Sdas if (n == 0) 245234799Sdas return (0); 246234799Sdas *wcp = 0; 247234585Sdas } 248234799Sdas return (n); 249234585Sdas} 250234585Sdas 251234585Sdasstatic __inline int 252234836Sdumbbellconvert_string(FILE *fp, char * p, int width) 253234585Sdas{ 254234585Sdas char *p0; 255234585Sdas int n; 256234585Sdas 257234585Sdas if (p == SUPPRESS_PTR) { 258234585Sdas n = 0; 259234585Sdas while (!isspace(*fp->_p)) { 260234585Sdas n++, fp->_r--, fp->_p++; 261234585Sdas if (--width == 0) 262234585Sdas break; 263234585Sdas if (fp->_r <= 0 && __srefill(fp)) 264234585Sdas break; 265234585Sdas } 266234585Sdas } else { 267234585Sdas p0 = p; 268234585Sdas while (!isspace(*fp->_p)) { 269234585Sdas fp->_r--; 270234585Sdas *p++ = *fp->_p++; 271234585Sdas if (--width == 0) 272234585Sdas break; 273234585Sdas if (fp->_r <= 0 && __srefill(fp)) 274234585Sdas break; 275234585Sdas } 276234585Sdas *p = 0; 277234585Sdas n = p - p0; 278234585Sdas } 279234585Sdas return (n); 280234585Sdas} 281234585Sdas 282234585Sdasstatic __inline int 283234799Sdasconvert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale) 284234585Sdas{ 285234585Sdas mbstate_t mbs; 286234799Sdas wint_t wi; 287234799Sdas int n, nread; 288234585Sdas 289234799Sdas mbs = initial_mbs; 290234799Sdas n = 0; 291234799Sdas if (wcp == SUPPRESS_PTR) { 292234799Sdas while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF && 293234799Sdas width-- != 0 && !iswspace(wi)) 294234799Sdas n += nread; 295234799Sdas if (wi != WEOF) 296234799Sdas __ungetwc(wi, fp, __get_locale()); 297234799Sdas } else { 298234799Sdas while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF && 299234799Sdas width-- != 0 && !iswspace(wi)) { 300234799Sdas *wcp++ = (wchar_t)wi; 301234799Sdas n += nread; 302234585Sdas } 303234799Sdas if (wi != WEOF) 304234799Sdas __ungetwc(wi, fp, __get_locale()); 305234799Sdas *wcp = '\0'; 306234585Sdas } 307234799Sdas return (n); 308234585Sdas} 309234585Sdas 310234585Sdas/* 311234585Sdas * Read an integer, storing it in buf. The only relevant bit in the 312234585Sdas * flags argument is PFXOK. 313234585Sdas * 314234585Sdas * Return 0 on a match failure, and the number of characters read 315234585Sdas * otherwise. 316234585Sdas */ 317234585Sdasstatic __inline int 318234585Sdasparseint(FILE *fp, char * __restrict buf, int width, int base, int flags) 319234585Sdas{ 320234585Sdas /* `basefix' is used to avoid `if' tests */ 321234585Sdas static const short basefix[17] = 322234585Sdas { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 323234585Sdas char *p; 324234585Sdas int c; 325234585Sdas 326234585Sdas flags |= SIGNOK | NDIGITS | NZDIGITS; 327234585Sdas for (p = buf; width; width--) { 328234585Sdas c = *fp->_p; 329234585Sdas /* 330234585Sdas * Switch on the character; `goto ok' if we accept it 331234585Sdas * as a part of number. 332234585Sdas */ 333234585Sdas switch (c) { 334234585Sdas 335234585Sdas /* 336234585Sdas * The digit 0 is always legal, but is special. For 337234585Sdas * %i conversions, if no digits (zero or nonzero) have 338234585Sdas * been scanned (only signs), we will have base==0. 339234585Sdas * In that case, we should set it to 8 and enable 0x 340234585Sdas * prefixing. Also, if we have not scanned zero 341234585Sdas * digits before this, do not turn off prefixing 342234585Sdas * (someone else will turn it off if we have scanned 343234585Sdas * any nonzero digits). 344234585Sdas */ 345234585Sdas case '0': 346234585Sdas if (base == 0) { 347234585Sdas base = 8; 348234585Sdas flags |= PFXOK; 349234585Sdas } 350234585Sdas if (flags & NZDIGITS) 351234585Sdas flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 352234585Sdas else 353234585Sdas flags &= ~(SIGNOK|PFXOK|NDIGITS); 354234585Sdas goto ok; 355234585Sdas 356234585Sdas /* 1 through 7 always legal */ 357234585Sdas case '1': case '2': case '3': 358234585Sdas case '4': case '5': case '6': case '7': 359234585Sdas base = basefix[base]; 360234585Sdas flags &= ~(SIGNOK | PFXOK | NDIGITS); 361234585Sdas goto ok; 362234585Sdas 363234585Sdas /* digits 8 and 9 ok iff decimal or hex */ 364234585Sdas case '8': case '9': 365234585Sdas base = basefix[base]; 366234585Sdas if (base <= 8) 367234585Sdas break; /* not legal here */ 368234585Sdas flags &= ~(SIGNOK | PFXOK | NDIGITS); 369234585Sdas goto ok; 370234585Sdas 371234585Sdas /* letters ok iff hex */ 372234585Sdas case 'A': case 'B': case 'C': 373234585Sdas case 'D': case 'E': case 'F': 374234585Sdas case 'a': case 'b': case 'c': 375234585Sdas case 'd': case 'e': case 'f': 376234585Sdas /* no need to fix base here */ 377234585Sdas if (base <= 10) 378234585Sdas break; /* not legal here */ 379234585Sdas flags &= ~(SIGNOK | PFXOK | NDIGITS); 380234585Sdas goto ok; 381234585Sdas 382234585Sdas /* sign ok only as first character */ 383234585Sdas case '+': case '-': 384234585Sdas if (flags & SIGNOK) { 385234585Sdas flags &= ~SIGNOK; 386234585Sdas flags |= HAVESIGN; 387234585Sdas goto ok; 388234585Sdas } 389234585Sdas break; 390234836Sdumbbell 391234585Sdas /* 392234585Sdas * x ok iff flag still set & 2nd char (or 3rd char if 393234585Sdas * we have a sign). 394234585Sdas */ 395234585Sdas case 'x': case 'X': 396234585Sdas if (flags & PFXOK && p == 397234585Sdas buf + 1 + !!(flags & HAVESIGN)) { 398234585Sdas base = 16; /* if %i */ 399234585Sdas flags &= ~PFXOK; 400234585Sdas goto ok; 401234585Sdas } 402234585Sdas break; 403234585Sdas } 404234585Sdas 405234585Sdas /* 406234585Sdas * If we got here, c is not a legal character for a 407234585Sdas * number. Stop accumulating digits. 408234585Sdas */ 409234585Sdas break; 410234585Sdas ok: 411234585Sdas /* 412234585Sdas * c is legal: store it and look at the next. 413234585Sdas */ 414234585Sdas *p++ = c; 415234585Sdas if (--fp->_r > 0) 416234585Sdas fp->_p++; 417234585Sdas else if (__srefill(fp)) 418234585Sdas break; /* EOF */ 419234585Sdas } 420234585Sdas /* 421234585Sdas * If we had only a sign, it is no good; push back the sign. 422234585Sdas * If the number ends in `x', it was [sign] '0' 'x', so push 423234585Sdas * back the x and treat it as [sign] '0'. 424234585Sdas */ 425234585Sdas if (flags & NDIGITS) { 426234585Sdas if (p > buf) 427234585Sdas (void) __ungetc(*(u_char *)--p, fp); 428234585Sdas return (0); 429234585Sdas } 430234585Sdas c = ((u_char *)p)[-1]; 431234585Sdas if (c == 'x' || c == 'X') { 432234585Sdas --p; 433234585Sdas (void) __ungetc(c, fp); 434234585Sdas } 435234585Sdas return (p - buf); 436234585Sdas} 437234585Sdas 438234585Sdas/* 43971579Sdeischen * __vfscanf - MT-safe version 4401573Srgrimes */ 44116586Sjraynardint 44271579Sdeischen__vfscanf(FILE *fp, char const *fmt0, va_list ap) 4431573Srgrimes{ 44471579Sdeischen int ret; 44571579Sdeischen 446321074Skib FLOCKFILE_CANCELSAFE(fp); 447227753Stheraven ret = __svfscanf(fp, __get_locale(), fmt0, ap); 448321074Skib FUNLOCKFILE_CANCELSAFE(); 44971579Sdeischen return (ret); 45071579Sdeischen} 451227753Stheravenint 452227753Stheravenvfscanf_l(FILE *fp, locale_t locale, char const *fmt0, va_list ap) 453227753Stheraven{ 454227753Stheraven int ret; 455227753Stheraven FIX_LOCALE(locale); 45671579Sdeischen 457321074Skib FLOCKFILE_CANCELSAFE(fp); 458227753Stheraven ret = __svfscanf(fp, locale, fmt0, ap); 459321074Skib FUNLOCKFILE_CANCELSAFE(); 460227753Stheraven return (ret); 461227753Stheraven} 462227753Stheraven 46371579Sdeischen/* 46471579Sdeischen * __svfscanf - non-MT-safe version of __vfscanf 46571579Sdeischen */ 46671579Sdeischenint 467227753Stheraven__svfscanf(FILE *fp, locale_t locale, const char *fmt0, va_list ap) 46871579Sdeischen{ 469234585Sdas#define GETARG(type) ((flags & SUPPRESS) ? SUPPRESS_PTR : va_arg(ap, type)) 47095137Sfenner const u_char *fmt = (const u_char *)fmt0; 47171579Sdeischen int c; /* character from format, or conversion */ 47271579Sdeischen size_t width; /* field width, or 0 */ 47371579Sdeischen int flags; /* flags as defined above */ 4741573Srgrimes int nassigned; /* number of fields assigned */ 47523352Sbde int nconversions; /* number of conversions */ 476234585Sdas int nr; /* characters read by the current conversion */ 4771573Srgrimes int nread; /* number of characters consumed from fp */ 47895137Sfenner int base; /* base argument to conversion function */ 4791573Srgrimes char ccltab[256]; /* character class table for %[...] */ 480234585Sdas char buf[BUF]; /* buffer for numeric conversions */ 4811573Srgrimes 482101776Stjr ORIENT(fp, -1); 483101776Stjr 4841573Srgrimes nassigned = 0; 48523352Sbde nconversions = 0; 4861573Srgrimes nread = 0; 4871573Srgrimes for (;;) { 4881573Srgrimes c = *fmt++; 4891573Srgrimes if (c == 0) 4901573Srgrimes return (nassigned); 4911573Srgrimes if (isspace(c)) { 49239644Sobrien while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) 4931573Srgrimes nread++, fp->_r--, fp->_p++; 4941573Srgrimes continue; 4951573Srgrimes } 4961573Srgrimes if (c != '%') 4971573Srgrimes goto literal; 4981573Srgrimes width = 0; 4991573Srgrimes flags = 0; 5001573Srgrimes /* 5011573Srgrimes * switch on the format. continue if done; 5021573Srgrimes * break once format type is derived. 5031573Srgrimes */ 5041573Srgrimesagain: c = *fmt++; 5051573Srgrimes switch (c) { 5061573Srgrimes case '%': 5071573Srgrimesliteral: 5081573Srgrimes if (fp->_r <= 0 && __srefill(fp)) 5091573Srgrimes goto input_failure; 5101573Srgrimes if (*fp->_p != c) 5111573Srgrimes goto match_failure; 5121573Srgrimes fp->_r--, fp->_p++; 5131573Srgrimes nread++; 5141573Srgrimes continue; 5151573Srgrimes 5161573Srgrimes case '*': 5171573Srgrimes flags |= SUPPRESS; 5181573Srgrimes goto again; 51995137Sfenner case 'j': 52095137Sfenner flags |= INTMAXT; 52195137Sfenner goto again; 5221573Srgrimes case 'l': 52395137Sfenner if (flags & LONG) { 52495137Sfenner flags &= ~LONG; 52595137Sfenner flags |= LONGLONG; 52695137Sfenner } else 52795137Sfenner flags |= LONG; 5281573Srgrimes goto again; 52927151Sjkh case 'q': 53095137Sfenner flags |= LONGLONG; /* not quite */ 53127151Sjkh goto again; 53295137Sfenner case 't': 53395137Sfenner flags |= PTRDIFFT; 53495137Sfenner goto again; 53595137Sfenner case 'z': 53695137Sfenner flags |= SIZET; 53795137Sfenner goto again; 5381573Srgrimes case 'L': 5391573Srgrimes flags |= LONGDBL; 5401573Srgrimes goto again; 5411573Srgrimes case 'h': 54295137Sfenner if (flags & SHORT) { 54395137Sfenner flags &= ~SHORT; 54495137Sfenner flags |= SHORTSHORT; 54595137Sfenner } else 54695137Sfenner flags |= SHORT; 5471573Srgrimes goto again; 5481573Srgrimes 5491573Srgrimes case '0': case '1': case '2': case '3': case '4': 5501573Srgrimes case '5': case '6': case '7': case '8': case '9': 5511573Srgrimes width = width * 10 + c - '0'; 5521573Srgrimes goto again; 5531573Srgrimes 5541573Srgrimes /* 5551573Srgrimes * Conversions. 5561573Srgrimes */ 5571573Srgrimes case 'd': 5581573Srgrimes c = CT_INT; 5591573Srgrimes base = 10; 5601573Srgrimes break; 5611573Srgrimes 5621573Srgrimes case 'i': 5631573Srgrimes c = CT_INT; 5641573Srgrimes base = 0; 5651573Srgrimes break; 5661573Srgrimes 5671573Srgrimes case 'o': 5681573Srgrimes c = CT_INT; 56995137Sfenner flags |= UNSIGNED; 5701573Srgrimes base = 8; 5711573Srgrimes break; 5721573Srgrimes 5731573Srgrimes case 'u': 5741573Srgrimes c = CT_INT; 57595137Sfenner flags |= UNSIGNED; 5761573Srgrimes base = 10; 5771573Srgrimes break; 5781573Srgrimes 57995137Sfenner case 'X': 5801573Srgrimes case 'x': 5811573Srgrimes flags |= PFXOK; /* enable 0x prefixing */ 5821573Srgrimes c = CT_INT; 58395137Sfenner flags |= UNSIGNED; 5841573Srgrimes base = 16; 5851573Srgrimes break; 5861573Srgrimes 587128819Sdas#ifndef NO_FLOATING_POINT 588116967Sdas case 'A': case 'E': case 'F': case 'G': 589116967Sdas case 'a': case 'e': case 'f': case 'g': 5901573Srgrimes c = CT_FLOAT; 5911573Srgrimes break; 5921573Srgrimes#endif 5931573Srgrimes 59495137Sfenner case 'S': 59595137Sfenner flags |= LONG; 59695137Sfenner /* FALLTHROUGH */ 5971573Srgrimes case 's': 5981573Srgrimes c = CT_STRING; 5991573Srgrimes break; 6001573Srgrimes 6011573Srgrimes case '[': 6021573Srgrimes fmt = __sccl(ccltab, fmt); 6031573Srgrimes flags |= NOSKIP; 6041573Srgrimes c = CT_CCL; 6051573Srgrimes break; 6061573Srgrimes 60795137Sfenner case 'C': 60895137Sfenner flags |= LONG; 60995137Sfenner /* FALLTHROUGH */ 6101573Srgrimes case 'c': 6111573Srgrimes flags |= NOSKIP; 6121573Srgrimes c = CT_CHAR; 6131573Srgrimes break; 6141573Srgrimes 6151573Srgrimes case 'p': /* pointer format is like hex */ 6161573Srgrimes flags |= POINTER | PFXOK; 61795137Sfenner c = CT_INT; /* assumes sizeof(uintmax_t) */ 61895137Sfenner flags |= UNSIGNED; /* >= sizeof(uintptr_t) */ 6191573Srgrimes base = 16; 6201573Srgrimes break; 6211573Srgrimes 6221573Srgrimes case 'n': 6231573Srgrimes if (flags & SUPPRESS) /* ??? */ 6241573Srgrimes continue; 62595137Sfenner if (flags & SHORTSHORT) 62695137Sfenner *va_arg(ap, char *) = nread; 62795137Sfenner else if (flags & SHORT) 6281573Srgrimes *va_arg(ap, short *) = nread; 6291573Srgrimes else if (flags & LONG) 6301573Srgrimes *va_arg(ap, long *) = nread; 63195137Sfenner else if (flags & LONGLONG) 63295137Sfenner *va_arg(ap, long long *) = nread; 63395137Sfenner else if (flags & INTMAXT) 63495137Sfenner *va_arg(ap, intmax_t *) = nread; 63595137Sfenner else if (flags & SIZET) 63695137Sfenner *va_arg(ap, size_t *) = nread; 63795137Sfenner else if (flags & PTRDIFFT) 63895137Sfenner *va_arg(ap, ptrdiff_t *) = nread; 6391573Srgrimes else 6401573Srgrimes *va_arg(ap, int *) = nread; 6411573Srgrimes continue; 6421573Srgrimes 64395137Sfenner default: 64495137Sfenner goto match_failure; 64595137Sfenner 6461573Srgrimes /* 64795137Sfenner * Disgusting backwards compatibility hack. XXX 6481573Srgrimes */ 6491573Srgrimes case '\0': /* compat */ 6501573Srgrimes return (EOF); 6511573Srgrimes } 6521573Srgrimes 6531573Srgrimes /* 6541573Srgrimes * We have a conversion that requires input. 6551573Srgrimes */ 6561573Srgrimes if (fp->_r <= 0 && __srefill(fp)) 6571573Srgrimes goto input_failure; 6581573Srgrimes 6591573Srgrimes /* 6601573Srgrimes * Consume leading white space, except for formats 6611573Srgrimes * that suppress this. 6621573Srgrimes */ 6631573Srgrimes if ((flags & NOSKIP) == 0) { 6641573Srgrimes while (isspace(*fp->_p)) { 6651573Srgrimes nread++; 6661573Srgrimes if (--fp->_r > 0) 6671573Srgrimes fp->_p++; 6681573Srgrimes else if (__srefill(fp)) 6691573Srgrimes goto input_failure; 6701573Srgrimes } 6711573Srgrimes /* 6721573Srgrimes * Note that there is at least one character in 6731573Srgrimes * the buffer, so conversions that do not set NOSKIP 6741573Srgrimes * ca no longer result in an input failure. 6751573Srgrimes */ 6761573Srgrimes } 6771573Srgrimes 6781573Srgrimes /* 6791573Srgrimes * Do the conversion. 6801573Srgrimes */ 6811573Srgrimes switch (c) { 6821573Srgrimes 6831573Srgrimes case CT_CHAR: 6841573Srgrimes /* scan arbitrary characters (sets NOSKIP) */ 6851573Srgrimes if (width == 0) 6861573Srgrimes width = 1; 687105247Stjr if (flags & LONG) { 688234585Sdas nr = convert_wchar(fp, GETARG(wchar_t *), 689234799Sdas width, locale); 6901573Srgrimes } else { 691234585Sdas nr = convert_char(fp, GETARG(char *), width); 6921573Srgrimes } 693234585Sdas if (nr < 0) 694234585Sdas goto input_failure; 6951573Srgrimes break; 6961573Srgrimes 6971573Srgrimes case CT_CCL: 6981573Srgrimes /* scan a (nonempty) character class (sets NOSKIP) */ 6991573Srgrimes if (width == 0) 70016586Sjraynard width = (size_t)~0; /* `infinity' */ 701105247Stjr if (flags & LONG) { 702234585Sdas nr = convert_wccl(fp, GETARG(wchar_t *), width, 703234799Sdas ccltab, locale); 704234585Sdas } else { 705234585Sdas nr = convert_ccl(fp, GETARG(char *), width, 706234585Sdas ccltab); 707234585Sdas } 708234585Sdas if (nr <= 0) { 709234585Sdas if (nr < 0) 710103854Stjr goto input_failure; 711234585Sdas else /* nr == 0 */ 712103854Stjr goto match_failure; 7131573Srgrimes } 7141573Srgrimes break; 7151573Srgrimes 7161573Srgrimes case CT_STRING: 7171573Srgrimes /* like CCL, but zero-length string OK, & no NOSKIP */ 7181573Srgrimes if (width == 0) 71916586Sjraynard width = (size_t)~0; 720105247Stjr if (flags & LONG) { 721234585Sdas nr = convert_wstring(fp, GETARG(wchar_t *), 722234799Sdas width, locale); 7231573Srgrimes } else { 724234585Sdas nr = convert_string(fp, GETARG(char *), width); 7251573Srgrimes } 726234585Sdas if (nr < 0) 727234585Sdas goto input_failure; 728234585Sdas break; 7291573Srgrimes 7301573Srgrimes case CT_INT: 73195137Sfenner /* scan an integer as if by the conversion function */ 7321573Srgrimes#ifdef hardway 7331573Srgrimes if (width == 0 || width > sizeof(buf) - 1) 7341573Srgrimes width = sizeof(buf) - 1; 7351573Srgrimes#else 7361573Srgrimes /* size_t is unsigned, hence this optimisation */ 7371573Srgrimes if (--width > sizeof(buf) - 2) 7381573Srgrimes width = sizeof(buf) - 2; 7391573Srgrimes width++; 7401573Srgrimes#endif 741234585Sdas nr = parseint(fp, buf, width, base, flags); 742234585Sdas if (nr == 0) 7431573Srgrimes goto match_failure; 7441573Srgrimes if ((flags & SUPPRESS) == 0) { 74595137Sfenner uintmax_t res; 7461573Srgrimes 747234585Sdas buf[nr] = '\0'; 74895137Sfenner if ((flags & UNSIGNED) == 0) 749227753Stheraven res = strtoimax_l(buf, (char **)NULL, base, locale); 75095137Sfenner else 751227753Stheraven res = strtoumax_l(buf, (char **)NULL, base, locale); 7521573Srgrimes if (flags & POINTER) 75327151Sjkh *va_arg(ap, void **) = 75495137Sfenner (void *)(uintptr_t)res; 75595137Sfenner else if (flags & SHORTSHORT) 75695137Sfenner *va_arg(ap, char *) = res; 7571573Srgrimes else if (flags & SHORT) 7581573Srgrimes *va_arg(ap, short *) = res; 7591573Srgrimes else if (flags & LONG) 7601573Srgrimes *va_arg(ap, long *) = res; 76195137Sfenner else if (flags & LONGLONG) 76295137Sfenner *va_arg(ap, long long *) = res; 76395137Sfenner else if (flags & INTMAXT) 76495137Sfenner *va_arg(ap, intmax_t *) = res; 76595137Sfenner else if (flags & PTRDIFFT) 76695137Sfenner *va_arg(ap, ptrdiff_t *) = res; 76795137Sfenner else if (flags & SIZET) 76895137Sfenner *va_arg(ap, size_t *) = res; 7691573Srgrimes else 7701573Srgrimes *va_arg(ap, int *) = res; 7711573Srgrimes } 7721573Srgrimes break; 7731573Srgrimes 774128819Sdas#ifndef NO_FLOATING_POINT 7751573Srgrimes case CT_FLOAT: 7761573Srgrimes /* scan a floating point number as if by strtod */ 7771573Srgrimes if (width == 0 || width > sizeof(buf) - 1) 7781573Srgrimes width = sizeof(buf) - 1; 779234585Sdas nr = parsefloat(fp, buf, buf + width, locale); 780234585Sdas if (nr == 0) 781116967Sdas goto match_failure; 7821573Srgrimes if ((flags & SUPPRESS) == 0) { 783116967Sdas if (flags & LONGDBL) { 784234585Sdas long double res = strtold_l(buf, NULL, 785234585Sdas locale); 78631359Sbde *va_arg(ap, long double *) = res; 787116967Sdas } else if (flags & LONG) { 788234585Sdas double res = strtod_l(buf, NULL, 789234585Sdas locale); 7901573Srgrimes *va_arg(ap, double *) = res; 791116967Sdas } else { 792234585Sdas float res = strtof_l(buf, NULL, locale); 7931573Srgrimes *va_arg(ap, float *) = res; 794116967Sdas } 7951573Srgrimes } 7961573Srgrimes break; 797128819Sdas#endif /* !NO_FLOATING_POINT */ 7981573Srgrimes } 799234585Sdas if (!(flags & SUPPRESS)) 800234585Sdas nassigned++; 801234585Sdas nread += nr; 802234585Sdas nconversions++; 8031573Srgrimes } 8041573Srgrimesinput_failure: 80523352Sbde return (nconversions != 0 ? nassigned : EOF); 8061573Srgrimesmatch_failure: 8071573Srgrimes return (nassigned); 8081573Srgrimes} 8091573Srgrimes 8101573Srgrimes/* 8111573Srgrimes * Fill in the given table from the scanset at the given format 8121573Srgrimes * (just after `['). Return a pointer to the character past the 8131573Srgrimes * closing `]'. The table has a 1 wherever characters should be 8141573Srgrimes * considered part of the scanset. 8151573Srgrimes */ 81695137Sfennerstatic const u_char * 817291336Sngie__sccl(char *tab, const u_char *fmt) 8181573Srgrimes{ 81992889Sobrien int c, n, v, i; 820227753Stheraven struct xlocale_collate *table = 821227753Stheraven (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; 8221573Srgrimes 8231573Srgrimes /* first `clear' the whole table */ 8241573Srgrimes c = *fmt++; /* first char hat => negated scanset */ 8251573Srgrimes if (c == '^') { 8261573Srgrimes v = 1; /* default => accept */ 8271573Srgrimes c = *fmt++; /* get new first char */ 8281573Srgrimes } else 8291573Srgrimes v = 0; /* default => reject */ 83022308Sache 83122308Sache /* XXX: Will not work if sizeof(tab*) > sizeof(char) */ 83221757Sache (void) memset(tab, v, 256); 83322308Sache 8341573Srgrimes if (c == 0) 8351573Srgrimes return (fmt - 1);/* format ended before closing ] */ 8361573Srgrimes 8371573Srgrimes /* 8381573Srgrimes * Now set the entries corresponding to the actual scanset 8391573Srgrimes * to the opposite of the above. 8401573Srgrimes * 8411573Srgrimes * The first character may be ']' (or '-') without being special; 8421573Srgrimes * the last character may be '-'. 8431573Srgrimes */ 8441573Srgrimes v = 1 - v; 8451573Srgrimes for (;;) { 8461573Srgrimes tab[c] = v; /* take character c */ 8471573Srgrimesdoswitch: 8481573Srgrimes n = *fmt++; /* and examine the next */ 8491573Srgrimes switch (n) { 8501573Srgrimes 8511573Srgrimes case 0: /* format ended too soon */ 8521573Srgrimes return (fmt - 1); 8531573Srgrimes 8541573Srgrimes case '-': 8551573Srgrimes /* 8561573Srgrimes * A scanset of the form 8571573Srgrimes * [01+-] 8581573Srgrimes * is defined as `the digit 0, the digit 1, 8591573Srgrimes * the character +, the character -', but 8601573Srgrimes * the effect of a scanset such as 8611573Srgrimes * [a-zA-Z0-9] 8621573Srgrimes * is implementation defined. The V7 Unix 8631573Srgrimes * scanf treats `a-z' as `the letters a through 8641573Srgrimes * z', but treats `a-a' as `the letter a, the 8651573Srgrimes * character -, and the letter a'. 8661573Srgrimes * 8671573Srgrimes * For compatibility, the `-' is not considerd 8681573Srgrimes * to define a range if the character following 8691573Srgrimes * it is either a close bracket (required by ANSI) 8701573Srgrimes * or is not numerically greater than the character 8711573Srgrimes * we just stored in the table (c). 8721573Srgrimes */ 8731573Srgrimes n = *fmt; 87424627Sache if (n == ']' 875227753Stheraven || (table->__collate_load_error ? n < c : 876303185Sache __collate_range_cmp(n, c) < 0 87724631Sache ) 87824627Sache ) { 8791573Srgrimes c = '-'; 8801573Srgrimes break; /* resume the for(;;) */ 8811573Srgrimes } 8821573Srgrimes fmt++; 88321757Sache /* fill in the range */ 884227753Stheraven if (table->__collate_load_error) { 88524627Sache do { 88624627Sache tab[++c] = v; 88724627Sache } while (c < n); 88824627Sache } else { 88924627Sache for (i = 0; i < 256; i ++) 890303185Sache if (__collate_range_cmp(c, i) <= 0 && 891303185Sache __collate_range_cmp(i, n) <= 0 89224627Sache ) 89324627Sache tab[i] = v; 89424627Sache } 8951573Srgrimes#if 1 /* XXX another disgusting compatibility hack */ 89621757Sache c = n; 8971573Srgrimes /* 8981573Srgrimes * Alas, the V7 Unix scanf also treats formats 8991573Srgrimes * such as [a-c-e] as `the letters a through e'. 9001573Srgrimes * This too is permitted by the standard.... 9011573Srgrimes */ 9021573Srgrimes goto doswitch; 9031573Srgrimes#else 9041573Srgrimes c = *fmt++; 9051573Srgrimes if (c == 0) 9061573Srgrimes return (fmt - 1); 9071573Srgrimes if (c == ']') 9081573Srgrimes return (fmt); 9091573Srgrimes#endif 9101573Srgrimes break; 9111573Srgrimes 9121573Srgrimes case ']': /* end of scanset */ 9131573Srgrimes return (fmt); 9141573Srgrimes 9151573Srgrimes default: /* just another character */ 9161573Srgrimes c = n; 9171573Srgrimes break; 9181573Srgrimes } 9191573Srgrimes } 9201573Srgrimes /* NOTREACHED */ 9211573Srgrimes} 922116967Sdas 923128819Sdas#ifndef NO_FLOATING_POINT 924116967Sdasstatic int 925227753Stheravenparsefloat(FILE *fp, char *buf, char *end, locale_t locale) 926116967Sdas{ 927116967Sdas char *commit, *p; 928187422Sdas int infnanpos = 0, decptpos = 0; 929116967Sdas enum { 930187422Sdas S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX, 931187422Sdas S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS 932116967Sdas } state = S_START; 933116967Sdas unsigned char c; 934227753Stheraven const char *decpt = localeconv_l(locale)->decimal_point; 935116967Sdas _Bool gotmantdig = 0, ishex = 0; 936116967Sdas 937116967Sdas /* 938116967Sdas * We set commit = p whenever the string we have read so far 939116967Sdas * constitutes a valid representation of a floating point 940116967Sdas * number by itself. At some point, the parse will complete 941116967Sdas * or fail, and we will ungetc() back to the last commit point. 942116967Sdas * To ensure that the file offset gets updated properly, it is 943116967Sdas * always necessary to read at least one character that doesn't 944116967Sdas * match; thus, we can't short-circuit "infinity" or "nan(...)". 945116967Sdas */ 946116967Sdas commit = buf - 1; 947116967Sdas for (p = buf; p < end; ) { 948116967Sdas c = *fp->_p; 949116967Sdasreswitch: 950116967Sdas switch (state) { 951116967Sdas case S_START: 952116967Sdas state = S_GOTSIGN; 953116967Sdas if (c == '-' || c == '+') 954116967Sdas break; 955116967Sdas else 956116967Sdas goto reswitch; 957116967Sdas case S_GOTSIGN: 958116967Sdas switch (c) { 959116967Sdas case '0': 960116967Sdas state = S_MAYBEHEX; 961116967Sdas commit = p; 962116967Sdas break; 963116967Sdas case 'I': 964116967Sdas case 'i': 965116967Sdas state = S_INF; 966116967Sdas break; 967116967Sdas case 'N': 968116967Sdas case 'n': 969116967Sdas state = S_NAN; 970116967Sdas break; 971116967Sdas default: 972116967Sdas state = S_DIGITS; 973116967Sdas goto reswitch; 974116967Sdas } 975116967Sdas break; 976116967Sdas case S_INF: 977116967Sdas if (infnanpos > 6 || 978116967Sdas (c != "nfinity"[infnanpos] && 979116967Sdas c != "NFINITY"[infnanpos])) 980116967Sdas goto parsedone; 981116967Sdas if (infnanpos == 1 || infnanpos == 6) 982116967Sdas commit = p; /* inf or infinity */ 983116967Sdas infnanpos++; 984116967Sdas break; 985116967Sdas case S_NAN: 986116967Sdas switch (infnanpos) { 987116967Sdas case 0: 988116967Sdas if (c != 'A' && c != 'a') 989116967Sdas goto parsedone; 990116967Sdas break; 991116967Sdas case 1: 992116967Sdas if (c != 'N' && c != 'n') 993116967Sdas goto parsedone; 994116967Sdas else 995116967Sdas commit = p; 996116967Sdas break; 997116967Sdas case 2: 998116967Sdas if (c != '(') 999116967Sdas goto parsedone; 1000116967Sdas break; 1001116967Sdas default: 1002116967Sdas if (c == ')') { 1003116967Sdas commit = p; 1004187422Sdas state = S_DONE; 1005116967Sdas } else if (!isalnum(c) && c != '_') 1006116967Sdas goto parsedone; 1007116967Sdas break; 1008116967Sdas } 1009116967Sdas infnanpos++; 1010116967Sdas break; 1011187422Sdas case S_DONE: 1012187422Sdas goto parsedone; 1013116967Sdas case S_MAYBEHEX: 1014116967Sdas state = S_DIGITS; 1015116967Sdas if (c == 'X' || c == 'x') { 1016116967Sdas ishex = 1; 1017116967Sdas break; 1018116967Sdas } else { /* we saw a '0', but no 'x' */ 1019116967Sdas gotmantdig = 1; 1020116967Sdas goto reswitch; 1021116967Sdas } 1022116967Sdas case S_DIGITS: 1023187422Sdas if ((ishex && isxdigit(c)) || isdigit(c)) { 1024116967Sdas gotmantdig = 1; 1025187422Sdas commit = p; 1026187422Sdas break; 1027187422Sdas } else { 1028187422Sdas state = S_DECPT; 1029187422Sdas goto reswitch; 1030187422Sdas } 1031187422Sdas case S_DECPT: 1032187422Sdas if (c == decpt[decptpos]) { 1033187422Sdas if (decpt[++decptpos] == '\0') { 1034187422Sdas /* We read the complete decpt seq. */ 1035187422Sdas state = S_FRAC; 1036187422Sdas if (gotmantdig) 1037187422Sdas commit = p; 1038187422Sdas } 1039187422Sdas break; 1040187422Sdas } else if (!decptpos) { 1041187422Sdas /* We didn't read any decpt characters. */ 1042116967Sdas state = S_FRAC; 1043187422Sdas goto reswitch; 1044187422Sdas } else { 1045187422Sdas /* 1046187422Sdas * We read part of a multibyte decimal point, 1047187422Sdas * but the rest is invalid, so bail. 1048187422Sdas */ 1049187422Sdas goto parsedone; 1050116967Sdas } 1051116967Sdas case S_FRAC: 1052124175Snectar if (((c == 'E' || c == 'e') && !ishex) || 1053124175Snectar ((c == 'P' || c == 'p') && ishex)) { 1054116967Sdas if (!gotmantdig) 1055116967Sdas goto parsedone; 1056116967Sdas else 1057116967Sdas state = S_EXP; 1058124175Snectar } else if ((ishex && isxdigit(c)) || isdigit(c)) { 1059116967Sdas commit = p; 1060116967Sdas gotmantdig = 1; 1061116967Sdas } else 1062116967Sdas goto parsedone; 1063116967Sdas break; 1064116967Sdas case S_EXP: 1065116967Sdas state = S_EXPDIGITS; 1066116967Sdas if (c == '-' || c == '+') 1067116967Sdas break; 1068116967Sdas else 1069116967Sdas goto reswitch; 1070116967Sdas case S_EXPDIGITS: 1071116967Sdas if (isdigit(c)) 1072116967Sdas commit = p; 1073116967Sdas else 1074116967Sdas goto parsedone; 1075116967Sdas break; 1076116967Sdas default: 1077116967Sdas abort(); 1078116967Sdas } 1079116967Sdas *p++ = c; 1080116967Sdas if (--fp->_r > 0) 1081116967Sdas fp->_p++; 1082116967Sdas else if (__srefill(fp)) 1083116967Sdas break; /* EOF */ 1084116967Sdas } 1085116967Sdas 1086116967Sdasparsedone: 1087116967Sdas while (commit < --p) 1088116967Sdas __ungetc(*(u_char *)p, fp); 1089116967Sdas *++commit = '\0'; 1090116967Sdas return (commit - buf); 1091116967Sdas} 1092116967Sdas#endif 1093