subr_scanf.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 794 行 · 第 1/2 页

C
794
字号
			/* 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 = *inp;				/*				 * Switch on the character; `goto ok'				 * if we accept it as a part of number.				 */				switch (c) {				/*				 * The digit 0 is always legal, but is				 * special.  For %i conversions, if no				 * digits (zero or nonzero) have been				 * scanned (only signs), we will have				 * base==0.  In that case, we should set				 * it to 8 and enable 0x prefixing.				 * Also, if we have not scanned zero digits				 * before this, do not turn off prefixing				 * (someone else will turn it off if we				 * have scanned any nonzero digits).				 */				case '0':					if (base == 0) {						base = 8;						flags |= PFXOK;					}					if (flags & NZDIGITS)					    flags &= ~(SIGNOK|NZDIGITS|NDIGITS);					else					    flags &= ~(SIGNOK|PFXOK|NDIGITS);					goto ok;				/* 1 through 7 always legal */				case '1': case '2': case '3':				case '4': case '5': case '6': case '7':					base = basefix[base];					flags &= ~(SIGNOK | PFXOK | NDIGITS);					goto ok;				/* digits 8 and 9 ok iff decimal or hex */				case '8': case '9':					base = basefix[base];					if (base <= 8)						break;	/* not legal here */					flags &= ~(SIGNOK | PFXOK | NDIGITS);					goto ok;				/* letters ok iff hex */				case 'A': case 'B': case 'C':				case 'D': case 'E': case 'F':				case 'a': case 'b': case 'c':				case 'd': case 'e': case 'f':					/* no need to fix base here */					if (base <= 10)						break;	/* not legal here */					flags &= ~(SIGNOK | PFXOK | NDIGITS);					goto ok;				/* sign ok only as first character */				case '+': case '-':					if (flags & SIGNOK) {						flags &= ~SIGNOK;						goto ok;					}					break;				/* x ok iff flag still set & 2nd char */				case 'x': case 'X':					if (flags & PFXOK && p == buf + 1) {						base = 16;	/* if %i */						flags &= ~PFXOK;						goto ok;					}					break;				}				/*				 * If we got here, c is not a legal character				 * for a number.  Stop accumulating digits.				 */				break;		ok:				/*				 * c is legal: store it and look at the next.				 */				*p++ = c;				if (--inr > 0)					inp++;				else 					break;		/* end of input */			}			/*			 * 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) {					inp--;					inr++;				}				goto match_failure;			}			c = ((u_char *)p)[-1];			if (c == 'x' || c == 'X') {				--p;				inp--;				inr++;			}			if ((flags & SUPPRESS) == 0) {				u_quad_t res;				*p = 0;				res = (*ccfn)(buf, (char **)NULL, base);				if (flags & POINTER)					*va_arg(ap, void **) =						(void *)(u_long)res;				else if (flags & SHORT)					*va_arg(ap, short *) = res;				else if (flags & LONG)					*va_arg(ap, long *) = res;				else if (flags & QUAD)					*va_arg(ap, quad_t *) = res;				else					*va_arg(ap, int *) = res;				nassigned++;			}			nread += p - buf;			nconversions++;			break;		}	}input_failure:	return (nconversions != 0 ? nassigned : -1);match_failure:	return (nassigned);}/* * Fill in the given table from the scanset at the given format * (just after `[').  Return a pointer to the character past the * closing `]'.  The table has a 1 wherever characters should be * considered part of the scanset. */static u_char *__sccl(char *tab, u_char *fmt){	int c, n, v;	/* first `clear' the whole table */	c = *fmt++;		/* first char hat => negated scanset */	if (c == '^') {		v = 1;		/* default => accept */		c = *fmt++;	/* get new first char */	} else		v = 0;		/* default => reject */	/* XXX: Will not work if sizeof(tab*) > sizeof(char) */	for (n = 0; n < 256; n++)		     tab[n] = v;	/* memset(tab, v, 256) */	if (c == 0)		return (fmt - 1);/* format ended before closing ] */	/*	 * Now set the entries corresponding to the actual scanset	 * to the opposite of the above.	 *	 * The first character may be ']' (or '-') without being special;	 * the last character may be '-'.	 */	v = 1 - v;	for (;;) {		tab[c] = v;		/* take character c */doswitch:		n = *fmt++;		/* and examine the next */		switch (n) {		case 0:			/* format ended too soon */			return (fmt - 1);		case '-':			/*			 * A scanset of the form			 *	[01+-]			 * is defined as `the digit 0, the digit 1,			 * the character +, the character -', but			 * the effect of a scanset such as			 *	[a-zA-Z0-9]			 * is implementation defined.  The V7 Unix			 * scanf treats `a-z' as `the letters a through			 * z', but treats `a-a' as `the letter a, the			 * character -, and the letter a'.			 *			 * For compatibility, the `-' is not considerd			 * to define a range if the character following			 * it is either a close bracket (required by ANSI)			 * or is not numerically greater than the character			 * we just stored in the table (c).			 */			n = *fmt;			if (n == ']' || n < c) {				c = '-';				break;	/* resume the for(;;) */			}			fmt++;			/* fill in the range */			do {			    tab[++c] = v;			} while (c < n);			c = n;			/*			 * Alas, the V7 Unix scanf also treats formats			 * such as [a-c-e] as `the letters a through e'.			 * This too is permitted by the standard....			 */			goto doswitch;			break;		case ']':		/* end of scanset */			return (fmt);		default:		/* just another character */			c = n;			break;		}	}	/* NOTREACHED */}/* * Convert a string to an unsigned quad integer. * * Ignores `locale' stuff.  Assumes that the upper and lower case * alphabets and digits are each contiguous. */u_quad_tstrtouq(const char *nptr, char **endptr, int base){	const char *s = nptr;	u_quad_t acc;	unsigned char c;	u_quad_t qbase, cutoff;	int neg, any, cutlim;	/*	 * See strtoq for comments as to the logic used.	 */	s = nptr;	do {		c = *s++;	} while (isspace(c));	if (c == '-') {		neg = 1;		c = *s++;	} else {		neg = 0;		if (c == '+')			c = *s++;	}	if ((base == 0 || base == 16) &&	    c == '0' && (*s == 'x' || *s == 'X')) {		c = s[1];		s += 2;		base = 16;	}	if (base == 0)		base = c == '0' ? 8 : 10;	qbase = (unsigned)base;	cutoff = (u_quad_t)UQUAD_MAX / qbase;	cutlim = (u_quad_t)UQUAD_MAX % qbase;	for (acc = 0, any = 0;; c = *s++) {		if (!isascii(c))			break;		if (isdigit(c))			c -= '0';		else if (isalpha(c))			c -= isupper(c) ? 'A' - 10 : 'a' - 10;		else			break;		if (c >= base)			break;		if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))			any = -1;		else {			any = 1;			acc *= qbase;			acc += c;		}	}	if (any < 0) {		acc = UQUAD_MAX;	} else if (neg)		acc = -acc;	if (endptr != 0)		*endptr = (char *)(any ? s - 1 : nptr);	return (acc);}/* * Convert a string to a quad integer. * * Ignores `locale' stuff.  Assumes that the upper and lower case * alphabets and digits are each contiguous. */quad_tstrtoq(const char *nptr, char **endptr, int base){	const char *s;	u_quad_t acc;	unsigned char c;	u_quad_t qbase, cutoff;	int neg, any, cutlim;	/*	 * Skip white space and pick up leading +/- sign if any.	 * If base is 0, allow 0x for hex and 0 for octal, else	 * assume decimal; if base is already 16, allow 0x.	 */	s = nptr;	do {		c = *s++;	} while (isspace(c));	if (c == '-') {		neg = 1;		c = *s++;	} else {		neg = 0;		if (c == '+')			c = *s++;	}	if ((base == 0 || base == 16) &&	    c == '0' && (*s == 'x' || *s == 'X')) {		c = s[1];		s += 2;		base = 16;	}	if (base == 0)		base = c == '0' ? 8 : 10;	/*	 * Compute the cutoff value between legal numbers and illegal	 * numbers.  That is the largest legal value, divided by the	 * base.  An input number that is greater than this value, if	 * followed by a legal input character, is too big.  One that	 * is equal to this value may be valid or not; the limit	 * between valid and invalid numbers is then based on the last	 * digit.  For instance, if the range for quads is	 * [-9223372036854775808..9223372036854775807] and the input base	 * is 10, cutoff will be set to 922337203685477580 and cutlim to	 * either 7 (neg==0) or 8 (neg==1), meaning that if we have	 * accumulated a value > 922337203685477580, or equal but the	 * next digit is > 7 (or 8), the number is too big, and we will	 * return a range error.	 *	 * Set any if any `digits' consumed; make it negative to indicate	 * overflow.	 */	qbase = (unsigned)base;	cutoff = neg ? (u_quad_t)-(QUAD_MIN + QUAD_MAX) + QUAD_MAX : QUAD_MAX;	cutlim = cutoff % qbase;	cutoff /= qbase;	for (acc = 0, any = 0;; c = *s++) {		if (!isascii(c))			break;		if (isdigit(c))			c -= '0';		else if (isalpha(c))			c -= isupper(c) ? 'A' - 10 : 'a' - 10;		else			break;		if (c >= base)			break;		if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))			any = -1;		else {			any = 1;			acc *= qbase;			acc += c;		}	}	if (any < 0) {		acc = neg ? QUAD_MIN : QUAD_MAX;	} else if (neg)		acc = -acc;	if (endptr != 0)		*endptr = (char *)(any ? s - 1 : nptr);	return (acc);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?