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; }