/*
 * Scanf code.  Extend File namespace with reading code
 */
extend namespace File {
    public int fscanf (file f, string format, *poly args ...)
	/*
	 * According to 'format', read from 'f' to 'args'
	 */
    {
	/* Skip whitespace */
	void whitespace ()
	{
	    int c;

	    while (!end (f))
		if (!Ctype::isspace (c = getc (f))) {
		    ungetc (c, f);
		    break;
		}
	}

	bool isbinary (int c)
	{
	    if ('0' <= c && c <= '1')
		return true;
	    if (c == '-')
		return true;
	    return false;
	}
	
	bool isoctal (int c)
	{
	    if ('0' <= c && c <= '7')
		return true;
	    if (c == '-')
		return true;
	    return false;
	}
	
	bool isdecimal (int c)
	{
	    if ('0' <= c && c <= '9')
		return true;
	    if (c == '-')
		return true;
	    return false;
	}

	bool ishex (int c)
	{
	    if ('0' <= c && c <= '9')
		return true;
	    if ('a' <= c && c <= 'f')
		return true;
	    if ('A' <= c && c <= 'F')
		return true;
	    if (c == '-')
		return true;
	    return false;
	}

	bool isfloat (int c)
	{
	    if ('0' <= c && c <= '9')
		return true;
	    if (c == 'e')
		return true;
	    if (c == '-')
		return true;
	    if (c == '.')
		return true;
	    if (c == '{' || c == '}')
		return true;
	    return false;
	}

	/* return next integer in input */
	int integer (bool(int c) test, int base)
	{
	    int	    c;
	    string  s = "";

	    whitespace();
	    while (!end (f)) {
		if (!test (c = getc (f))) {
		    ungetc (c, f);
		    break;
		}
		s = s + String::new(c);
	    }
	    return string_to_integer (s, base);
	}

	/* return next number in input */
	real number (bool(int c) test)
	{
	    int	    c;
	    string  s = "";

	    whitespace();
	    while (!end (f)) {
		if (!test (c = getc (f))) {
		    ungetc (c, f);
		    break;
		}
		s = s + String::new(c);
	    }
	    return string_to_real (s);
	}

	string word ()
	{
	    whitespace();
	    string  s = "";
	    while (!end (f)) {
		int c = getc(f);
		if (!Ctype::isgraph (c)) {
		    ungetc (c, f);
		    break;
		}
		s = s + String::new(c);
	    }
	    return s;
	}

	int	i = 0;
	int	argc = 0;
	int	c;

	while (i < String::length (format) && !end(f) && !error(f))
	{
	    switch (format[i]) {
	    case ' ':
	    case '\t':
		whitespace ();
		break;
	    case '%':
		i++;
		switch (format[i]) {
		case 'b': case 'B':
		    *args[argc++] = integer (isbinary, 2);
		    break;
		case 'o': case 'O':
		    *args[argc++] = integer (isoctal, 8);
		    break;
		case 'd': case 'D':
		    *args[argc++] = integer (isdecimal, 10);
		    break;
		case 'x': case 'X':
		    *args[argc++] = integer (ishex, 16);
		    break;
		case 'e': case 'E':
		case 'f': case 'F':
		case 'g': case 'G':
		    *args[argc++] = number(isfloat);
		    break;
		case 'c':
		    *args[argc++] = getc(f);
		    break;
		case 's':
		    *args[argc++] = word();
		    break;
		default:
		    if (end (f))
			return argc;
		    c = getc(f);
		    if (c != format[i]) {
			ungetc (c, f);
			return argc;
		    }
		    break;
		}
		break;
	    default:
		if (end (f))
		    return argc;
		c = getc(f);
		if (c != format[i]) {
		    ungetc (c, f);
		    return argc;
		}
		break;
	    }
	    i++;
	}
	return argc;
    }

    public int sscanf (string s, string format, *poly args...)
	/*
	 * According to 'format', read from 's' to 'args'
	 */
    {
        file sf = string_read(s);
        int n = fscanf (sf, format, args ...);
        close(sf);
        return n;
    }

    public namespace ScanfGlobals {
	
	public int scanf (string format, *poly args...)
	    /*
	     * According to 'format', read from stdin to 'args'
	     */
	{
	    return fscanf (stdin, format, args ...);
	}

    }

    public import ScanfGlobals;
}
import File::ScanfGlobals;
