Hi Dave,

Here's a cut-down scanf I wrote for the AVR (gcc).  You'll have to see 
if it builds in SDCC because I haven't tried it.  There's a bit of 
jiggery pokery going on with read_ram and read_rom because the format 
string comes from AVR ROM space and the scanned string is in RAM.

Licence: Use at your own risk.  Help is not guaranteed.


// === Scanf ===

bool in_set(prog_char *set, char c, short size) {
    /* Searches max size chars at string set to find character c
     * set is NOT required to be NUL-terminated.
     */
    while (size--) if (pgm_read_byte(set++) == c) return true;
    return false;
}

char read_ram(char *addr) {
    return *addr;
}
char read_rom(char *addr) {
    return pgm_read_byte(addr);
}

char hex2num(char hex) {
    if (hex>='A') {
        if (hex>='a') hex -= 'a'-'A';   // tolower
        hex -= 'A'-'9'-1;               // todigit
    }
    return hex-'0';                      // tohex
}

// atoi that terminates at maxlen and adjust input string to point to 
character that conversion stopped at)
//  Note: a maxlen of -1 equals a length of 65535 which is essentially 
infinite.
short bounded_atoi(char fetch(char *), prog_char **s_p, short maxlen) {
    short number=0;
    bool sign=false;
    char c;
    prog_char *s = *s_p;

    if (fetch(s) == '-') {
        sign = true;
        s++;
    }
    while (maxlen-- and isdigit(c=fetch(s))) {
        number = (number<<3) + number + number; // *= 10
        number += c-'0';
        s++;
    }
    if (sign) number=-number;
    *s_p = s;
    return number;
}

// cut-down scanf
//  handles %% %s %[] %c %d %x (=%X) %n:
//  return no. conversions stored. Returns early if mismatch
// Notes:
//   - length modifier may be supplied after '%' and it defaults to 
65535 (or for %c, 1)
//   - %x requires length
//   - '*' modifier not allowed
//   - 'l' modifier not allowed
//   - %n does increment count returned (scanf docs are undecided on 
whether it should)
//   - no special whitespace handling, so a space matches just one space
unsigned char vxsscanf(char *s, prog_char *fmt, va_list args) {
    char *sstore, *set, *s0=s;
    char c, count;
    short length;
    bool contains;
    unsigned short *number;

    count = 0;                                  // how many conversions 
stored
    while (c=pgm_read_byte(fmt++), c) {
        if (c!='%') {
            if (*s++ != c) return count;        // match s against c
        } else {
            if (isdigit(pgm_read_byte(fmt))) {
                length = bounded_atoi(read_rom, &fmt, -1);
            } else length = -1;

            c = pgm_read_byte(fmt++);
            switch (c) {
                case 'n':
                    *va_arg(args, short *) = (short)(s-s0);
                    count++;
                    break;
                case '[':
                    sstore = va_arg(args, char*);
                    if (pgm_read_byte(fmt) == '^') {
                        contains = false;
                        fmt++;
                    } else contains = true;
                    set = fmt;
                    while (pgm_read_byte(fmt) and pgm_read_byte(fmt++) 
!= ']');

                    while (length-- and *s) {
                        if (in_set(set, *s, fmt-set) == contains) 
*sstore++=*s++;
                        else break;
                    }
                    *sstore = 0;
                    count++;
                    break;
                case 'c':                       // character
                    if (length==-1) length=1;
                case 's':
                    sstore = va_arg(args, char*);
                    while (length-- and *s) *sstore++=*s++;
                    if (c=='s') *sstore = 0;
                    count++;
                    break;
                case 'u':                       // unsigned short 
decimal number
                case 'd':                       // signed decimal number
                    sstore = s;
                    *va_arg(args, unsigned short *) = 
bounded_atoi(read_ram, &s, length);
                    if (s==sstore) return count; // no number found
                    count++;
                    break;
                case 'x':
                case 'X':
                    number = va_arg(args, unsigned short *);
                    *number = 0;
                    if (not isxdigit(*s)) return count;  // no hex 
number found
                    while (length-- and isxdigit(c=*s)) {
                        s++;
                        *number <<= 4;
                        *number += hex2num(c);
                    }
                    count++;
                    break;
                case '%':
                    if (*s++ != '%') return count;      // match s 
against '%'
                    break;
                default:
                    return count;                       // can't decode 
format string
            }
        }
    }
    return count;
}



-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08
_______________________________________________
Sdcc-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sdcc-user

Reply via email to