aboutsummaryrefslogblamecommitdiffstats
path: root/libfonts_parse_encoding_line.c
blob: 6750b1e497d12c7e4caf919490af0ede41fada9e (plain) (tree)


































                                                                                      
                                                         








                                                               
                                         












                                                                         
                             
                      


















                                                                                    
                         


















                                                                                                                    







































































                                                                                              

                                                                     







                                          
                                                                                                                             










                                                                    

                                                                                                            




                                                              
                                                                                              
                                                                      
                                                                                                                           

                              

                                                                                                                     
                                                                                         
                                                                                                                                   







                                                               
                                                                                                        










                                                                                                                    

                                                                                                                  




                                                               
                                                                                                       

                                                     
                                                                                                                  





                                                                         
                                                                                                  

                                   
                                                                                                 

                                                                    
                                                                                                         

                              

                                                                                                








                                                               
                                                                                                             

                                                            
                                                                                                                        




                                                                        
                                                                                                  






                                                               
                                                                                                 


                                                             
                                                                                                         








                                                              
                                                                                              
                                                                      
                                                                                                               










                                                                                                                       



                                                                                                           





                                                                  
                                                                                                                         

                              

                                                                                                                    




                                                                   
                                                                                                           





                                                                                        
                                                                                                  


                                    






                                                                                                  


                                                             
                                                                                                         
















                                                                                                                           
                                                                                                        





                                                                                        
                                                                                          


                                                                      
                                                                                                






                                        
                                                                                                  


                                      
                                                                                                 


                                                                       
                                                                                                         

























                                                                                                                                 
                                                                                  

















                                                
                                                                                














                                    
/* 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