/* * Copyright (C) 2026 Peter Mikkelsen * * This file is part of aplwc. * * Aplwc is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Aplwc is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with aplwc. If not, see . */ #include #include #include #include #include static char *files[] = { "DerivedCoreProperties.txt", "PropList.txt" }; static char *properties[] = { "ID_Start", "ID_Continue", "Pattern_Syntax" }; struct line { int from; int to; int valid; char property[64]; }; static void emit_line(struct line *, int *); static int peek(FILE *); static int parse_line(FILE *, struct line *); static int parse_int(FILE *); static void parse_dots(FILE *); static void parse_sep(FILE *); static jmp_buf env; int main(int argc, char *argv[]) { FILE *f; char *dir, *buf; size_t dir_size, tmp_size, max_size, i, j, nfiles; struct line line, prev_line; int current, n; assert(argc == 2); dir = argv[1]; dir_size = strlen(dir); if(dir[dir_size-1] == '/') dir_size--; nfiles = sizeof(files)/sizeof(files[0]); max_size = 0; for(i = 0; i < nfiles; i++){ tmp_size = strlen(files[i]); if(tmp_size > max_size) max_size = tmp_size; } max_size += dir_size + 2; buf = malloc(max_size); assert(buf != NULL); memcpy(buf, dir, dir_size); buf[dir_size] = '/'; current = -1; n = 0; printf("enum unicode_property {"); for(i = 0; i < sizeof(properties)/sizeof(properties[0]); i++) printf("%s%s", (i == 0) ? "\n\t" : ",\n\t", properties[i]); printf("\n};\n\n"); printf("struct unicode_range {\n\tuint32_t from;\n\tuint32_t to;\n};\n\n"); for(i = 0; i < sizeof(files)/sizeof(files[0]); i++){ memcpy(buf+dir_size+1, files[i], strlen(files[i])+1); f = fopen(buf, "r"); assert(f != NULL); printf("/* %s */\n\n", files[i]); memset(&prev_line, 0, sizeof(prev_line)); while(parse_line(f, &line)){ if(!line.valid) continue; if((current == -1) || (strcmp(line.property, properties[current]) != 0)){ if(current != -1){ emit_line(&prev_line, &n); printf("\n};\n"); printf("#define %s_count %d\n\n", properties[current], n); } prev_line.valid = 0; current = -1; for(j = 0; j < sizeof(properties)/sizeof(properties[0]); j++){ if(strcmp(line.property, properties[j]) == 0){ current = j; n = 0; printf("static struct unicode_range %s_ranges[] = {", properties[j]); break; } } } if(current != -1){ if(prev_line.valid){ if(prev_line.to+1 == line.from) line.from = prev_line.from; else emit_line(&prev_line, &n); } prev_line = line; } } fclose(f); } printf("static void\n"); printf("unicode_get(enum unicode_property p, int *n, struct unicode_range **r)\n{\n"); printf("\tswitch(p){\n"); for(i = 0; i < sizeof(properties)/sizeof(properties[0]); i++){ printf("\tcase %s:\n", properties[i]); printf("\t\t*n = %s_count;\n", properties[i]); printf("\t\t*r = %s_ranges;\n", properties[i]); printf("\t\tbreak;\n"); } printf("\t}\n}\n\n"); free(buf); return 0; } static void emit_line(struct line *line, int *n) { printf("%s{0x%X, 0x%X}", (*n == 0) ? "\n\t" : ",\n\t", line->from, line->to); (*n)++; } static int peek(FILE *f) { int c; c = fgetc(f); ungetc(c, f); return c; } static int parse_line(FILE *f, struct line *l) { int c, i; if(setjmp(env)){ l->valid = 0; do c = fgetc(f); while(c != EOF && c != '\n'); return c != EOF; }else{ l->valid = 1; } l->from = parse_int(f); if(peek(f) == '.'){ parse_dots(f); l->to = parse_int(f); }else{ l->to = l->from; } parse_sep(f); for(i = 0; ; i++){ c = fgetc(f); l->property[i] = c; if(c == ' ' || c == EOF){ if(i == 0) longjmp(env, 1); break; } } l->property[i] = 0; for(;;){ c = fgetc(f); if(c == '\n' || c == EOF) break; } return 1; } static int parse_int(FILE *f) { int n, m, c; n = 0; for(;;){ c = peek(f); if((c >= '0') && (c <= '9')) m = c - '0'; else if((c >= 'A') && (c <= 'F')) m = 10 + (c - 'A'); else if(c == '.' || c == ' ') return n; else longjmp(env, 1); fgetc(f); n <<= 4; n += m; } } static void parse_dots(FILE *f) { int i; for(i = 0; i < 2; i++){ if(peek(f) != '.') longjmp(env, 1); fgetc(f); } } static void parse_sep(FILE *f) { while(peek(f) == ' ') fgetc(f); if(peek(f) == ';') fgetc(f); else longjmp(env, 1); while(peek(f) == ' ') fgetc(f); }