/*+++* * title: keysubst.c * abstract: fill in a key-template file according to a JOVE wallchart * author: T.R.Hageman, Groningen, The Netherlands. * created: october 1989 * modified: 25-Jun-91, add ANSI prototypes. * description: * Usage: keysubst keybinding-file(s) ) * (for Left,Centered,Right justification, respectively), * optionally followed by a continuation character (+ or \). * The '+' expresses a preference for the first field, * '\' for the second field. * * The template is replaced by the name of the function bound * to that key. If the name is too long to fit in a short field, * the program attempts to shorten the name in some sensible way. * If no binding exists, the field is filled with blanks. * * Comment lines start with `#' in the first column. * * bugs: * If a field is continued with '\', it is assumed that there is * only one more field to follow and that it has the same size as * the first field. * * example: * bindings: * ^:ku previous-line * ESC ^:ku previous-window * input: * UpArrow: ^:ku<<<<<<<<<<<<< ESC UpArrow: ^[^:ku------------- * output: * UpArrow: previous-line ESC UpArrow: previous-window *---*/ #define NOT_JOVE #include "jove.h" RCS("$Id: keysubst.c,v 14.30.0.9 1993/12/13 01:57:29 tom Exp tom $") /* #undef NULL /* I'd rather have a warning than nothing at all... */ #include #include #ifdef DEBUG # define DPRINTF(x) dprintf x # ifdef _STDARG_ # include # else # include # endif int debug; void DEFVARG( dprintf, (char *fmt, ...), (fmt, va_alist) const char *fmt;) { va_list ap; if (!debug) return; va_begin(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } #else # define DPRINTF(x) #endif /* DEBUG */ #undef NFKEYS #define NFKEYS 128 /* we want many */ #undef MAPSIZE #define MAPSIZE (128 + NFKEYS) #define VOIDKEY (MAPSIZE-1) #define NMAPS 4 typedef char *keydef[MAPSIZE]; keydef KeyTab[NMAPS]; char *_pname; char line[BUFSIZ]; int line_no; const char *file_name; /*==============================*/ /* support routines */ /*==============================*/ static char Prefix[] = "prefix-"; #define ISPREFIX(key,map) (!lmatch(Prefix, KeyTab[map][key]) ? 0 : \ KeyTab[map][key][sizeof(Prefix)-1] - '0') int numcomp __(( const char *_(s1), const char *_(s2) )); int numcomp(s1, s2) register const char *s1, *s2; { register const char *base = s1; while (*s1 == *s2++) if (*s1++ == '\0') { --s1; break; } return s1 - base; } int lmatch __(( const char *_(s1), const char *_(s2) )); int lmatch(s1, s2) register const char *s1, *s2; { if (!s1 || !s2) return 0; while (*s1) if (*s1++ != *s2++) return 0; return 1; } char * copystr __(( const char *_(str) )); char * copystr(str) const char *str; { register const char *s; register char *d, *base; for (s = str; *s++;) ; base = d = malloc((size_t)(s - str)); if (!base) { fprintf(stderr, "%s: [out of memory]\n", _pname); exit(2); } for (s = str; *d++ = *s++; ) ; if (d[-2] == '\n') /* delete trailing newline */ d[-2] = '\0'; return base; } /*==============================*/ /* function keys */ /*==============================*/ short FkeyDef[NFKEYS]; int AddFkeys = 1; int function_key __(( const char *_(s) )); int function_key(s) const char *s; { register int id = (s[0] << 8) | s[1]; register int i; for (i = 0; i < sizeof(FkeyDef)/sizeof(FkeyDef[0]) - 1; i++) { if (FkeyDef[i] == id) return i + 128; if (FkeyDef[i] == 0) { if (!AddFkeys) return VOIDKEY; FkeyDef[i] = id; return i + 128; } } fprintf(stderr, "Function key table overflow: \"%.2s\" ignored\n", s); return VOIDKEY; } void fixup_function_keys __(( void )); void fixup_function_keys() { int i; for (i = 0; i < NMAPS; i++) KeyTab[i][VOIDKEY] = NULL; AddFkeys = 0; } /*==============================*/ /* wallchart parsing */ /*==============================*/ const char * GetKey __(( const char *_(s), int *_(result) )); const char * GetKey(s, result) register const char *s; int *result; { register int c; switch (c = *s++) { case 'C': if (s[0] == '-') s++, c = *s++ ^ '@'; break; case 'E': if (s[0] == 'S' && s[1] == 'C') c = '\33', s += 2; break; case 'S': if (s[0] == 'P' && s[1] == 'C') c = ' ', s += 2; break; case '^': if (s[0] == '?') s++, c = '\177'; else if (s[0] == ':') c = function_key(++s), s += 2; break; } *result = c; return s; } #undef CHECK #define CHECK(cond) if (!(cond)) return 0; int GetKeySeq __(( char *_(line) )); int GetKeySeq(line) char *line; { register const char *s = line; int key1, key2; register int map, newmap; DPRINTF(("%.14s\n", s)); for (map = 0; ; map = newmap) { switch (*s++) { case '{': if (s[0] != ' ' || s[1] == ',') { CHECK(s = GetKey(s, &key1)); CHECK(*s++ == ','); CHECK(s = GetKey(s, &key2)); CHECK(*s++ == '}'); key2 = -key2; break; } /* fall through to default */ case '[': if (s[0] != ' ' || s[1] == '-') { CHECK(s = GetKey(s, &key1)); CHECK(*s++ == '-'); CHECK(s = GetKey(s, &key2)); CHECK(*s++ == ']'); break; } /* fall through */ default: s = GetKey(s - 1, &key1); key2 = key1; } CHECK(*s++ == ' '); if ((newmap = ISPREFIX(key1, map)) == 0) break; } while (*s++ == ' ') ; --s; if (map && key1 == ' ' && key2 == key1 && lmatch(Prefix, s)) return 1; /* double occurrence of prefix-binding */ KeyTab[map][key1] = copystr(s); if (key2 < 0) KeyTab[map][-key2] = KeyTab[map][key1]; else while (key2 > key1) KeyTab[map][key2--] = KeyTab[map][key1]; return 1; } int eat_wallchart __(( const char *_(filename) )); int eat_wallchart(filename) const char *filename; { int notstdin = strcmp(filename, "-"); FILE *f = (notstdin) ? fopen(filename, "r") : stdin; if (!f) { fprintf(stderr, "%s: warning: can't read wallchart \"%s\"\n", _pname, filename); return notstdin; } DPRINTF(("**** eat_wallchart \"%s\"\n", (f != stdin) ? filename : "(stdin)")); line_no = 0; file_name = filename; while (fgets(line, sizeof line, f)) { line_no++; if (!GetKeySeq(line)) fprintf(stderr, "%s:%d:[bad key-sequence] %s", file_name, line_no, line); } if (!notstdin) fclose(f); return notstdin; } /*==============================*/ /* pending Formats */ /*==============================*/ #define NPENDINGS 40 typedef struct { /* to remember a continuation */ int key, map; char *tail; } pending; int Npending; pending PendTab[NPENDINGS]; void MakePending __(( int _(key), int _(map), const char *_(tail) )); void MakePending(key, map, tail) int key, map; const char *tail; { register pending *p; if (Npending == NPENDINGS) { fprintf(stderr, "warning: too many pending continuations, max = %d\n", NPENDINGS); return; } for (p = &PendTab[0]; p->tail; p++) /* find empty slot */ ; DPRINTF(("[MakePending (%d) map %d, key %d, tail %s]\n", Npending, map, key, tail)); Npending++; p->map = map; p->key = key; p->tail = copystr(tail); } char *Pending __(( int _(key), int _(map) )); char * Pending(key, map) { register pending *p; register int count = Npending; char *tail; if (count == 0) return NULL; for (p = &PendTab[0]; p <= &PendTab[NPENDINGS]; p++) { if (tail = p->tail) { if (key == p->key && map == p->map) { Npending--; p->tail = NULL; return tail; } if (--count == 0) break; } } return NULL; } /*==============================*/ /* Shortening */ /*==============================*/ const char * const ShortTab[] = { "backward", "back", "beginning", "begin", "buffer", "buf", "buffers", "bufs", "character", "char", "characters", "chars", "current", "curr", "delete", "del", "error", "err", "execute", "exec", "expression", "expr", "forward", "forw", "keyboard", "kbd", "modified", "mod", "paragraph", "par", "position", "pos", "previous", "prev", "region", "reg", "sentence", "sent", "window", "wind", "windows", "wind", NULL }; char ShortBuf[128]; int Shorten __(( const char *_(name) )); int Shorten(name) const char *name; { register const char *s = name, * const *tp, *te; register char *d = ShortBuf; register int n; while (*s) { for (tp = ShortTab; te = *tp++; tp++) { if (*te < *s) continue; if (*te > *s) { /* assume alphabetic order */ te = NULL; break; } if (te[n = numcomp(s, te)]) continue; /* exact match? */ if (!isalnum(s[n])) break; /* match */ } if (te) { /* match */ if (te = *tp) { /* get abbrev */ while (*d++ = *te++); --d; s += n; if (*s) *d++ = *s++; } } else { /* the original */ while (isalnum(*s)) *d++ = *s++; if (*s) *d++ = *s++; } } *d = '\0'; return (d - ShortBuf); } /*==============================*/ /* output processing */ /*==============================*/ void Pad __(( char *_(buf), int _(len) )); void Pad(buf, len) register char *buf; register int len; { while (--len >= 0) *buf++ = ' '; } void DoFormat __((char *_(buf), int _(key), int _(map), int _(field), int _(just), int _(cont) )); void DoFormat(buf, key, map, field, just, cont) char *buf; { char *s; register char *p, *q; int len, padlen; if (!(s = Pending(key, map))) s = KeyTab[map][key]; len = strlen(s); q = p = s; if (cont != '\\') q += len; /* preference for current */ DPRINTF(("[Initial: (field,len,p,q) (%d,%d,%d,%d) %s]\n", field, len, (int)(p - s), (int)(q - s), s)); if (len > field) { int short_len = Shorten(s); /* try short version first */ if (short_len < len) { len = short_len; p = q = s = ShortBuf; if (cont != '\\') q += len; } if (len > field && cont) { for (;;) { while (isalnum(*p)) p++; if ((p - s) >= field) break; q = p; p++; if (cont == '\\' && len - (p - s) <= field) break; } } } len = ((q - s) > field) ? field : (q - s); DPRINTF(("[final: (field,len,p,q) (%d,%d,%d,%d) %s]\n", field, len, (int)(p - s), (int)(q - s), s)); if (*q) { if (*q == '-') q++; if (*q) MakePending(key, map, q); } switch(just) { case '>': padlen = (field - len); break; case '-': padlen = (field - len) / 2; break; case '<': padlen = 0; } Pad(buf, padlen); strncpy(buf + padlen, s, len); Pad(buf + padlen + len, field - padlen - len); if (s != ShortBuf && s != KeyTab[map][key]) free(s); /* it was Pending, so cleanup */ } char *DoHat __(( char *_(base) )); char * DoHat(base) char *base; { register char *s = base; register int c; register int key, map; int newmap; newmap = 0; while((c = *s++) == '^') { c = *s++; c = toupper(c); if (('@' <= c && c < '`') || c == '?') key = c ^ '@'; else if (c == ':') key = function_key(s), s += 2; else if (c == '`') key = *s++; else { s--; break; } map = newmap; if (map >= 0 && !(newmap = ISPREFIX(key, map))) newmap = -1; /* unknown key sequence */ } s--; DPRINTF(("[Matched \"%.*s}{%6.6s\" map %d, key %d, newmap %d\n", (int)(s - base), base, s, map, key, newmap)); if (s > base) { /* we found a key sequence */ int justify; int continuation = 0; char *where = s; /* remember */ if (*s == '<' || *s == '-' || *s == '>') justify = *s++; while (*s++ == justify); --s; /* skip fill chars */ if (*s == '+' || *s == '\\') continuation = *s++; if (where == s) { /* at least one directive */ s = base; } else if (map < 0 || !KeyTab[map][key]) { Pad(base, (int)(s - base)); } else { DoFormat(base, key, map, (int)(s - base), justify, continuation); } } if (*s == '^') /* standalone "hat" */ s++; return s; } void gobble_input __(( void )); void gobble_input() { register char *s; while (fgets(line, sizeof line, stdin)) { if (*(s = line) == '#') continue; while (*s) { if (*s++ == '^') s = DoHat(s - 1); } if (*--s == '\n') { while (--s >= line && (*s == ' ' || *s == '\t')) ;/* remove trailing blanks */ ++s; *s++ = '\n'; *s = '\0'; } fputs(line, stdout); } } char inbuf[BUFSIZ], outbuf[BUFSIZ]; void main(argc, argv) int argc; char *argv[]; { _pname = *argv++; setbuf(stdin, inbuf); setbuf(stdout, outbuf); #ifdef DEBUG if (argc > 1 && strcmp(*argv, "-d") == 0) { debug++; argc--, argv++; } #endif if (--argc == 0) { fprintf(stderr, "usage: %s wall-chart-files