// Copyright status: this file is in the public domain. #include #include #include #include #include #include #include extern const char *__progname; static const char *in_path = "keysyms"; static const char *public_h = "lx-keysyms.h"; static const char *internal_h = "keysym-internal.h"; static const char *casemap_c = "keysym-casemap.c"; static const char *names_c = "keysym-names.c"; typedef struct ksname KSNAME; typedef struct kssym KSSYM; typedef struct casemap CASEMAP; typedef struct foldstate FOLDSTATE; struct foldstate { FILE *inner; int cc; ES token; int tokstate; } ; struct casemap { char *name; char *lc; char *uc; } ; struct ksname { KSNAME *link_; char *name_; KSSYM *sym_; int nx_; int knx; } ; struct kssym { unsigned int sym; int nnames; KSNAME **names; } ; static FILE *in_f; static ES in_line; static AVL *ks_name; static AVL *ks_sym; static AVL *casemap; static int nks; #define Cisspace(x) isspace((unsigned char)(x)) static int cmp_ks_name(void *av, void *bv) { return(strcmp(((KSNAME *)av)->name_,((KSNAME *)bv)->name_)); } static int cmp_ks_sym(void *av, void *bv) { KSSYM *a; KSSYM *b; a = av; b = bv; if (a->sym < b->sym) return(-1); if (a->sym > b->sym) return(1); return(0); } static int cmp_casemap(void *av, void *bv) { return(strcmp(((CASEMAP *)av)->name,((CASEMAP *)bv)->name)); } static const AVL_OPS ao_ks_name = { .compare = &cmp_ks_name, .flags = AVL_F_NODUPS }; static const AVL_OPS ao_ks_sym = { .compare = &cmp_ks_sym, .flags = AVL_F_NODUPS }; static const AVL_OPS ao_casemap = { .compare = &cmp_casemap, .flags = AVL_F_NODUPS }; static void setup(void) { in_f = fopen(in_path,"r"); if (! in_f) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,in_path,strerror(errno)); exit(1); } es_init(&in_line); ks_name = avl_new(&ao_ks_name); ks_sym = avl_new(&ao_ks_sym); casemap = avl_new(&ao_casemap); } static char *pl_to_nt(const char *p, int l) { char *s; s = malloc(l+1); bcopy(p,s,l); s[l] = '\0'; return(s); } static void input_line(const char *body, int len, int lno) { int fs[4]; int fl[4]; int x; int i; CASEMAP *cm; KSNAME *ksn; KSSYM *kss; KSSYM *kssf; char *key; unsigned long int v; char *ep; char *xs; for (x=0;(x= len) return; if (body[x] == '#') return; for (i=0;i<4;i++) { fs[i] = x; for (;(x 0x1fffffff) { fprintf(stderr,"%s: %s, line %d: value %s out of range\n",__progname,in_path,lno,xs); exit(1); } free(xs); ksn = malloc(sizeof(KSNAME)); kss = malloc(sizeof(KSSYM)); kss->sym = v; kssf = avl_insert(ks_sym,kss); if (kssf) { free(kss); kss = kssf; kss->nnames ++; kss->names = realloc(kss->names,kss->nnames*sizeof(KSNAME *)); kss->names[kss->nnames-1] = ksn; } else { kss->nnames = 1; kss->names = malloc(sizeof(KSNAME *)); kss->names[0] = ksn; } ksn->name_ = key; ksn->sym_ = kss; ksn->nx_ = kss->nnames - 1; if (avl_insert(ks_name,ksn)) { fprintf(stderr,"%s: %s, line %d: duplicate name\n",__progname,in_path,lno); exit(1); } if (fl[2]) { if (v > 65535) { fprintf(stderr,"%s: %s, line %d: casemapping outside low 64k\n",__progname,in_path,lno); exit(1); } cm = malloc(sizeof(CASEMAP)); cm->name = key; cm->lc = pl_to_nt(body+fs[2],fl[2]); cm->uc = pl_to_nt(body+fs[3],fl[3]); if (! strcmp(key,cm->lc)) { free(cm->lc); cm->lc = key; } else if (! strcmp(key,cm->uc)) { free(cm->uc); cm->uc = key; } else { fprintf(stderr,"%s: %s, line %d: name matches neither lc nor uc form\n",__progname,in_path,lno); exit(1); } if (avl_insert(casemap,cm)) abort(); } nks ++; } static void read_input(void) { int c; int lno; nks = 0; lno = 0; while (1) { c = getc(in_f); if (c == EOF) { if (ferror(in_f)) { fprintf(stderr,"%s: read error on %s: %s\n",__progname,in_path,strerror(errno)); exit(1); } if (es_len(&in_line)) { fprintf(stderr,"%s: %s: incomplete last line\n",__progname,in_path); exit(1); } break; } if (c == '\n') { es_append_1(&in_line,'\0'); lno ++; input_line(es_buf(&in_line),es_len(&in_line)-1,lno); es_clear(&in_line); } else { es_append_1(&in_line,c); } } } /* * Check case-mapping sanity. Specifically, check: * * - For each case-mapping pair, check that its flipped variant is * present as well. That is, if A has lc Al and uc Au, then (a) * check that either A==Al and Au also has lc Al and uc Au, or that * A==Au and Al also has lc Al and uc Au. * * - Whenever we have A, B, and C, such that A and B are a case-mapping * pair, C uses the same keysym as A, and C is part of a casemapping * pair, let D be C's casemapping-paired keysym. Then D must use * the same keysym as B. */ static void check_data(void) { AVL_WALKER *w; CASEMAP *cm; CASEMAP cmc; CASEMAP *cmo; CASEMAP *cmd; KSNAME ksnc; KSNAME *a; KSNAME *b; KSNAME *c; KSNAME *d; int i; w = avl_walk_start(casemap,AVL_INORDER); while ((cm = avl_walk_next(w))) { do <"good"> { if (cm->name == cm->lc) { cmc.name = cm->uc; cmo = avl_find(casemap,&cmc); if (cmo && (cmo->name == cmo->uc) && !strcmp(cmo->lc,cm->name)) break <"good">; } else if (cm->name == cm->uc) { cmc.name = cm->lc; cmo = avl_find(casemap,&cmc); if (cmo && (cmo->name == cmo->lc) && !strcmp(cmo->uc,cm->name)) break <"good">; } else { abort(); } fprintf(stderr,"%s: %s: case mapping reverse fail for %s\n",__progname,in_path,cm->name); exit(1); } while (0); ksnc.name_ = cm->name; a = avl_find(ks_name,&ksnc); if (! a) abort(); ksnc.name_ = cmo->name; b = avl_find(ks_name,&ksnc); if (! b) abort(); if (a->sym_->nnames > 1) { for (i=a->sym_->nnames-1;i>=0;i--) { if (i == a->nx_) continue; c = a->sym_->names[i]; cmc.name = c->name_; cmd = avl_find(casemap,&cmc); if (! cmd) continue; if (cmd->name == cmd->lc) { ksnc.name_ = cmd->uc; } else if (cmd->name == cmd->uc) { ksnc.name_ = cmd->lc; } else { abort(); } d = avl_find(ks_name,&ksnc); if (! d) abort(); if (d->sym_ != b->sym_) { fprintf(stderr,"%s: %s: case mapping problem: <%s,%s> but <%s,%s>\n",__progname,in_path,a->name_,b->name_,c->name_,d->name_); exit(1); } } } } } static void write_file(const char *fn, int (*gen)(FILE *)) { FILE *f; f = fopen(fn,"w"); if (! f) { fprintf(stderr,"%s: can't write %s: %s\n",__progname,fn,strerror(errno)); exit(1); } if ( (*gen)(f) || (fclose(f) == EOF) ) { fprintf(stderr,"%s: error writing %s: %s\n",__progname,fn,strerror(errno)); exit(1); } } static int wrap_flushtoken(FOLDSTATE *s) { int n; n = es_len(&s->token); if (n) { if (s->cc+n > 72) { if (putc('\n',s->inner) == EOF) return(-1); s->cc = 0; } if (fwrite(es_buf(&s->token),1,n,s->inner) != n) return(-1); es_clear(&s->token); s->cc += n; } return(0); } #define ALNUM '0': case '1': case '2': case '3': case '4': \ case '5': case '6': case '7': case '8': case '9': \ case '_': \ case 'A': case 'B': case 'C': case 'D': case 'E': \ case 'F': case 'G': case 'H': case 'I': case 'J': \ case 'K': case 'L': case 'M': case 'N': case 'O': \ case 'P': case 'Q': case 'R': case 'S': case 'T': \ case 'U': case 'V': case 'W': case 'X': case 'Y': \ case 'Z': \ case 'a': case 'b': case 'c': case 'd': case 'e': \ case 'f': case 'g': case 'h': case 'i': case 'j': \ case 'k': case 'l': case 'm': case 'n': case 'o': \ case 'p': case 'q': case 'r': case 's': case 't': \ case 'u': case 'v': case 'w': case 'x': case 'y': \ case 'z' static int wrap_w(void *sv, const char *data, int len) { FOLDSTATE *s; int i; s = sv; for (i=0;itokstate) { case 0: switch (data[i]) { case ALNUM: es_append_1(&s->token,data[i]); s->tokstate = 1; break; case '"': es_append_1(&s->token,'"'); s->tokstate = 2; break; default: if (s->cc >= 72) { if (putc('\n',s->inner) == EOF) return(-1); s->cc = 0; } if (putc(data[i],s->inner) == EOF) return(-1); s->cc ++; break; } break; case 1: switch (data[i]) { case ALNUM: es_append_1(&s->token,data[i]); break; default: if (wrap_flushtoken(s) < 0) return(-1); i --; s->tokstate = 0; continue; break; } break; case 2: switch (data[i]) { case '"': es_append_1(&s->token,'"'); if (wrap_flushtoken(s) < 0) return(-1); s->tokstate = 0; break; case '\\': es_append_1(&s->token,'\\'); s->tokstate = 3; break; default: es_append_1(&s->token,data[i]); break; } break; case 3: es_append_1(&s->token,data[i]); s->tokstate = 2; break; } } return(len); } #undef ALNUM static int wrap_c(void *sv) { FOLDSTATE *s; s = sv; switch (s->tokstate) { case 0: break; case 1: if (wrap_flushtoken(s) < 0) return(-1); break; default: abort(); break; } if (putc('\n',s->inner) == EOF) return(-1); es_done(&s->token); free(s); return(0); } static FILE *fold_wrap(FILE *inner) { FOLDSTATE *s; FILE *f; s = malloc(sizeof(FOLDSTATE)); if (! s) return(0); f = funopen(s,0,&wrap_w,0,&wrap_c); if (! f) { free(s); return(0); } s->inner = inner; s->cc = 0; s->tokstate = 0; es_init(&s->token); return(f); } static KSNAME *sort_ksnlist(KSNAME *list, int (*lt)(KSNAME *, KSNAME *)) { KSNAME *a; KSNAME *b; KSNAME *e; KSNAME *t; KSNAME **listt; if (!list || !list->link_) return(list); a = 0; b = 0; while ((e = list)) { list = e->link_; e->link_ = a; a = b; b = e; } a = sort_ksnlist(a,lt); b = sort_ksnlist(b,lt); listt = &list; while (1) { if (a && (!b || (*lt)(a,b))) { t = a; a = a->link_; } else if (b) { t = b; b = b->link_; } else { break; } *listt = t; listt = &t->link_; } *listt = 0; return(list); } static int lt_ksn_sym(KSNAME *a, KSNAME *b) { return(a->sym_->symsym_->sym); } static int lt_ksn_name(KSNAME *a, KSNAME *b) { return(strcmp(a->name_,b->name_)<0); } static int gen_public_h(FILE *f) { AVL_WALKER *w; KSNAME *ksn; if (fprintf(f,"// This file is mechanically generated\n") < 0) return(1); w = avl_walk_start(ks_name,AVL_INORDER); while ((ksn = avl_walk_next(w))) { if (fprintf(f,"#define LX_KEYSYM_%s 0x%x\n",ksn->name_,ksn->sym_->sym) < 0) return(1); } avl_walk_done(w); return(0); } static int gen_internal_h(FILE *f) { const char *t; if (nks < 256) { t = "unsigned char"; } else if (nks < 65536) { t = "unsigned short int"; } else { t = "unsigned int"; } if (fprintf(f,"// This file is mechanically generated\n#ifndef SYMXTIME_DEFINED\ntypedef %s SYMXTYPE;\n#define SYMXTIME_DEFINED\n#endif\n",t) < 0) return(1); return(0); } static int gen_casemap_c(FILE *f) { FILE *g; int i; AVL_WALKER *w; CASEMAP *e; unsigned short int (*map)[3]; KSNAME ksnc; KSNAME *ksn; int n; if (fprintf(f,"// This file is mechanically generated\n#include \"lx.h\"\nLX_KEYSYM lx__casemap[][3] = {\n"/*}*/) < 0) return(1); map = malloc(65536*sizeof(*map)); for (i=65536-1;i>=0;i--) map[i][2] = 0; w = avl_walk_start(casemap,AVL_INORDER); while ((e = avl_walk_next(w))) { ksnc.name_ = e->name; ksn = avl_find(ks_name,&ksnc); if (! ksn) abort(); if (ksn->sym_->sym > 65535) abort(); i = ksn->sym_->sym; ksnc.name_ = e->lc; ksn = avl_find(ks_name,&ksnc); if (! ksn) abort(); if (ksn->sym_->sym > 65535) abort(); map[i][0] = ksn->sym_->sym; ksnc.name_ = e->uc; ksn = avl_find(ks_name,&ksnc); if (! ksn) abort(); if (ksn->sym_->sym > 65535) abort(); map[i][1] = ksn->sym_->sym; map[i][2] = 1; } avl_walk_done(w); g = fold_wrap(f); n = 0; for (i=0;i<65536;i++) { if (! map[i][2]) continue; if (fprintf(g,"{%d,%d,%d},",i,map[i][0],map[i][1]) < 0) return(1); n ++; } if (fclose(g) == EOF) return(1); if (fprintf(f,/*{*/"};\nconst int lx__n_casemap = %d;\n",n) < 0) return(1); return(0); } static int gen_names_c(FILE *f) { FILE *g; AVL_WALKER *w; KSNAME *ksn; int i; KSNAME *list; if (fprintf(f,"// This file is mechanically generated\n#include \"lx.h\"\nconst KEYSYM_NAME lx__keysym_names[] = {\n"/*}*/) < 0) return(1); g = fold_wrap(f); w = avl_walk_start(ks_name,AVL_PREORDER); list = 0; i = 0; while ((ksn = avl_walk_next(w))) { ksn->knx = i; if (fprintf(g,"{%u,\"%s\"},",ksn->sym_->sym,ksn->name_) < 0) return(1); i ++; ksn->link_ = list; list = ksn; } if (fclose(g) == EOF) return(1); if (fprintf(f,/*{*/"};\n") < 0) return(1); list = sort_ksnlist(list,<_ksn_sym); if (fprintf(f,"const SYMXTYPE lx__keysym_by_sym[] = {\n"/*}*/) < 0) return(1); g = fold_wrap(f); for (ksn=list;ksn;ksn=ksn->link_) if (fprintf(g,"%u,",ksn->knx) < 0) return(1); if (fclose(g) == EOF) return(1); if (fprintf(f,/*{*/"};\n") < 0) return(1); list = sort_ksnlist(list,<_ksn_name); if (fprintf(f,"const SYMXTYPE lx__keysym_by_name[] = {\n"/*}*/) < 0) return(1); g = fold_wrap(f); for (ksn=list;ksn;ksn=ksn->link_) if (fprintf(g,"%u,",ksn->knx) < 0) return(1); if (fclose(g) == EOF) return(1); if (fprintf(f,/*{*/"};\nconst int lx__n_keysym_names = %d;\n",i) < 0) return(1); return(0); } static void gen_output(void) { write_file(public_h,&gen_public_h); write_file(internal_h,&gen_internal_h); write_file(casemap_c,&gen_casemap_c); write_file(names_c,&gen_names_c); } int main(void); int main(void) { setup(); read_input(); check_data(); gen_output(); return(0); } /* #! /bin/sh # Copyright status: this file is in the public domain. # 1.4T's awk gives 0, not 18, for "0x12"+0. # So we do our own hex->decimal conversion. # Ugh. awk ' function hexval(s, v, c) { v = 0; while (s != "") { c = substr(s,1,1); s = substr(s,2); v = (v * 16) + xval[c]; } return(v); } function sortit(n, i, t) { d = n / 2; any = 1; while ((d > 1) || any) { any = 0; for (i=min+d;i sortkey[sortvec[i]]) { t = sortvec[i-d]; sortvec[i-d] = sortvec[i]; sortvec[i] = t; any = 1; } } } } BEGIN { val[""] = 0; delete val[""]; lcform[""] = 0; delete lcform[""]; ucform[""] = 0; delete ucform[""]; casemap[""] = 0; delete casemap[""]; xval["0"] = 0; xval["1"] = 1; xval["2"] = 2; xval["3"] = 3; xval["4"] = 4; xval["5"] = 5; xval["6"] = 6; xval["7"] = 7; xval["8"] = 8; xval["9"] = 9; xval["a"] = 10; xval["A"] = 10; xval["b"] = 11; xval["B"] = 11; xval["c"] = 12; xval["C"] = 12; xval["d"] = 13; xval["D"] = 13; xval["e"] = 14; xval["E"] = 14; xval["f"] = 15; xval["F"] = 15; } /^#/ { next; } NF==2 { val[$1] = $2; next; } NF==4 { if ($2 !~ "^00") { printf("Casemapping outside low 64k: %s\n",$1) > "/dev/stderr"; exit 1 } val[$1] = $2; lcform[$1] = $3; ucform[$1] = $4; casemap[$1] = 0; numval[$1] = hexval($2); next; } { printf("Unrecognizable line %s\n",$0) > "/dev/stderr"; exit 1 } END { for (n in casemap) { if (! (lcform[n] in casemap)) { printf("Botched casemapping for %s\n",n) > "/dev/stderr"; exit 1 } if (! (ucform[n] in casemap)) { printf("Botched casemapping for %s\n",n) > "/dev/stderr"; exit 1 } } printf("// This file is mechanically generated\n") > "lx-keysyms.h"; nsyms = 0; for (n in val) { printf("#define LX_KEYSYM_%s 0x%s\n",n,val[n]) > "lx-keysyms.h"; nsyms ++; } printf("// This file is mechanically generated\n") > "keysym-casemap.c"; printf("#include \"lx.h\"\n") > "keysym-casemap.c"; for (n in casemap) { havemap[hexval(val[n])] = n; } printf("LX_KEYSYM lx__casemap[][3] = {\n") > "keysym-casemap.c"; mapcount = 0; for (i=0;i<65536;i++) { if (i in havemap) { n = havemap[i]; printf("{%d,%d,%d},\n",numval[n],numval[lcform[n]],numval[ucform[n]]) > "keysym-casemap.c"; mapcount ++; } } printf("};\n") > "keysym-casemap.c"; printf("int lx__n_casemap = %d;\n",mapcount) > "keysym-casemap.c"; if (nsyms < 256) { symxtype = "unsigned char"; } else if (nsyms < 65536) { symxtype = "unsigned short int"; } else { symxtype = "unsigned int"; } printf("typedef %s SYMXTYPE;\n",symxtype) > "keysym-internal.h"; printf("#include \"internal.h\"\n") > "keysym-names.c"; printf("const int lx__n_keysym_names = %d;\n",nsyms) > "keysym-names.c"; printf("const KEYSYM_NAME lx__keysym_names[] = {\n") > "keysym-names.c"; maxnlen = 0; for (n in val) { printf("{0x%s,\"%s\"},\n",val[n],n) > "keysym-names.c"; l = length(n); if (l > maxnlen) maxnlen = l; } printf("}\n") > "keysym-names.c"; zeros = "0"; while (length(zeros) < maxnlen) { zeros = zeros zeros; } i = 0; for (n in val) { l = length(n); if (l < maxnlen) { zn = substr(zeros,1,maxnlen-l) n; } else { zn = n; } addz[n] = zn; dropz[zn] = n; ksorder_ks[i] = zn; ksorder_name[i] = val[n]; i ++; } for (j=0;j "keysym-names.c"; for (j=0;j "keysym-names.c"; } printf("};\n") > "keysym-names.c"; for (j=0;j "keysym-names.c"; for (j=0;j "keysym-names.c"; } printf("};\n") > "keysym-names.c"; }' < keysyms */