Page 159

Write a private version of scanf analogous to minprintf from the previous section.

#include <stdbool.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>

#define MAX_FMT 100

enum state { NORMAL, FORMAT };

int minscanf(char *fmt, ...)
{
	va_list ap;
	char scanf_fmt[MAX_FMT];
	char *p = scanf_fmt;
	int c;
	int num_found = 0;
	bool suppress;
	enum state state;
	int *iptr;
	unsigned *uiptr;
	char *cptr;
	double *dptr;

	va_start(ap, fmt);
	for (state = NORMAL; *fmt != '\0' && c != EOF; fmt++) {
		switch (state) {
		case NORMAL:
			if (*fmt == '%') {
				state = FORMAT;
				*p++ = '%';
			} else if ((c = getchar()) != *fmt) {
				return num_found;
			}
			break;
		case FORMAT:
			suppress = *fmt == '*';
			for (; isdigit(*p = *fmt); p++, fmt++)
				;
			for (; (*p = *fmt) == 'h' || *p == 'l' || *p == 'L';
			     p++, fmt++)
				;
			*(p + 1) = '\0';
			switch (*p) {
			case 'd':
			case 'i':
			case 'o':
			case 'x':
				iptr = va_arg(ap, int *);
				num_found += scanf(scanf_fmt, iptr);
				break;
			case 'e':
			case 'f':
			case 'g':
				dptr = va_arg(ap, double *);
				num_found += scanf(scanf_fmt, dptr);
				break;
			case 's':
				cptr = va_arg(ap, char *);
				num_found += scanf(scanf_fmt, cptr);
				break;
			case 'u':
				uiptr = va_arg(ap, unsigned *);
				num_found += scanf(scanf_fmt, uiptr);
				break;
			default:
				return num_found;
			}
			state = NORMAL;
			break;
		}
	}

	va_end(ap);
	return num_found;
}

int main()
{
	/* Has not been tested with other inputs and does not have any error handling. */

	int i = 0;
	int count = minscanf("Hello %d world", &i);
	printf("%d %d\n", count, i);
}