/* See LICENSE file for copyright and license details. */ #include "common.h" #ifndef TEST #define LEVEL_MASK 0x0003 #define ENCODING_LEVEL 1 #define MAPPING_LEVEL 2 #define MAX_LEVEL MAPPING_LEVEL #define HAVE_SIZE_BIT 0x4000 #define HAVE_FIRST_INDEX_BIT 0x2000 #define HAVE_FIRST_INDEX_BYTE_2_BIT 0x1000 #define HAVE_BITS (HAVE_SIZE_BIT | HAVE_FIRST_INDEX_BIT | HAVE_FIRST_INDEX_BYTE_2_BIT) enum keyword { NO_KEYWORD, STARTENCODING, ENDENCODING, ALIAS, SIZE, FIRSTINDEX, STARTMAPPING, ENDMAPPING, UNDEFINE }; static char * get_string(const char *s, const char **endp) { size_t len; const char *p; char *r; char *ret; for (len = 1, p = &s[1]; *p && *p != '\n' && *p != '#'; p++) len += (!isblank(*p) || !isblank(p[-1])); ret = malloc(len + 1); if (!ret) return NULL; r = ret; for (*r++ = *s++; *s && *s != '\n' && *s != '#'; s++) { if (!isblank(*s)) *r++ = *s; else if (!isblank(s[-1])) *r++ = ' '; } *r = '\0'; return ret; } static int get_uints(uintmax_t *u1p, uintmax_t *u2p, uintmax_t *u3p, uintmax_t max, const char *s, const char **endp, struct libfonts_context *ctx) { int r = 0; uintmax_t u, *up, digit; while (r < INT_MAX) { u = 0; if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && isxdigit(s[2])) { s = &s[2]; do { digit = (uintmax_t)((*s & 15) + (*s > '9' ? 9 : 0)); if (u > (max - digit) >> 4) goto overflow; u = (u << 4) | digit; } while (isxdigit(*++s)); } else if (s[0] == '0') { s = &s[1]; for (;; s++) { digit = (uintmax_t)(unsigned char)(*s - '0'); if (digit > 7) break; if (u > (max - digit) >> 3) goto overflow; u = (u << 3) | digit; } } else if (isdigit(s[0])) { do { digit = (uintmax_t)(*s & 15); if (u > (max - digit) / 10) goto overflow; u = u * 10 + digit; } while (isdigit(*++s)); } else { break; } if (0) { overflow: u = max; while (isdigit(*++s)); warning(ctx, 0, "libfonts_parse_encoding_line", "value too large: in excess of 0x%jX", max); } up = (r == 0 ? u1p : r == 1 ? u2p : r == 2 ? u3p : NULL); if (up) *up = u; r += 1; if (!*s || *s == '\n' || *s == '#') break; while (isblank(*s)) s++; } *endp = s; return r; } int libfonts_parse_encoding_line(struct libfonts_encoding **encodingp, int *statep, const char *line, char **endp, struct libfonts_context *ctx) { int ret = 0, r; size_t keyword_len; uintmax_t ju1, ju2, ju3; enum keyword keyword; struct libfonts_encoding_mapping *mapping; void *new; if (!encodingp || !statep || !line || (*statep & LEVEL_MASK) > MAX_LEVEL || ((*statep & LEVEL_MASK) ? (*statep & ~(LEVEL_MASK | HAVE_BITS)) : *statep) || (*statep && !*encodingp) || ((*statep & LEVEL_MASK) == MAPPING_LEVEL && !(*encodingp)->nmappings) || (*encodingp && ((!(*encodingp)->aliases && (*encodingp)->naliases) || (!(*encodingp)->mappings && (*encodingp)->nmappings) || (*encodingp)->size_rows > 0x100 || (*encodingp)->size_cols > 0x100))) { errno = EINVAL; goto fail; } while (isblank(*line)) line++; if (!*line || *line == '#') goto out; if (!strncasecmp(line, "STARTENCODING", keyword_len = sizeof("STARTENCODING") - 1)) keyword = STARTENCODING; else if (!strncasecmp(line, "ENDENCODING", keyword_len = sizeof("ENDENCODING") - 1)) keyword = ENDENCODING; else if (!strncasecmp(line, "ALIAS", keyword_len = sizeof("ALIAS") - 1)) keyword = ALIAS; else if (!strncasecmp(line, "SIZE", keyword_len = sizeof("SIZE") - 1)) keyword = SIZE; else if (!strncasecmp(line, "FIRSTINDEX", keyword_len = sizeof("FIRSTINDEX") - 1)) keyword = FIRSTINDEX; else if (!strncasecmp(line, "STARTMAPPING", keyword_len = sizeof("STARTMAPPING") - 1)) keyword = STARTMAPPING; else if (!strncasecmp(line, "ENDMAPPING", keyword_len = sizeof("ENDMAPPING") - 1)) keyword = ENDMAPPING; else if (!strncasecmp(line, "UNDEFINE", keyword_len = sizeof("UNDEFINE") - 1)) keyword = UNDEFINE; else if (!isdigit(*line)) goto bad_keyword; else keyword = NO_KEYWORD; if (keyword != NO_KEYWORD) { if (line[keyword_len] && !isblank(line[keyword_len])) goto bad_keyword; line = &line[keyword_len]; while (isblank(*line)) line++; } switch (keyword) { case STARTENCODING: if (*statep != 0) { warning(ctx, 0, "libfonts_parse_encoding_line", "STARTENCODING nested inside another STARTENCODING"); break; } if (!*encodingp) { *encodingp = calloc(1, sizeof(**encodingp)); if (!*encodingp) goto enomem; } (*encodingp)->size_rows = 256; (*encodingp)->name = get_string(line, &line); if (!(*encodingp)->name) goto enomem; if (!*(*encodingp)->name) warning(ctx, 0, "libfonts_parse_encoding_line", "empty string as name of encoding"); *statep = ENCODING_LEVEL; break; case ENDENCODING: if ((*statep & LEVEL_MASK) == MAPPING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "missing ENDMAPPING"); } else if ((*statep & LEVEL_MASK) != ENCODING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "ENDENCODING without corresponding STARTENCODING"); break; } if (*line && *line != '\n' && *line != '#') warning(ctx, 0, "libfonts_parse_encoding_line", "unexpected data after ENDENCODING keyword"); if ((*statep & HAVE_FIRST_INDEX_BYTE_2_BIT) && (*encodingp)->size_cols) { warning(ctx, 0, "libfonts_parse_encoding_line", "dual-byte FIRSTINDEX was combined with single-byte SIZE"); (*encodingp)->first_index_col = 0; } ret = 1; *statep = 0; break; case ALIAS: if ((*statep & LEVEL_MASK) != ENCODING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "ALIAS keyword in wrong scope"); break; } if ((*encodingp)->naliases > SIZE_MAX / sizeof(*(*encodingp)->aliases) - 1) goto enomem; new = realloc((*encodingp)->aliases, ((*encodingp)->naliases + 1) * sizeof(*(*encodingp)->aliases)); if (!new) goto enomem; (*encodingp)->aliases = new; (*encodingp)->aliases[(*encodingp)->naliases] = get_string(line, &line); if (!(*encodingp)->aliases[(*encodingp)->naliases]) goto enomem; if (!*(*encodingp)->aliases[(*encodingp)->naliases]) warning(ctx, 0, "libfonts_parse_encoding_line", "empty string as alias name of encoding"); (*encodingp)->naliases += 1; break; case SIZE: if ((*statep & LEVEL_MASK) != ENCODING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "SIZE keyword in wrong scope"); break; } else if (*statep & HAVE_SIZE_BIT) { warning(ctx, 0, "libfonts_parse_encoding_line", "multiple SIZE declarations in encoding"); } ju2 = 0; r = get_uints(&ju1, &ju2, NULL, 0x100, line, &line, ctx); if (r < 0) { goto fail; } else if (r == 0) { warning(ctx, 0, "libfonts_parse_encoding_line", "missing numerical data"); break; } else if (r > 2) { warning(ctx, 0, "libfonts_parse_encoding_line", "excess numerical data"); break; } else if (*line && *line != '\n' && *line != '#') { warning(ctx, 0, "libfonts_parse_encoding_line", "unexpected non-numerical data"); break; } if (!ju1 || (r > 1 && !ju2)) { warning(ctx, 0, "libfonts_parse_encoding_line", "invalid SIZE of zero"); break; } (*encodingp)->size_rows = (uint16_t)ju1; (*encodingp)->size_cols = (uint16_t)ju2; *statep |= HAVE_SIZE_BIT; break; case FIRSTINDEX: if ((*statep & LEVEL_MASK) != ENCODING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "FIRSTINDEX keyword in wrong scope"); break; } else if (*statep & HAVE_FIRST_INDEX_BIT) { warning(ctx, 0, "libfonts_parse_encoding_line", "multiple FIRSTINDEX declarations in encoding"); } r = get_uints(&ju1, &ju2, NULL, 0xFF, line, &line, ctx); if (r < 0) { goto fail; } else if (r == 0) { warning(ctx, 0, "libfonts_parse_encoding_line", "missing numerical data"); break; } else if (r == 1) { ju2 = 0; *statep &= ~HAVE_FIRST_INDEX_BIT; } else if (r == 2) { *statep |= HAVE_FIRST_INDEX_BYTE_2_BIT; } else { warning(ctx, 0, "libfonts_parse_encoding_line", "excess numerical data"); break; } if (*line && *line != '\n' && *line != '#') { warning(ctx, 0, "libfonts_parse_encoding_line", "unexpected non-numerical data"); break; } (*encodingp)->first_index_row = (uint16_t)ju1; (*encodingp)->first_index_col = (uint16_t)ju2; *statep |= HAVE_FIRST_INDEX_BIT; break; case STARTMAPPING: if ((*statep & LEVEL_MASK) == MAPPING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "missing ENDMAPPING"); } else if ((*statep & LEVEL_MASK) != ENCODING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "STARTMAPPING keyword in wrong scope"); break; } if ((*encodingp)->nmappings > SIZE_MAX / sizeof(*(*encodingp)->mappings) - 1) goto enomem; new = realloc((*encodingp)->mappings, ((*encodingp)->nmappings + 1) * sizeof(*(*encodingp)->mappings)); if (!new) goto enomem; (*encodingp)->mappings = new; (*encodingp)->mappings[(*encodingp)->nmappings].entries = NULL; (*encodingp)->mappings[(*encodingp)->nmappings].nentries = 0; (*encodingp)->mappings[(*encodingp)->nmappings].target = get_string(line, &line); if (!(*encodingp)->mappings[(*encodingp)->nmappings].target) goto enomem; if (!*(*encodingp)->mappings[(*encodingp)->nmappings].target) warning(ctx, 0, "libfonts_parse_encoding_line", "empty string as target encoding"); (*encodingp)->nmappings += 1; *statep = (*statep & ~LEVEL_MASK) | MAPPING_LEVEL; break; case ENDMAPPING: if ((*statep & LEVEL_MASK) != MAPPING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "ENDMAPPING without corresponding STARTMAPPING"); break; } if (*line && *line != '\n' && *line != '#') warning(ctx, 0, "libfonts_parse_encoding_line", "unexpected data after ENDMAPPING keyword"); *statep = (*statep & ~LEVEL_MASK) | ENCODING_LEVEL; break; case UNDEFINE: if ((*statep & LEVEL_MASK) != MAPPING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "UNDEFINE keyword in wrong scope"); break; } r = get_uints(&ju1, &ju2, NULL, UINT32_C(0xFFFFFFFF), line, &line, ctx); if (r < 0) { goto fail; } else if (r == 0) { warning(ctx, 0, "libfonts_parse_encoding_line", "missing numerical data"); break; } else if (r == 1) { ju2 = ju1; } else if (r == 2) { if (ju2 < ju1) { warning(ctx, 0, "libfonts_parse_encoding_line", "reversed range"); break; } } else { warning(ctx, 0, "libfonts_parse_encoding_line", "excess numerical data"); break; } if (*line && *line != '\n' && *line != '#') { warning(ctx, 0, "libfonts_parse_encoding_line", "unexpected non-numerical data"); break; } mapping = &(*encodingp)->mappings[(*encodingp)->nmappings - 1]; if (mapping->nentries > SIZE_MAX / sizeof(*mapping->entries) - 1) goto enomem; new = realloc(mapping->entries, (mapping->nentries + 1) * sizeof(*mapping->entries)); if (!new) goto enomem; mapping->entries = new; mapping->entries[mapping->nentries].undefined_range.type = LIBFONTS_ENCODING_MAPPING_ENTRY_UNDEFINED_RANGE; mapping->entries[mapping->nentries].undefined_range.low_source = (uint32_t)ju1; mapping->entries[mapping->nentries].undefined_range.high_source = (uint32_t)ju2; mapping->nentries += 1; break; case NO_KEYWORD: if ((*statep & LEVEL_MASK) != MAPPING_LEVEL) { warning(ctx, 0, "libfonts_parse_encoding_line", "index mapping in wrong scope"); break; } r = get_uints(&ju1, &ju2, &ju3, UINT32_C(0xFFFFFFFF), line, &line, ctx); if (r < 0) { goto fail; } else if (r == 0) { warning(ctx, 0, "libfonts_parse_encoding_line", "internal error"); break; } else if (r == 1) { if (!*line || *line == '\n' || *line == '#') { warning(ctx, 0, "libfonts_parse_encoding_line", "missing data"); break; } } else if (r == 2) { ju3 = ju2; ju2 = ju1; } else if (r == 3) { if (ju2 < ju1) { warning(ctx, 0, "libfonts_parse_encoding_line", "reversed range"); break; } } else { warning(ctx, 0, "libfonts_parse_encoding_line", "excess numerical data"); break; } if (r != 1 && *line && *line != '\n' && *line != '#') { warning(ctx, 0, "libfonts_parse_encoding_line", "unexpected non-numerical data"); break; } mapping = &(*encodingp)->mappings[(*encodingp)->nmappings - 1]; if (mapping->nentries > SIZE_MAX / sizeof(*mapping->entries) - 1) goto enomem; new = realloc(mapping->entries, (mapping->nentries + 1) * sizeof(*mapping->entries)); if (!new) goto enomem; mapping->entries = new; if (r != 1) { mapping->entries[mapping->nentries].remapped_range.type = LIBFONTS_ENCODING_MAPPING_ENTRY_REMAPPED_RANGE; mapping->entries[mapping->nentries].remapped_range.low_source = (uint32_t)ju1; mapping->entries[mapping->nentries].remapped_range.high_source = (uint32_t)ju2; mapping->entries[mapping->nentries].remapped_range.low_target = (uint32_t)ju3; } else { mapping->entries[mapping->nentries].index_to_name.type = LIBFONTS_ENCODING_MAPPING_ENTRY_INDEX_TO_NAME; mapping->entries[mapping->nentries].index_to_name.source = (uint32_t)ju1; mapping->entries[mapping->nentries].index_to_name.target = get_string(line, &line); if (!mapping->entries[mapping->nentries].index_to_name.target) goto enomem; } mapping->nentries += 1; break; default: warning(ctx, 0, "libfonts_parse_encoding_line", "internal error"); break; } out: if (line) while (*line && *line != '\n') line++; if (endp) *endp = *(char **)(void *)&line; return ret; enomem: errno = ENOMEM; fail: ret = -1; goto out; bad_keyword: warning(ctx, 0, "libfonts_parse_encoding_line", "unrecognised keyword"); goto out; } #else int main(void) { return 0; /* XXX add test */ } #endif