Following up: a UTF-8 plain text checker.  Only lightly tested, and I'm
not yet clear how to integrate this cleanly with the sendlib.c routines.

-- Andras Salamon                   and...@dns.net
/*
 * is_utf8_text(): check if buffer is valid UTF-8 text; exclude control chars
 * via RFC 3629
 */

#include <stddef.h>

#define O '\000'
#define I '\001'

/* allow control characters ESC BEL BS HT LF VT FF CR, 7-bit text, UTF-8 */
const char BADFIRST[] = {
I,I,I,I,I,I,I,O,O,O,O,O,O,O,I,I, /* 0x0_ */
I,I,I,I,I,I,I,I,I,I,I,O,I,I,I,I, /* 0x1_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0x2_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0x3_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0x4_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0x5_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0x6_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,I, /* 0x7_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x8_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x9_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0xa_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0xb_ */
I,I,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0xc_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0xd_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0xe_ */
O,O,O,O,O,I,I,I,I,I,I,I,I,I,I,I  /* 0xf_ */
};

/* allow 10xxxxxx */
const char BADCONT[] = {
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x0_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x1_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x2_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x3_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x4_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x5_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x6_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0x7_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0x8_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0x9_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0xa_ */
O,O,O,O,O,O,O,O,O,O,O,O,O,O,O,O, /* 0xb_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0xc_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0xd_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, /* 0xe_ */
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I  /* 0xf_ */
};

/*
 * check if plain UTF-8 text
 * returns:
 *  0  uses unusual control characters, or is invalid UTF-8
 *  1  valid UTF-8
 */
int
is_utf8_text(const unsigned char *buf, size_t numbytes) {
        size_t i;

/* overlong:
 * 4-byte 11110000 1000.... 0xf0 0x8.
 * 3-byte 11100000 100..... 0xe0 0x[8|9].
 * 2-byte 1100000x          0xc[01]
 * invalid:
 * 11101101 101.....        0xed 0x[ab].      U+D800 to   U+DFFF
 * 11110100 1001....        0xf4 0x9.         U+110000 to U+11FFFF
 * 11110100 101.....        0xf4 0x[ab].      U+120000 to U+13FFFF
 *                          0xf[5-9a-f]       U+140000 to U+1FFFFF
 */
        for (i = 0; i < numbytes; i++) {
                if (BADFIRST[buf[i]])
                        return 0;
                /* the remainder can now assume first byte is reasonable */
                if ((buf[i] & 0xc0) == 0xc0) {      /* UTF-8 byte1 11xxxxxx */
                        if ((buf[i] & 0x20) == 0) {             /* 110xxxxx */
                                if (i+1 >= numbytes)
                                        return 0;
                                if (BADCONT[buf[i+1]])
                                        return 0;
                                i++;
                        } else if ((buf[i] & 0x10) == 0) {      /* 1110xxxx */
                                if (i+2 >= numbytes)
                                        return 0;
                                if (BADCONT[buf[i+1]])
                                        return 0;
                                if (BADCONT[buf[i+2]])
                                        return 0;
                                if ((buf[i] == 0xed) && ((buf[i+1] & 0x20) != 
0))
                                        return 0;
                                i += 2;
                        } else {                        /* must be 11110xxx */
                                if (i+3 >= numbytes)
                                        return 0;
                                if (BADCONT[buf[i+1]])
                                        return 0;
                                if (BADCONT[buf[i+2]])
                                        return 0;
                                if (BADCONT[buf[i+3]])
                                        return 0;
                                if ((buf[i] == 0xf4) && ((buf[i+1] & 0x30) != 
0))
                                        return 0;
                                i += 3;
                        }
                }
        }
        return 1;
}

Reply via email to