/* Copyright 1996 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* scanf.c: ANSI draft (X3J11 Oct 86) part of section 4.9 code */ /* Copyright (C) Codemist Ltd., 1988 */ /* version 4a */ /* BEWARE: there are quite a few ambiguities/oddities in the Oct 86 ANSI */ /* draft definition of scanf(). */ /* Memo: consider using ftell() (or rather fgetpos() for big file worries) */ /* one day instead of charcount below. See also 'countgetc()'. */ /* Memo 2: the code below always reads one char beyond the end of the */ /* item to be converted. The exception is '%c' (q.v.). */ /* The last char is then __backspace()'d. This is done so avoid using up */ /* the 1 char ungetc() guaranteed at all other times. */ #define __system_io 1 /* makes stdio.h declare more */ /* sscanf wants to know about the system part of the FILE descriptor */ #include "hostsys.h" #include "kernel.h" #include <stdio.h> /* we define scanf for this */ #include <stdlib.h> /* and strtol/strtoul etc for this */ #include <math.h> #include <errno.h> #include <string.h> #include <ctype.h> #include <stddef.h> #include <stdarg.h> #include <limits.h> /* HIDDEN IMPORT from stdio.c */ extern int __backspace(FILE *stream); /* a strict R-inverse of getc() */ #define NOSTORE 01 #define LONG 02 #define SHORT 04 #define FIELDGIVEN 010 #define LONGDOUBLE 020 #define ALLOWSIGN 040 /* + or - acceptable to rd_int */ #define DOTSEEN 0100 /* internal to rd_real */ #define NEGEXP 0200 /* internal to rd_real */ #define NUMOK 0400 /* ditto + rd_int */ #define NUMNEG 01000 /* ditto + rd_int */ #define countgetc(p) (charcount++, getc(p)) /* The next macros, with the help of the compiler, ensures that we can */ /* test for LONG and SHORT properly, but not general extra code. */ #define isLONG_(flag) ((flag) & LONG && sizeof(int) != sizeof(long)) #define isSHORT_(flag) ((flag) & SHORT) #define isLONGDOUBLE_(flag) \ ((flag) & LONGDOUBLE && sizeof(double) != sizeof(long double)) #define CVTEOF (-1) /* used for eof return (!= any number of chars) */ #define CVTFAIL (-2) /* used for error return (!= any number of chars) */ #define scanf_intofdigit(c) ((c) - '0') static int ch_val(int ch, int radix) { int val; /* Memo: beware that ANSI currently say a-z act as 10-35 for strtol() */ /* etc. The test below is isalpha() etc. This means that this test */ /* may not work in a non-C locale where isalpha('{') may be true */ /* (e.g. Swedish ASCII). */ if ('A' == 193) /* ebcdic */ val = (isdigit(ch) ? (ch) - '0' : isalpha(ch) ? (ch |= 0x40, /* quick ebcdic toupper */ ch <= 'I' ? ch - 'A' + 10 : ch <= 'R' ? ch - 'J' + 19 : ch - 'S' + 28) : -1); else val = (isdigit(ch) ? (ch) - '0' : islower(ch) ? (ch) - 'a' + 10 : isupper(ch) ? (ch) - 'A' + 10 : -1); return (val < radix ? val : -1); } static long int rd_int(FILE *p, va_list res, int flag, int radix, int field) { long int charcount = -1; /* allow for always ungetc */ unsigned long int n = 0; int ch; while (isspace(ch = countgetc(p))); /* leading whitespace */ if (ch == EOF) return CVTEOF; flag &= ~(NUMOK+NUMNEG); if (field > 0 && flag & ALLOWSIGN) switch (ch) { case '-': flag |= NUMNEG; case '+': ch = countgetc(p); field--; break; } if (field > 0 && ch=='0') { flag |= NUMOK, field--; /* starts with 0 - maybe octal or hex mode */ ch = countgetc(p); if (field > 0 && (ch=='x' || ch=='X') && (radix==0 || radix==16)) { flag &= ~NUMOK, field--; ch = countgetc(p); radix = 16; } else if (radix == 0) radix = 8; } if (radix == 0) radix = 10; { int digit; while (field > 0 && (digit = ch_val(ch, radix)) >= 0) { flag |= NUMOK, field--; n = n*radix + digit; ch = countgetc(p); } } __backspace(p); if (!(flag & NUMOK)) return CVTFAIL; if (!(flag & NOSTORE)) { /* This code is pretty specious on a 2's complement machine */ if (flag & ALLOWSIGN) { long int m = flag & NUMNEG ? -n : n; int *p = va_arg(res, int *); /* rely on sizeof(int*)=sizeof(short*) */ if isSHORT_(flag) *(short *)p = (short)m; else if isLONG_(flag) *(long *)p = m; else *(int *)p = (int)m; } else /* pointer case comes here too - with quite some type pun! */ { unsigned int *p = va_arg(res, unsigned int *); /* rely on sizeof(unsigned int *)==sizeof(unsigned short *) */ if isSHORT_(flag) *(unsigned short *)p = (unsigned short)n; else if isLONG_(flag) *(unsigned long *)p = n; else *(unsigned int *)p = (unsigned int)n; } } return charcount; } #ifndef NO_FLOATING_POINT #ifdef IBMFLOAT static float carefully_narrow(double l) { /* Maybe in scanf I should ROUND rather than TRUNCATE here... */ static const int amax = 0x7fffffff, amin = 0xffffffff; if (l >= (double)*(float *)&amax || l <= (double)*(float *)&amin) return (float)l; else /* above line avoids overflow */ { float f = (float) l; /* narrower version */ double lost = l - (double)f; /* amount that was chopped off */ return (float)(l + +(lost + lost)); } } #else /* IBMFLOAT */ static float carefully_narrow(double l) /* All the trouble I went to to detect potential overflow has to be re- */ /* done here so that underflow and overflow do not occur during the */ /* narrowing operation that is about to be done. */ /* *********** machine dependent code ************* (should not go here) */ { int w; (void)frexp(l, &w); /* extract exponent */ w = w + 0x7e; /* exponent for single precision version */ if (w<=0 && l!=0.0) { errno = ERANGE; return 0.0f; } else if (w >= 0xff) /* Overflow of single-precision values - fudge single precision infinity */ /* *********** machine dependent code ************* (should not go here) */ { static int posinf = 0x7f800000, neginf = 0xff800000; errno = ERANGE; return *(float *)(l >= 0.0 ? &posinf : &neginf); } else return (float)l; /* what we really wanted to do */ } #endif /* IEEE */ #ifdef HOST_HAS_BCD_FLT static long int rd_real(FILE *p, va_list res, int flag, int field) { long int charcount = -1; /* allow for always ungetc */ int ch, x = 0; unsigned int a[3]; /* IEEE 'packed' format as per ACORN FPE2 */ double l = 0.0; a[0] = a[1] = a[2] = 0; while (isspace(ch = countgetc(p))); /* not counted towards field width */ if (ch == EOF) return CVTEOF; flag &= ~(NUMOK+DOTSEEN+NUMNEG); if (field > 0) switch (ch) { case '-': flag |= NUMNEG; case '+': ch = countgetc(p); field--; break; } while (field > 0) { if ((ch==*decimal_point || ch=='.') && !(flag & DOTSEEN)) flag |= DOTSEEN, field--; else if (isdigit(ch)) { flag |= NUMOK, field--; if ((a[0] & 0xf00) == 0) { a[0] = (a[0]<<4) | (a[1]>>28); a[1] = (a[1]<<4) | (a[2]>>28); a[2] = (a[2]<<4) | scanf_intofdigit(ch); if (flag & DOTSEEN) x -= 1; } else if (!(flag & DOTSEEN)) x += 1; } else break; ch = countgetc(p); } /* we must unread the 'e' in (say) "+.e" as cannot be valid */ if (field > 0 && (ch == 'e' || ch == 'E') && (flag & NUMOK)) { int x2 = 0; flag &= ~(NUMOK+NEGEXP), field--; switch (ch = countgetc(p)) { case '-': flag |= NEGEXP; case '+': ch = countgetc(p); field--; default: break; } while (field > 0 && isdigit(ch)) { flag |= NUMOK, field--; x2 = 10*x2 + scanf_intofdigit(ch); ch = countgetc(p); } if (flag & NEGEXP) x -= x2; else x += x2; } __backspace(p); if (a[0]==0 && a[1]==0 && a[2]==0) l = 0.0; else { if (a[0]==0 && a[1]==0) { a[1] = a[2]; a[2] = 0; x -= 8; } while ((a[0] & 0xf00)==0) { a[0] = (a[0]<<4) | (a[1]>>28); a[1] = (a[1]<<4) | (a[2]>>28); a[2] = a[2]<<4; x -= 1; } x += 18; /* allow for position of decimal point in packed format */ if (x < -999) l = 0.0, errno = ERANGE; else if (x > 999) { if (flag & NUMNEG) l = -HUGE_VAL; else l = HUGE_VAL; errno = ERANGE; } else { int xDiv10; if (x < 0) a[0] |= 0x40000000, x = -x; xDiv10 = _kernel_sdiv10(x); a[0] |= (x - xDiv10 * 10) << 12; x = xDiv10; xDiv10 = _kernel_sdiv10(x); a[0] |= (x - xDiv10 * 10) << 16; x = xDiv10; a[0] |= _kernel_srem(10, x) << 20; if (flag & NUMNEG) a[0] |= 0x80000000; l = _ldfp(a); /* sets errno if necessary */ } } if (!(flag & NUMOK)) return CVTFAIL; if (flag & LONG) { if (!(flag & NOSTORE)) { if (isLONGDOUBLE_(flag)) *va_arg(res, long double *) = l; /* not fully done */ else *va_arg(res, double *) = l; } } else { float f = carefully_narrow(l); /* treat overflow consistently whether or not stored */ if (!(flag & NOSTORE)) *va_arg(res, float *) = f; } return charcount; } #else /* HOST_HAS_BCD_FLT */ static long int rd_real(FILE *p, va_list res, int flag, int field) { long int charcount = -1; /* allow for always ungetc */ int ch, x = 0, w; int i = 0; double l = 0.0, pten = 0.1; while (isspace(ch = countgetc(p))); /* not counted towards field width */ if (ch == EOF) return CVTEOF; flag &= ~(NUMOK+DOTSEEN+NUMNEG); if (field > 0) switch (ch) { case '-': flag |= NUMNEG; case '+': ch = countgetc(p); field--; break; } /* I accumulate up to 6 (decimal) significant figures in the integer */ /* variable i, and remaining digits in the floating point variable l. */ while (field > 0) { if ((ch==*decimal_point || ch=='.') && !(flag & DOTSEEN)) flag |= DOTSEEN, field--; else if (isdigit(ch)) { flag |= NUMOK, field--; if (i < 100000) { i = 10*i + scanf_intofdigit(ch); if (flag & DOTSEEN) x -= 1; } else { l += pten * scanf_intofdigit(ch); pten /= 10.0; if (!(flag & DOTSEEN)) x += 1; } } else break; ch = countgetc(p); } /* we must unread the 'e' in (say) "+.e" as cannot be valid */ if (field > 0 && (ch == 'e' || ch == 'E') && (flag & NUMOK)) { int x2 = 0; flag &= ~(NUMOK+NEGEXP), field--; switch (ch = countgetc(p)) { case '-': flag |= NEGEXP; case '+': ch = countgetc(p); field--; default: break; } while (field > 0 && isdigit(ch)) { flag |= NUMOK, field--; x2 = 10*x2 + scanf_intofdigit(ch); ch = countgetc(p); } if (flag & NEGEXP) x -= x2; else x += x2; } __backspace(p); /* The code that follows multiplies (i.l) by 10^x using one-and-a-half */ /* precision arithmetic, with relevant scaling so that any over or under */ /* flow is deferred to the very last minute. */ { double d, dlow, d3, d3low; int bx = (10*x)/3, w1; l = ldexp(l, x-bx); d = ldexp((double)i, x-bx); dlow = 0.0; if (x < 0) { w1 = -x; d3 = 0.2; d3low = 0.0; _fp_normalize(d3, d3low); d3low = (1.0 - 5.0*d3)/5.0; } else { w1 = x; d3 = 5.0; d3low = 0.0; } if (w1!=0) for(;;) { if ((w1&1)!=0) { l *= (d3 + d3low); dlow = d*d3low + dlow*(d3 + d3low); d *= d3; _fp_normalize(d, dlow); if (w1==1) break; } d3low *= (2.0*d3 + d3low); d3 *= d3; _fp_normalize(d3, d3low); w1 = w1 >> 1; } #ifdef IBMFLOAT l = l + dlow; /* I want to set l to (l+d)*2^bx here. */ /* At present d has lots of low zero bits, and l is much smaller. */ switch (bx & 3) { case 3: l *= 8.0; d *= 8.0; break; case 2: l *= 4.0; d *= 4.0; break; case 1: l *= 2.0; d *= 2.0; break; case 0: break; } { double ltemp = l + d; double err = +(d - ltemp) + l; ltemp = ltemp + +(err + err); /* Achieves rounding */ l = frexp(ltemp, &w); w += (bx & ~3); } #else l = l + dlow; l = l + d; l = frexp(l, &w); w += bx; #endif /* Now I check to see if the number would give a floating point overflow */ /* and if so I return HUGE_VAL, and set errno to ERANGE. */ /* ********* machine dependent integers *********** */ #ifdef IBMFLOAT if (w > (0x3f)*4) #else if (w >= 0x7ff-0x3fe) #endif { if (flag & NUMNEG) l = -HUGE_VAL; else l = HUGE_VAL; errno = ERANGE; } /* Underflows yield a zero result but set errno to ERANGE */ #ifdef IBMFLOAT else if (w <= -(0x41*4) && l!=0.0) #else else if (w <= -0x3fe && l!=0.0) #endif { l = 0.0; errno = ERANGE; } else { if (flag & NUMNEG) l = -ldexp(l, w); else l = ldexp(l, w); } } if (!(flag & NUMOK)) return CVTFAIL; if (flag & LONG) { if (!(flag & NOSTORE)) { if (isLONGDOUBLE_(flag)) *va_arg(res, long double *) = l; /* not fully done */ else *va_arg(res, double *) = l; } } else { float f = carefully_narrow(l); /* treat overflow consistently whether or not stored */ if (!(flag & NOSTORE)) *va_arg(res, float *) = f; } return charcount; } #endif /* HOST_HAS_BCD_FLT */ #endif /* NO_FLOATING_POINT */ /* Amalgamate the next two routines? */ static long int rd_string(FILE *p, va_list res, int flag, int field) { long int charcount = -1; /* allow for always ungetc */ int ch; char *s = NULL; while (isspace(ch = countgetc(p))); /* not counted towards field width */ if (ch == EOF) return CVTEOF; /* fail if EOF occurs here */ if (!(flag & NOSTORE)) s = va_arg(res, char *); while (field > 0 && ch!=EOF && !isspace(ch)) { field--; if (!(flag & NOSTORE)) *s++ = ch; ch = countgetc(p); } __backspace(p); /* OK if ch == EOF */ if (!(flag & NOSTORE)) *s = 0; return charcount; } /* Ambiguity in Oct 86 ANSI draft: can "%[x]" match a zero-length string? */ /* p119 line 19 suggests no, p121 example suggests yes. Treat as yes here */ static long int rd_string_map(FILE *p, va_list res, int flag, int field, int charmap[]) { long int charcount = -1; /* allow for always ungetc */ int ch; char *s = NULL; if (!(flag & NOSTORE)) s = va_arg(res, char *); ch = countgetc(p); if (ch == EOF) return CVTEOF; while (field > 0 && ch != EOF && (charmap[ch/32] & (1<<(ch%32))) != 0) { field--; if (!(flag & NOSTORE)) *s++ = ch; ch = countgetc(p); } __backspace(p); /* OK if ch == EOF */ if (!(flag & NOSTORE)) *s = 0; return charcount; } /* It seems amazing that vfscanf is not available externally in ANSI */ static int vfscanf(FILE *p, const char *sfmt, va_list argv) { /* The next line is essential (see isspace() ANSI doc. and also use of * charmap[] below) if char is signed by default. * Our char is unsigned, but the following line should * just use the same register for fmt/sfmt and so cost nothing! */ const unsigned char *fmt = (const unsigned char *)sfmt; int cnt = 0; long charcount = 0; for (;;) { int fch; switch (fch = *fmt++) { case 0: return cnt; /* end of format string */ default: { int ch; if (isspace(fch)) /* consolidate whitespace */ { int seen = 0; while (isspace(fch = *fmt++)); fmt--; /* N.B. isspace() must return 0 if its arg is '\0' or EOF. */ while (isspace(ch = getc(p))) charcount++, seen = 1; __backspace(p); #ifdef never /* The next line requires non empty whitespace to match format whilespace. */ /* Removed as incompatible with bsd unix (and other prior practice?). */ if (!seen) return cnt; /* require at least 1 */ #endif continue; } else if ((ch = getc(p)) == fch) { charcount++; continue; } __backspace(p); /* offending char is left unread */ if (ch == EOF && cnt == 0) return EOF; return cnt; /* unmatched literal */ } case '%': { int field = 0, flag = 0; long int worked; if (*fmt == '*') fmt++, flag |= NOSTORE; while (isdigit(fch = *fmt++)) { if (field > INT_MAX/10) return cnt; /* overflow check */ field = field*10 + fch - '0'; if (field < 0) return cnt; /* overflow check */ flag |= FIELDGIVEN; } if (!(flag & FIELDGIVEN)) field = INT_MAX; if (fch == 'l') fch = *fmt++, flag |= LONG; else if (fch == 'L') fch = *fmt++, flag |= LONG | LONGDOUBLE; else if (fch == 'h') fch = *fmt++, flag |= SHORT; switch (fch) { default: return cnt; /* illegal conversion code */ case '%': { int ch = getc(p); /* treat as fatuous the omission of '%' from non-skipping white space list */ if (ch == '%') { charcount++; continue; } __backspace(p); /* offending char is left unread */ if (ch == EOF && cnt == 0) return EOF; return cnt; /* unmatched literal '%' */ } case 'c': if (!(flag & FIELDGIVEN)) field = 1; { char *cp = NULL; int ch; if (!(flag & NOSTORE)) cp = va_arg(argv, char *); /* ANSI say no chars match -> failure. Hence 0 width must always fail. */ if (field == 0) return cnt; for (; field > 0; field--) /* The next line reflects the ANSI wording suggesting EXACTLY 'field' chars */ /* should be read. */ { if ((ch = countgetc(p)) == EOF) return cnt == 0 ? EOF : cnt; if (!(flag & NOSTORE)) *cp++ = ch; } } if (!(flag & NOSTORE)) cnt++; /* If conversion succeeds */ continue; case 'd': worked = rd_int(p, argv, flag | ALLOWSIGN, 10, field); break; case 'e': case 'E': case 'f': case 'g': case 'G': #ifndef NO_FLOATING_POINT worked = rd_real(p, argv, flag, field); #else return(cnt); /* Floating point not implemented */ #endif break; case 'i': worked = rd_int(p, argv, flag | ALLOWSIGN, 0, field); break; /* %n assigns the number of characters read from the input so far - NOTE */ /* that this assignment is NOT influenced by the * flag and does NOT */ /* count towards the value returned by scanf. Note that h and l apply. */ case 'n': if isSHORT_(flag) *va_arg(argv, short *) = (short)charcount; else if isLONG_(flag) *va_arg(argv, long *) = charcount; else *va_arg(argv, int *) = (int)charcount; continue; case 'o': worked = rd_int(p, argv, flag | ALLOWSIGN, 8, field); break; /* pointers are displayed in hex, but h,l,L ignored */ case 'p': worked = rd_int(p, argv, flag & ~(LONG|SHORT), 16, field); break; case 's': worked = rd_string(p, argv, flag, field); break; case 'u': worked = rd_int(p, argv, flag, 10, field); break; case 'x': case 'X': worked = rd_int(p, argv, flag | ALLOWSIGN, 16, field); break; case '[': { int negated = 0, i, charmap[8]; if ((fch = *fmt++) == '^') negated = 1, fch = *fmt++; for (i=0; i<8; i++) charmap[i] = 0; /* the 'do' next allows special treatment of %[]})] */ do { if (fch==0) return cnt; /* %[... unterminated */ charmap[fch/32] |= 1<<(fch%32); } while ((fch = *fmt++) != ']'); if (negated) for (i=0; i<8; i++) charmap[i] = ~charmap[i]; worked = rd_string_map(p, argv, flag, field, charmap); } break; } if (worked < 0) /* conversion failed */ return worked == CVTEOF && cnt == 0 ? EOF : cnt; if (!(flag & NOSTORE)) cnt++; /* another assignment made */ charcount += worked; /* chars were read anyway */ continue; } } } } int fscanf(FILE *fp, const char *fmt, ...) { va_list a; int n; va_start(a, fmt); n = vfscanf(fp, fmt, a); va_end(a); return n; } int scanf(const char *fmt, ...) { va_list a; int n; va_start(a, fmt); n = vfscanf(stdin, fmt, a); va_end(a); return n; } typedef struct __extradata { /* * BODGE BODGE BODGE BODGE * This structure is copied from c.stdio */ unsigned char __a[2]; long __b; unsigned char *__c; int __d; int __e; int __f; } _extradata, *_extradatap; int sscanf(const char *buff, const char *fmt, ...) { /*************************************************************************/ /* Note that this code interacts in a dubious way with the getc macro. */ /* Also ungetc. */ /*************************************************************************/ va_list a; FILE hack; _extradata extra; _extradatap extrap = &extra; int n; va_start(a, fmt); memclr(&hack, sizeof(FILE)); memclr(extrap, sizeof(_extradata)); hack.__flag = _IOSTRG+_IOREAD; hack.__ptr = hack.__base = (unsigned char *)buff; hack.__icnt = strlen(buff); hack.__extrap = extrap; n = vfscanf(&hack, fmt, a); va_end(a); return n; } double strtod(const char *nptr, char **endptr) { double d; int nchars, res; /* Here I rely on scanf to set errno to ERANGE if the converted value is */ /* too big or too small. */ res = sscanf(nptr, "%lf%n", &d, &nchars); /* If the conversion failed that must be because there were no digits at */ /* all in the input. */ if (res==EOF || res<1) { if (endptr!=NULL) *endptr = (char *)nptr; /* the cast is needed in prev. line. See May 86 ANSI draft 3.2.2.4 */ return 0.0; } if (endptr!=NULL) *endptr = (char *)nptr + nchars; return d; } static unsigned long int _strtoul(const char *nsptr, char **endptr, int base) { const unsigned char *nptr = (const unsigned char *)nsptr; /* see scanf */ int c, ok = 0, overflowed = 0; while ((c = *nptr++)!=0 && isspace(c)); if (c=='0') { ok = 1; c = *nptr++; if (c=='x' || c=='X') { if (base==0 || base==16) { ok = 0; base = 16; c = *nptr++; } } else if (base==0) base = 8; } if (base==0) base = 10; { unsigned long dhigh = 0, dlow = 0; int digit; while ((digit = ch_val(c,base)) >= 0) { ok = 1; dlow = base * dlow + digit; dhigh = base * dhigh + (dlow >> 16); dlow &= 0xffff; if (dhigh >= 0x10000) overflowed = 1; c = *nptr++; } if (endptr) *endptr = ok ? (char *)nptr-1 : (char *)nsptr; /* extra result */ return overflowed ? (errno = ERANGE, ULONG_MAX) : (dhigh << 16) | dlow; } } long int strtol(const char *nsptr, char **endptr, int base) { /* The specification in the ANSI information bulletin upsets me here: */ /* strtol is of type long int, and 'if the correct value would cause */ /* overflow LONG_MAX or LONG_MIN is returned'. Thus for hex input the */ /* string 0x80000000 will be considered to have overflowed, and so will */ /* be returned as LONG_MAX. */ /* These days one should use strtoul for unsigned values, so some of */ /* my worries go away. */ /* This code is NOT shared with the %i conversion in scanf for several */ /* reasons: (a) here I deal with overflow in a silly way as noted above, */ /* (b) in scanf I have to deal with field width limitations, which does */ /* not fit in neatly here (c) this functions scans an array of char, */ /* while scanf reads from a stream - fudging these together seems too */ /* much work, (d) here I have the option of specifying the radix, while */ /* in scanf there seems to be no provision for that. Ah well! */ const unsigned char *nptr = (const unsigned char *)nsptr; /* see scanf */ int flag = 0, c; while ((c = *nptr++)!=0 && isspace(c)); switch (c) { case '-': flag |= NUMNEG; /* drop through */ case '+': break; default: nptr--; break; } { char *endp; unsigned long ud = _strtoul((char *)nptr, &endp, base); if (endptr) *endptr = endp==(char *)nptr ? (char *)nsptr : endp; /* The following lines depend on the fact that unsigned->int casts and */ /* unary '-' cannot cause arithmetic traps. Recode to avoid this? */ if (flag & NUMNEG) return (-(long)ud <= 0) ? -(long)ud : (errno = ERANGE, LONG_MIN); else return (+(long)ud >= 0) ? +(long)ud : (errno = ERANGE, LONG_MAX); } } unsigned long int strtoul(const char *nsptr, char **endptr, int base) /* * I don't think the way negation is treated in this is right, but its closer * than before */ { const unsigned char *nptr = (const unsigned char *)nsptr; /* see scanf */ int flag = 0, c; int errno_saved = errno; while ((c = *nptr++)!=0 && isspace(c)); switch (c) { case '-': flag |= NUMNEG; /* drop through */ case '+': break; default: nptr--; break; } errno = 0; { char *endp; unsigned long ud = _strtoul((char *)nptr, &endp, base); if (endptr) *endptr = endp==(char *)nptr ? (char *)nsptr : endp; /* ??? The following lines depend on the fact that unsigned->int casts and */ /* ??? unary '-' cannot cause arithmetic traps. Recode to avoid this? */ if (errno == ERANGE) return ud; errno = errno_saved; if (flag & NUMNEG) return (ud <= LONG_MAX) ? -(unsigned long)ud : (errno = ERANGE, ULONG_MAX); else return +(unsigned long)ud; } } double atof(const char *nptr) { int save_errno = errno; double res = strtod(nptr, (char **)NULL); errno = save_errno; return res; } int atoi(const char *nptr) { int save_errno = errno; long int res = strtol(nptr, (char **)NULL, 10); errno = save_errno; return (int)res; } long int atol(const char *nptr) { int save_errno = errno; long int res = strtol(nptr, (char **)NULL, 10); errno = save_errno; return res; } /* end of scanf.c */