#include #include extern char _numstr[]; /* #define skip() do{c=(*get)(ip); if (c<1) goto done;}while(isspace(c))*/ #define skip() while(isspace(c)) { if ((c=(*get)(ip))<1) goto done; } #define char_downcase(foo) (isupper(foo) ? tolower(foo) : foo) #define char_upcase(foo) (islower(foo) ? toupper(foo) : foo) _scanf(ip, get, unget, fmt, args) register unsigned char *ip; int (*get)(); int (*unget)(); register unsigned char *fmt; char **args; /* * _scanf (ver. 1.1c) written 12-21-87 by John Stanley * for DLibs public-domain C source library. * * points to a format control string. pointers to a * list of arguments, each of which is the address of a variable in * which input data may be stored. The format string is used to * control reading of characters from the function. As each * character is needed is called in the form "c = (*get)(ip);" * where is the character read (negative for errors) and is * the auxiliary pointer specified by the parameter. If a * character needs to be un-gotten, a call to of the form * "(*unget)(c, ip);" is made. The format string is composed of * characters and format specifications. Any characters in , * except whitespace characters, which are not part of a format * specifier are expected to be matched one-to-one by characters in * the input stream. Scanning terminates if a mismatch occurs or if * any call to results in an error. Whitespace characters * match 0 or more whitespace characters in the input stream. The * '%' character introduces a format specifier. The general form of * a format specifier is: * * %[*][][l|h]{d|u|o|x|b|i|c|s} * * The '*' specifies that a field is to be scanned by not stored. * No variable pointer should be provided for non-stored format * specs. The field specifies that maximum number of * characters to be process to fill the given format type. Less * than characters will be processed if the field ends * before characters have been processed. A field ends when * either a whitespace character, or a character which does not fit * the specified format, is read. The preceding 'l' (or * capitalizing the conversion character) specifies that the * associated variable is a "long" type. The trailing character * specifies the format type, as follows: * * d Signed decimal integer * u Unsigned decimal integer * o Unsigned octal integer * x Unsigned hexadecimal integer * b Unsigned binary integer * i Unsigned decimal/octal/hexadecimal/binary integer * c Character * s String * * If a is specified with the 'c' format, exactly * characters (including whitespace) are read from the input stream, * and written to a string. No '\0' character is added If the * character following the '%' is not recognized, it is expected to * match the input stream as a non-format character, thus "%%" is * used to match a single '%' character. * * One additional conversion is the brace-format. Shown as "%[...]", * the '...' represent a list of characters. If the first character * in the list is a '^', the field contains any characters -not- in * the list (starting with the 1st character after the '^'). If the * first character of the list is not a '^', then the field will * only contain those characters found in the list. A right brace * character (']') can be included as one of the list of characters * by placing it as the first character in the list. If the '^' * negation character is the first character, the included brace * should be the next character after the '^'. For maximum * portability, a range should be explicitly given (a good example * would be "%[0123456789]"), but to allow for porting from * systems with smarter scanf functions, this version of scanf * also supports ranges represented using a - * form (eg: "%[0-9]"). To use the first-last form, the * character must be lexically less than or equal to * the character . If this rule is violated, or if the * hyphen is the first or last character of the list, the * hyphen will be assumed to be just another character in the * list and no range expansion will be done. The resulting * string containing the characters in (or not in) the list * will be null terminated. It should be noted that, unlike * most of the other formats, this conversion does allow the * programmer to specify that whitespace characters will be * included in the resulting string. * */ { register long n; register int c, width, lval, cnt = 0; int store, neg, base, wide1, endnull, rngflag, c2; register unsigned char *p; unsigned char delim[128], digits[17], *q; char *strchr(), *strcpy(); if (!*fmt) return(0); c = (*get)(ip); while(c > 0) { store = FALSE; if (*fmt == '%') { n = 0; width = -1; wide1 = 1; base = 10; lval = FALSE; store = TRUE; endnull = TRUE; neg = -1; strcpy(delim, "\011\012\013\014\015 "); strcpy(digits, "01234567890ABCDEF"); if (fmt[1] == '*') { endnull = store = FALSE; ++fmt; } while (isdigit(*++fmt)) /* width digit(s) */ { if (width == -1) width = 0; wide1 = width = (width * 10) + (*fmt - '0'); } --fmt; fmtnxt: ++fmt; /* switch(tolower(*fmt)) /* tolower() is a MACRO! */ switch(char_downcase(*fmt)) { case '*': endnull = store = FALSE; goto fmtnxt; case 'l': /* long data */ lval = TRUE; /* for compatability --> */ case 'h': /* short data */ goto fmtnxt; case 'i': /* any-base numeric */ base = 0; goto numfmt; case 'b': /* unsigned binary */ base = 2; goto numfmt; case 'o': /* unsigned octal */ base = 8; goto numfmt; case 'x': /* unsigned hexadecimal */ base = 16; goto numfmt; case 'd': /* SIGNED decimal */ neg = FALSE; /* FALL-THRU */ case 'u': /* unsigned decimal */ numfmt: skip(); if (isupper(*fmt)) lval = TRUE; if (!base) { base = 10; neg = FALSE; if (c == '%') { base = 2; goto skip1; } else if (c == '0') { c = (*get)(ip); if (c < 1) goto savnum; if ((c != 'x') && (c != 'X')) { base = 8; digits[8]= '\0'; goto zeroin; } base = 16; goto skip1; } } if ((neg == FALSE) && (base == 10) && ((neg = (c == '-')) || (c == '+'))) { skip1: c = (*get)(ip); if (c < 1) goto done; } digits[base] = '\0'; p = strchr(digits,char_upcase(c)); if ((!c || !p) && width) goto done; while (p && width-- && c) { n = (n * base) + (p - digits); c = (*get)(ip); zeroin: p = strchr(digits,char_upcase(c)); } savnum: if (store) { p = *args; if (neg == TRUE) n = -n; if (lval) *((long*) p) = n; else *((int *) p) = n; ++cnt; } break; case 'c': /* character data */ width = wide1; endnull = FALSE; delim[0] = '\0'; goto strproc; case '[': /* string w/ delimiter set */ /* get delimiters */ p = delim; if (*++fmt == '^') fmt++; else lval = TRUE; rngflag = 2; if ((*fmt == ']') || (*fmt == '-')) { *p++ = *fmt++; rngflag = FALSE; } while (*fmt != ']') { if (*fmt == '\0') goto done; switch (rngflag) { case TRUE: c2 = *(p-2); if (c2 <= *fmt) { p -= 2; while (c2 < *fmt) *p++ = c2++; rngflag = 2; break; } /* fall thru intentional */ case FALSE: rngflag = (*fmt == '-'); break; case 2: rngflag = FALSE; } *p++ = *fmt++; } *p = '\0'; goto strproc; case 's': /* string data */ skip(); strproc: /* process string */ p = *args; /* if the 1st char fails, match fails */ if (width) { q = strchr(delim, c); if ((c < 1) || (lval ? !(q != 0) : (q != 0))) { if (endnull) *p = '\0'; goto done; } } for (;;) /* FOREVER */ { if (store) *p++ = c; if (((c = (*get)(ip)) < 1) || (--width == 0)) break; q = strchr(delim, c); if (lval ? !(q != 0) : (q != 0)) break; } if (store) { if (endnull) *p = '\0'; ++cnt; } break; case '\0': /* early EOS */ --fmt; /* FALL THRU */ default: goto cmatch; } } else if (isspace(*fmt)) /* skip whitespace */ { skip(); } else { /* normal match char */ cmatch: if (c != *fmt) break; c = (*get)(ip); } if (store) args++; if (!*++fmt) break; } done: /* end of scan */ if ((c < 0) && (cnt == 0)) return(EOF); (*unget)(c, ip); return(cnt); }