#include #include #include /* entry has 1 byte in widtab + 8 bytes per tile */ #define ufxlen(n) (1 + ((n)*(n)*8)) enum{ MR_BLANK, MR_SINGLE, MR_RANGE }; struct MapRule{ struct MapRule *next; uchar ty; uint start, end; }; struct Mapping{ struct Mapping *next; uint linum; Rune start; union {Rune end; uint idx;}; struct MapRule *rules; }; struct MapFile{ struct MapFile *next; char *file; struct Mapping *mappings; }; uint fwidth, nglyphs; uchar setconv, donaive, doholes; struct MapFile *mapfiles, *lastmf; uchar wasmferr, wasreaderr; void bioerr(char *s) { wasreaderr = 1; wasmferr = 1; fprint(2, "%s: %s\n", argv0, s); } char * readtoken(Biobuf *b, char **line, uint *linum) { char *ret, *p; while(*line != nil){ p = *line; while(p[0] != '\0' && strchr("\t\r ", p[0]) != nil) p++; if(strchr("#;\n", p[0]) != nil){ *line = Brdline(b, '\n'); (*linum)++; continue; } ret = p; while(strchr("\t\r\n ", p[0]) == nil) p++; *line = p; if(p[0] != '\n') (*line)++; p[0] = '\0'; return ret; } return nil; } #pragma varargck argpos mferr 3 void mferr(char *file, uint linum, char *fmt, ...) { char buf[1024]; va_list va; va_start(va, fmt); vsnprint(buf, sizeof(buf), fmt, va); va_end(va); if(linum != 0) fprint(2, "%s: %s:%ud: %s\n", argv0, file, linum, buf); else fprint(2, "%s: %s: %s\n", argv0, file, buf); wasmferr = 1; } void _parsemapfile(Biobuf *b, struct MapFile *mf) { char *line, *tok, *p; uint linum = 1; struct Mapping *mlast = nil; struct MapRule *rlast; wasreaderr = 0; line = Brdline(b, '\n'); if(line == nil){ if(!wasreaderr) fprint(2, "%s: warn: %s: empty file\n", argv0, mf->file); return; } tok = readtoken(b, &line, &linum); while(tok != nil){ if(strcmp(tok, "mapping") == 0){ if(mlast == nil) mf->mappings = mlast = malloc(sizeof(struct Mapping)); else mlast = mlast->next = malloc(sizeof(struct Mapping)); mlast->linum = linum; mlast->rules = nil; mlast->next = nil; tok = readtoken(b, &line, &linum); if(tok == nil){ mferr(mf->file, linum, "unexpected eof"); return; } mlast->start = strtoul(tok, &p, 0); if(tok == p){ mferr(mf->file, linum, "empty lower rune bound"); return; } tok = p; if(tok[0] == '\0'){ /* 1:1 mapping */ tok = readtoken(b, &line, &linum); if(tok == nil){ mferr(mf->file, linum, "unexpected eof"); return; } mlast->idx = strtoul(tok, &p, 0); if(tok == p || p[0] != '\0'){ mferr(mf->file, linum, "bad glyph index"); return; } tok = readtoken(b, &line, &linum); continue; }else if(tok[0] != '-'){ mferr(mf->file, linum, "bad rune range"); return; } tok++; /* many:many mapping */ mlast->end = strtoul(tok, &p, 0); if(tok == p){ mferr(mf->file, linum, "empty upper rune bound"); return; }else if(p[0] != '\0'){ mferr(mf->file, linum, "bad upper rune bound"); return; } rlast = nil; while((tok = readtoken(b, &line, &linum)) != nil){ if(rlast == nil) mlast->rules = rlast = malloc(sizeof(struct MapRule)); else rlast = rlast->next = malloc(sizeof(struct MapRule)); rlast->next = nil; if(strcmp(tok, "blank") == 0){ rlast->ty = MR_BLANK; continue; }else if(tok[0] == '-'){ /* e.g. -0x4f */ tok++; rlast->ty = MR_RANGE; rlast->start = 0; rlast->end = strtoul(tok, &p, 0); if(tok == p || p[0] != '\0'){ mferr(mf->file, linum, "bad upper glyph bound"); return; } continue; } rlast->start = strtoul(tok, &p, 0); if(tok == p) break; /* return to toplevel parser */ else if(p[0] == '\0'){ rlast->ty = MR_SINGLE; continue; }else if(p[0] != '-'){ mferr(mf->file, linum, "bad lower glyph bound"); return; } rlast->ty = MR_RANGE; tok = p+1; if(tok[0] == '\0'){ /* e.g. 0x80- */ rlast->end = nglyphs - 1; continue; } rlast->end = strtoul(tok, &p, 0); if(tok == p || p[0] != '\0'){ mferr(mf->file, linum, "bad upper glyph bound"); return; } } /* end while */ if(rlast == nil){ mferr(mf->file, linum, "unexpected eof"); return; } }else{ mferr(mf->file, linum, "unknown verb %s", tok); return; } } /* end while */ } void parsemapfile(struct MapFile *mf) { Biobuf *b; b = Bopen(mf->file, OREAD); if(b == nil){ fprint(2, "%s: %r\n", argv0); wasmferr = 1; return; } Blethal(b, bioerr); _parsemapfile(b, mf); Bterm(b); } void printmapfile(struct MapFile *mf) { struct Mapping *mp; struct MapRule *rp; print("mapfile: %s\n", mf->file); for(mp = mf->mappings; mp != nil; mp = mp->next){ if(mp->rules == nil) print("\t%ud: %ux => %ux\n", mp->linum, mp->start, mp->idx); else{ print("\t%ud: %ux - %ux =>\n", mp->linum, mp->start, mp->end); for(rp = mp->rules; rp != nil; rp = rp->next){ switch(rp->ty){ case MR_SINGLE: print("\t\t%ux\n", rp->start); break; case MR_RANGE: print("\t\t%ux - %ux\n", rp->start, rp->end); break; case MR_BLANK: print("\t\tblank\n"); break; } } } } } void usage(void) { fprint(2, "usage: %s [-nh] [-m mapfile] [-l length] [-x width] in.ufx out/\n", argv0); exits("usage"); } void main(int argc, char **argv) { char *infile, *outdir; struct MapFile *mf; ARGBEGIN{ case 'l': nglyphs = atoi(EARGF(usage())); break; case 'x': fwidth = atoi(EARGF(usage())); break; case 'n': setconv = 1; donaive = 1; break; case 'h': setconv = 1; doholes = 1; break; case 'm': setconv = 1; if(lastmf == nil) mapfiles = lastmf = malloc(sizeof(struct MapFile)); else lastmf = lastmf->next = malloc(sizeof(struct MapFile)); lastmf->file = EARGF(usage()); lastmf->next = nil; lastmf->mappings = nil; break; default: usage(); }ARGEND; if(argv0 == nil) argv0 = "ufx2font"; if(argc != 2) usage(); // TODO: calculate length etc etc for(mf = mapfiles; mf != nil; mf = mf->next) parsemapfile(mf); if(wasmferr) exits("mapfile"); for(mf = mapfiles; mf != nil; mf = mf->next) printmapfile(mf); }