123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- /*
- * doscan.c - scan formatted input
- */
- /* $Header$ */
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
- #include <stdarg.h>
- #include "loc_incl.h"
- #define NUMLEN 512
- #define NR_CHARS 256
- static char Xtable[NR_CHARS];
- static char inp_buf[NUMLEN];
- /* Collect a number of characters which constitite an ordinal number.
- * When the type is 'i', the base can be 8, 10, or 16, depending on the
- * first 1 or 2 characters. This means that the base must be adjusted
- * according to the format of the number. At the end of the function, base
- * is then set to 0, so strtol() will get the right argument.
- */
- static char *
- o_collect(register int c, register FILE *stream, char type,
- int width, int *basep)
- {
- register char *bufp = inp_buf;
- register int base;
- switch (type) {
- case 'i': /* i means octal, decimal or hexadecimal */
- case 'p':
- case 'x':
- case 'X': base = 16; break;
- case 'd':
- case 'u': base = 10; break;
- case 'o': base = 8; break;
- case 'b': base = 2; break;
- }
- if (c == '-' || c == '+') {
- *bufp++ = c;
- if (--width);
- c = getc(stream);
- }
- if (width && c == '0' && base == 16) {
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- if (c != 'x' && c != 'X') {
- if (type == 'i') base = 8;
- }
- else if (width) {
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- }
- }
- else if (type == 'i') base = 10;
- while (width) {
- if (((base == 10) && isdigit(c))
- || ((base == 16) && isxdigit(c))
- || ((base == 8) && isdigit(c) && (c < '8'))
- || ((base == 2) && isdigit(c) && (c < '2'))) {
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- }
- else break;
- }
- if (width && c != EOF) ungetc(c, stream);
- if (type == 'i') base = 0;
- *basep = base;
- *bufp = '\0';
- return bufp - 1;
- }
- #ifndef NOFLOAT
- /* The function f_collect() reads a string that has the format of a
- * floating-point number. The function returns as soon as a format-error
- * is encountered, leaving the offending character in the input. This means
- * that 1.el leaves the 'l' in the input queue. Since all detection of
- * format errors is done here, _doscan() doesn't call strtod() when it's
- * not necessary, although the use of the width field can cause incomplete
- * numbers to be passed to strtod(). (e.g. 1.3e+)
- */
- static char *
- f_collect(register int c, register FILE *stream, register int width)
- {
- register char *bufp = inp_buf;
- int digit_seen = 0;
- if (c == '-' || c == '+') {
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- }
- while (width && isdigit(c)) {
- digit_seen++;
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- }
- if (width && c == '.') {
- *bufp++ = c;
- if(--width)
- c = getc(stream);
- while (width && isdigit(c)) {
- digit_seen++;
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- }
- }
- if (!digit_seen) {
- if (width && c != EOF) ungetc(c, stream);
- return inp_buf - 1;
- }
- else digit_seen = 0;
- if (width && (c == 'e' || c == 'E')) {
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- if (width && (c == '+' || c == '-')) {
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- }
- while (width && isdigit(c)) {
- digit_seen++;
- *bufp++ = c;
- if (--width)
- c = getc(stream);
- }
- if (!digit_seen) {
- if (width && c != EOF) ungetc(c,stream);
- return inp_buf - 1;
- }
- }
- if (width && c != EOF) ungetc(c, stream);
- *bufp = '\0';
- return bufp - 1;
- }
- #endif /* NOFLOAT */
- /*
- * the routine that does the scanning
- */
- int
- _doscan(register FILE *stream, const char *format, va_list ap)
- {
- int done = 0; /* number of items done */
- int nrchars = 0; /* number of characters read */
- int conv = 0; /* # of conversions */
- int base; /* conversion base */
- unsigned long val; /* an integer value */
- char *str, *tmp_string; /* temporary pointers */
- unsigned width; /* width of field */
- int flags; /* some flags */
- int reverse; /* reverse the checking in [...] */
- int kind;
- register int ic; /* the input character */
- #ifndef NOFLOAT
- long double ld_val;
- #endif
- while (1) {
- if (isspace(*format)) {
- while (isspace(*format))
- ++format; /* skip whitespace */
- ic = getc(stream);
- nrchars++;
- while (isspace (ic)) {
- ic = getc(stream);
- nrchars++;
- }
- if (ic != EOF) ungetc(ic,stream);
- nrchars--;
- }
- if (!*format) break; /* end of format */
- ic = getc(stream);
- nrchars++;
- if (ic == EOF)
- return conv ? done : EOF;
- if (*format != '%') {
- if (ic != *format)
- break; /* matching error */
- ++format;
- continue;
- }
- ++format;
- if (*format == '%') {
- if (ic == '%') {
- format++;
- continue;
- }
- else break;
- }
- flags = 0;
- if (*format == '*') {
- ++format;
- flags |= FL_NOASSIGN;
- }
- if (isdigit (*format)) {
- flags |= FL_WIDTHSPEC;
- for (width = 0; isdigit (*format);)
- width = width * 10 + *format++ - '0';
- }
- switch (*format) {
- case 'h': flags |= FL_SHORT; format++; break;
- case 'l': flags |= FL_LONG; format++; break;
- case 'L': flags |= FL_LONGDOUBLE; format++; break;
- }
- kind = *format;
- if ((kind != 'c') && (kind != '[') && (kind != 'n')) {
- while (ic != EOF && isspace(ic)) {
- ic = getc(stream);
- nrchars++;
- }
- if (ic == EOF) return conv ? done : EOF;
- }
- conv++;
- switch (kind) {
- default:
- if (kind == ic) continue;
- break;
- case 'n':
- if (ic != EOF) ungetc(ic, stream);
- nrchars--;
- if (flags & FL_SHORT)
- *va_arg(ap, short *) = (short) nrchars;
- else if (flags & FL_LONG)
- *va_arg(ap, long *) = (long) nrchars;
- else
- *va_arg(ap, int *) = (int) nrchars;
- break;
- case 'b': /* binary */
- case 'd': /* decimal */
- case 'i': /* general integer */
- case 'o': /* octal */
- case 'p': /* pointer */
- case 'u': /* unsigned */
- case 'x': /* hexadecimal */
- case 'X': /* ditto */
- if (!(flags & FL_WIDTHSPEC))
- width = NUMLEN;
- if (!width) {
- if (ic != EOF) ungetc(ic, stream);
- return done;
- }
- str = o_collect(ic, stream, kind, width, &base);
- if (str < inp_buf) return done;
- nrchars += str - inp_buf + 1;
- if (!(flags & FL_NOASSIGN)) {
- if (kind == 'd' || kind == 'i')
- val = strtol(inp_buf, &tmp_string, base);
- else
- val = strtoul(inp_buf, &tmp_string, base);
- if (flags & FL_LONG)
- *va_arg(ap, unsigned long *) = (unsigned long) val;
- else if (flags & FL_SHORT)
- *va_arg(ap, unsigned short *) = (unsigned short) val;
- else
- *va_arg(ap, unsigned *) = (unsigned) val;
- done++;
- }
- break;
- case 'c':
- if (!(flags & FL_WIDTHSPEC))
- width = 1;
- if (!(flags & FL_NOASSIGN))
- tmp_string = va_arg(ap, char *);
- if (!width) {
- if (ic != EOF) ungetc(ic, stream);
- return done;
- }
- if (!(flags & FL_NOASSIGN) && (ic != EOF))
- ++done;
- while (width && ic != EOF) {
- if (!(flags & FL_NOASSIGN))
- *tmp_string++ = (char) ic;
- if (--width) {
- ic = getc(stream);
- nrchars++;
- }
- }
- if (width) {
- if (ic != EOF) ungetc(ic,stream);
- nrchars--;
- }
- break;
- case 's':
- if (!(flags & FL_WIDTHSPEC))
- width = 0xffff;
- if (!(flags & FL_NOASSIGN))
- tmp_string = va_arg(ap, char *);
- if (!width) {
- if (ic != EOF) ungetc(ic,stream);
- return done;
- }
- if (!(flags & FL_NOASSIGN))
- ++done;
- while (width && ic != EOF && !isspace(ic)) {
- if (!(flags & FL_NOASSIGN))
- *tmp_string++ = (char) ic;
- if (--width) {
- ic = getc(stream);
- nrchars++;
- }
- }
- /* terminate the string */
- if (!(flags & FL_NOASSIGN))
- *tmp_string = '\0';
- if (width) {
- if (ic != EOF) ungetc(ic,stream);
- nrchars--;
- }
- break;
- case '[':
- { int old_nrchars;
- if (!(flags & FL_WIDTHSPEC))
- width = 0xffff;
- if (!width) {
- if (ic != EOF) ungetc(ic, stream);
- return done;
- }
- if ( *(++format) == '^' ) {
- reverse = 1;
- format++;
- } else
- reverse = 0;
- for (tmp_string = Xtable; tmp_string < &Xtable[NR_CHARS]
- ; tmp_string++)
- *tmp_string = 0;
- if (*format == ']') Xtable[*format++] = 1;
- while (*format && *format != ']') {
- Xtable[*format++] = 1;
- if (*format == '-') {
- format++;
- if (*format
- && *format != ']'
- && *(format) >= *(format -2)) {
- int c;
- for( c = *(format -2) + 1
- ; c <= *format ; c++)
- Xtable[c] = 1;
- format++;
- }
- else Xtable['-'] = 1;
- }
- }
- if (!*format) {
- if (ic != EOF) ungetc(ic,stream);
- return done;
- }
-
- old_nrchars = nrchars;
- if (!(flags & FL_NOASSIGN))
- tmp_string = va_arg(ap, char *);
- if (ic == EOF || !(Xtable[ic] ^ reverse)) {
- if (ic != EOF) ungetc(ic, stream);
- return done;
- }
- while (width && ic != EOF && (Xtable[ic] ^ reverse)) {
- if (!(flags & FL_NOASSIGN))
- *tmp_string++ = (char) ic;
- if (--width) {
- ic = getc(stream);
- nrchars++;
- }
- }
- if (width) {
- if (ic != EOF) ungetc(ic, stream);
- nrchars--;
- }
- if (!(flags & FL_NOASSIGN)) { /* terminate string */
- *tmp_string = '\0';
- ++done;
- }
- break;
- }
- #ifndef NOFLOAT:
- case 'e':
- case 'E':
- case 'f':
- case 'g':
- case 'G':
- if (!(flags & FL_WIDTHSPEC)) width = NUMLEN;
- if (width > NUMLEN) width = NUMLEN;
- if (!width) return done;
- str = f_collect(ic, stream, width);
- if (str < inp_buf) return done;
- nrchars += str - inp_buf + 1;
- if (!(flags & FL_NOASSIGN)) {
- ld_val = strtod(inp_buf, &tmp_string);
- if (flags & FL_LONGDOUBLE)
- *va_arg(ap, long double *) = (long double) ld_val;
- else
- if (flags & FL_LONG)
- *va_arg(ap, double *) = (double) ld_val;
- else
- *va_arg(ap, float *) = (float) ld_val;
- done++;
- }
- break;
- #endif
- } /* end switch */
- ++format;
- }
- return conv ? done : EOF;
- }
|