Added runtime rcfile parsing. - sam - An updated version of the sam text editor.
 (HTM) git clone git://vernunftzentrum.de/sam.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit 071c95698bc28739fb0289c84ff46a2278d1270c
 (DIR) parent 140ba1042462570fc3c966652ea1d6e79cd3ae93
 (HTM) Author: Rob King <jking@deadpixi.com>
       Date:   Fri,  9 Sep 2016 13:07:48 -0500
       
       Added runtime rcfile parsing.
       
       Diffstat:
         include/libg.h                      |       2 ++
         libXg/gwin.c                        |     121 ++++++++++++++++++++++---------
         libXg/libgint.h                     |       4 ++++
         libXg/xtbinit.c                     |       2 ++
         samterm/Makefile                    |       2 +-
         samterm/main.c                      |      12 ++++++++++++
         samterm/samrc.c                     |     255 +++++++++++++++++++++++++++++++
         samterm/samterm.h                   |       3 +++
       
       8 files changed, 365 insertions(+), 36 deletions(-)
       ---
 (DIR) diff --git a/include/libg.h b/include/libg.h
       @@ -230,4 +230,6 @@ extern  XftColor bgcolor;
        #define BPSHORT(p, v)       ((p)[0]=(v), (p)[1]=((v)>>8))
        #define BPLONG(p, v)        (BPSHORT(p, (v)), BPSHORT(p+2, (v)>>16))
        
       +extern int installbinding(int, KeySym, int, int);
       +extern int installchord(int, int, int, int);
        #endif
 (DIR) diff --git a/libXg/gwin.c b/libXg/gwin.c
       @@ -160,19 +160,6 @@ Mappingaction(Widget w, XEvent *e, String *p, Cardinal *np)
                            for (c = 0; c < composing; c++) \
                                (*f)(compose[c], 0, Tcurrent, 0, 0)
        
       -typedef struct Keymapping Keymapping;
       -struct Keymapping{
       -    int mask;
       -    int sym;
       -    int kind;
       -    int result;
       -};
       -
       -Keymapping keymappings[] ={
       -    #include "../commands.h"
       -    {0, 0, Kend, 0}
       -};
       -
        typedef struct Unikeysym Unikeysym;
        struct Unikeysym{
            KeySym keysym;
       @@ -195,6 +182,49 @@ keysymtoshort(KeySym k)
            return k;
        }
        
       +typedef struct Keymapping Keymapping;
       +struct Keymapping{
       +    Keymapping *next;
       +    int m;
       +    KeySym s;
       +    int k;
       +    int c;
       +};
       +
       +static Keymapping *keymappings = NULL;
       +
       +int
       +installbinding(int m, KeySym s, int k, int c)
       +{
       +    if (m < 0 || s == NoSymbol || k < 0 || c < 0)
       +        return -1;
       +
       +    Keymapping *km = calloc(1, sizeof(Keymapping));
       +    if (!km)
       +        return -1;
       +
       +    km->m = m;
       +    km->s = s;
       +    km->k = k;
       +    km->c = c;
       +
       +    km->next = keymappings;
       +    keymappings = km;
       +
       +    return 0;
       +}
       +
       +void
       +freebindings(void)
       +{
       +    Keymapping *m = keymappings;
       +    while (m){
       +        Keymapping *n = m->next;
       +        free(m);
       +        m = n;
       +    }
       +}
       +
        static void
        Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
        {
       @@ -220,12 +250,12 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
            XtTranslateKeycode(e->xany.display, (KeyCode)e->xkey.keycode, e->xkey.state, &md, &k);
        
            /* Check to see if it's a specially-handled key first. */
       -    for (Keymapping *m = keymappings; m && m->kind != Kend; m++){
       -        if (k == m->sym && m->kind != Kdefault){
       -            if ((e->xkey.state & m->mask) || m->mask == 0){
       +    for (Keymapping *m = keymappings; m; m = m->next){
       +        if (k == m->s){
       +            if ((e->xkey.state & m->m) || m->m == 0){
                        f = ((GwinWidget)w)->gwin.gotchar;
                        if (f)
       -                    (*f)(m->result, m->kind, Tcurrent, 0, 0);
       +                    (*f)(m->c, m->k, Tcurrent, 0, 0);
                        return;
                    }
                }
       @@ -308,24 +338,45 @@ Keyaction(Widget w, XEvent *e, String *p, Cardinal *np)
        
        typedef struct Chordmapping Chordmapping;
        struct Chordmapping{
       -    int start;
       -    int end;
       -    int kind;
       -    int result;
       -    int target;
       +    Chordmapping *next;
       +    int s1;
       +    int s2;
       +    int c;
       +    int t;
        };
        
       -#define Bnone  0
       -#define B1     1
       -#define B2     2
       -#define B3     4
       -#define B4     8
       -#define B5    16
       +static Chordmapping *chordmap = NULL;
        
       -Chordmapping chordmappings[] ={
       -    #include "../chords.h"
       -    {0, 0, Kend, 0, 0}
       -};
       +int
       +installchord(int s1, int s2, int c, int t)
       +{
       +    if (s1 < 0 || s2 < 0 || c < 0 || (t != Tmouse && t != Tcurrent))
       +        return -1;
       +
       +    Chordmapping *m = calloc(1, sizeof(Chordmapping));
       +    if (!m)
       +        return -1;
       +
       +    m->s1 = s1;
       +    m->s2 = s2;
       +    m->c = c;
       +    m->t = t;
       +
       +    m->next = chordmap;
       +    chordmap = m;
       +    return 0;
       +}
       +
       +void
       +freechords(void)
       +{
       +    Chordmapping *m = chordmap;
       +    while (m){
       +        Chordmapping *n = m->next;
       +        free(m);
       +        m = n;
       +    }
       +}
        
        static void
        Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
       @@ -391,11 +442,11 @@ Mouseaction(Widget w, XEvent *e, String *p, Cardinal *np)
            if(s & Button5Mask) m.buttons |= 16;
        
            /* Check to see if it's a chord first. */
       -    for (Chordmapping *cm = chordmappings; cm && cm->kind != Kend; cm++){
       -        if (ob == cm->start && m.buttons == cm->end){
       +    for (Chordmapping *cm = chordmap; cm; cm = cm->next){
       +        if (ob == cm->s1 && m.buttons == cm->s2){
                    Charfunc kf = ((GwinWidget)w)->gwin.gotchar;
                    if (kf)
       -                (*kf)(cm->result, cm->kind, cm->target, m.xy.x, m.xy.y);
       +                (*kf)(cm->c, Kcommand, cm->t, m.xy.x, m.xy.y);
        
                    m.buttons = 0;
                }
 (DIR) diff --git a/libXg/libgint.h b/libXg/libgint.h
       @@ -29,6 +29,10 @@ typedef char*   caddr_t;
        #undef Font
        #undef Event
        
       +/* binding and chord management */
       +void freechords(void);
       +void freebindings(void);
       +
        /* Cursor initialization */
        void initcursors(void);
        
 (DIR) diff --git a/libXg/xtbinit.c b/libXg/xtbinit.c
       @@ -209,6 +209,8 @@ xtbinit(Errfunc f, char *class, int *pargc, char **argv, char **fallbacks)
            }
        
            initcursors();
       +    atexit(freebindings);
       +    atexit(freechords);
        
            font = XftFontOpenName(_dpy, DefaultScreen(_dpy), getenv("FONT") ? getenv("FONT") : "monospace");
            screen.id = 0;
 (DIR) diff --git a/samterm/Makefile b/samterm/Makefile
       @@ -22,7 +22,7 @@ CFLAGS=$(INCS) $(STANDARDS) $(INCLUDES)
        LIBS=../libframe/libframe.a ../libXg/libXg.a
        CC?=c99
        
       -OBJ=main.o flayer.o icons.o io.o menu.o mesg.o rasp.o scroll.o unix.o
       +OBJ=main.o flayer.o icons.o io.o menu.o mesg.o rasp.o samrc.o scroll.o unix.o
        
        all:        samterm
        
 (DIR) diff --git a/samterm/main.c b/samterm/main.c
       @@ -47,6 +47,18 @@ main(int argc, char *argv[])
            Rectangle r;
            Flayer *nwhich;
            int fwdbut;
       +    char rcpath[PATH_MAX + 1] = {0};
       +    FILE *rc = NULL;
       +
       +    installdefaultbindings();
       +    installdefaultchords();
       +
       +    snprintf(rcpath, PATH_MAX, "%s/.samrc", getenv("HOME") ? getenv("HOME") : ".");
       +    rc = fopen(rcpath, "r");
       +    if (rc){
       +        loadrcfile(rc);
       +        fclose(rc);
       +    }
        
            while ((opt = getopt(argc, argv, "efr:")) != -1){
                switch (opt){
 (DIR) diff --git a/samterm/samrc.c b/samterm/samrc.c
       @@ -0,0 +1,255 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <strings.h>
       +#include <X11/Xlib.h>
       +#include <X11/keysym.h>
       +
       +#include <commands.h>
       +#include <u.h>
       +#include <libg.h>
       +
       +typedef struct Namemapping Namemapping;
       +struct Namemapping{
       +    const char *name;
       +    int value;
       +};
       +
       +static Namemapping commandmapping[] ={
       +    {"escape",          Cescape},
       +    {"scrolldown",      Cscrolldown},
       +    {"scrollup",        Cscrollup},
       +    {"scrolldownline",  Cscrolldownline},
       +    {"scrollupline",    Cscrollupline},
       +    {"jump",            Cjump},
       +    {"charright",       Ccharright},
       +    {"charleft",        Ccharleft},
       +    {"linedown",        Clinedown},
       +    {"lineup",          Clineup},
       +    {"delword",         Cdelword},
       +    {"delbol",          Cdelbol},
       +    {"del",             Cdel},
       +    {"snarf",           Csnarf},
       +    {"cut",             Ccut},
       +    {"paste",           Cpaste},
       +    {"exchange",        Cexchange},
       +    {"write",           Cwrite},
       +    {"eol",             Ceol},
       +    {"bol",             Cbol},
       +    {NULL, 0}
       +};
       +
       +static Namemapping targetmapping[] ={
       +    {"current",     Tcurrent},
       +    {"mouse",       Tmouse},
       +    {NULL, 0}
       +};
       +
       +static Namemapping buttonmapping[] ={
       +    {"0",  0}, 
       +    {"n",  0}, 
       +#define B1 1
       +    {"1",  1}, 
       +#define B2 2
       +    {"2",  2}, 
       +#define B3 4
       +    {"3",  4}, 
       +#define B4 8
       +    {"4",  8}, 
       +#define B5 16
       +    {"5", 16}, 
       +    {NULL, 0}
       +};
       +
       +static Namemapping modmapping[] ={
       +    {"n", 0},
       +    {"c", ControlMask}, 
       +    {"a", Mod1Mask}, 
       +    {"m", Mod1Mask}, 
       +    {"s", Mod2Mask}, 
       +    {"h", Mod3Mask}, 
       +    {"1", Mod1Mask}, 
       +    {"2", Mod2Mask}, 
       +    {"3", Mod3Mask}, 
       +    {"4", Mod4Mask}, 
       +    {"5", Mod5Mask}, 
       +    {NULL, 0}
       +};
       +
       +static int
       +lookupmapping(const char *n, Namemapping *m)
       +{
       +    for (Namemapping *k = m; k->name != NULL; k++){
       +        if (strcasecmp(k->name, n) == 0)
       +            return k->value;
       +    }
       +
       +    return -1;
       +}
       +
       +static int
       +nametocommand(const char *n)
       +{
       +    return lookupmapping(n, commandmapping);
       +}
       +
       +static int
       +nametotarget(const char *n)
       +{
       +    return lookupmapping(n, targetmapping);
       +}
       +
       +typedef struct Defaultbinding Defaultbinding;
       +struct Defaultbinding{
       +    int modifiers;
       +    KeySym keysym;
       +    int kind;
       +    int command;
       +};
       +
       +static Defaultbinding defaultbindings[] ={    
       +    /* Motion commands. */
       +    {ControlMask, XK_e,             Kcommand,  Clineup},
       +    {ControlMask, XK_x,             Kcommand,  Clinedown},
       +    {ControlMask, XK_d,             Kcommand,  Ccharright},
       +    {ControlMask, XK_s,             Kcommand,  Ccharleft},
       +    {ControlMask, XK_u,             Kcommand,  Cdelbol},
       +    {ControlMask, XK_w,             Kcommand,  Cdelword},
       +    {ControlMask, XK_k,             Kcommand,  Cjump},
       +    {ControlMask, XK_BackSpace,     Kcommand,  Cdelword},
       +    {ControlMask, XK_y,             Kcommand,  Ccut},
       +    {ControlMask, XK_c,             Kcommand,  Csnarf},
       +    {ControlMask, XK_v,             Kcommand,  Cpaste},
       +    {ControlMask, XK_q,             Kcommand,  Cexchange},
       +    
       +    /* Use Control-Tab to insert a literal tab when tab expansion is enabled. */
       +    {ControlMask, XK_Tab,           Kcomposed, '\t'},
       +    
       +    /* Handle arrow keys, page up/down, and escape. */
       +    {0,           XK_Up,            Kcommand, Cscrollup},
       +    {0,           XK_Prior,         Kcommand, Cscrollup},
       +    {0,           XK_Left,          Kcommand, Cscrollup},
       +    {0,           XK_Down,          Kcommand, Cscrolldown},
       +    {0,           XK_Next,          Kcommand, Cscrolldown},
       +    {0,           XK_Right,         Kcommand, Cscrolldown},
       +    {0,           XK_Escape,        Kcommand, Cescape},
       +    
       +    /* More fundamental stuff: backspace, delete, etc. */
       +    {0,           XK_BackSpace,     Kcommand, Cdel},
       +    {0,           XK_Delete,        Kcommand, Cdel},
       +    {0,           XK_Return,        Kraw,     '\n'},
       +    {0,           XK_KP_Enter,      Kraw,     '\n'},
       +    {0,           XK_Linefeed,      Kraw,     '\r'},
       +    {0,           XK_Tab,           Kraw,     '\t'},
       +    {0,           XK_KP_0,          Kraw,     '0'},
       +    {0,           XK_KP_1,          Kraw,     '1'},
       +    {0,           XK_KP_2,          Kraw,     '2'},
       +    {0,           XK_KP_3,          Kraw,     '3'},
       +    {0,           XK_KP_4,          Kraw,     '4'},
       +    {0,           XK_KP_5,          Kraw,     '5'},
       +    {0,           XK_KP_6,          Kraw,     '6'},
       +    {0,           XK_KP_7,          Kraw,     '7'},
       +    {0,           XK_KP_8,          Kraw,     '8'},
       +    {0,           XK_KP_9,          Kraw,     '9'},
       +    {0,           XK_KP_Divide,     Kraw,     '/'},
       +    {0,           XK_KP_Multiply,   Kraw,     '*'},
       +    {0,           XK_KP_Subtract,   Kraw,     '-'},
       +    {0,           XK_KP_Add,        Kraw,     '+'},
       +    {0,           XK_KP_Decimal,    Kraw,     '.'},
       +    {0,           XK_hyphen,        Kraw,     '-'},
       +    {0,           0,                Kend,     0}
       +};
       +
       +void
       +installdefaultbindings(void)
       +{
       +    for (Defaultbinding *b = defaultbindings; b->kind != Kend; b++)
       +        installbinding(b->modifiers, b->keysym, b->kind, b->command);
       +}
       +
       +typedef struct Defaultchord Defaultchord;
       +struct Defaultchord{
       +    int state1;
       +    int state2;
       +    int command;
       +    int target;
       +};
       +
       +static Defaultchord defaultchords[] ={
       +    {B1, B1|B2,  Ccut,   Tcurrent},
       +    {B1, B1|B3,  Cpaste, Tcurrent},
       +    {B1|B2, B1,  Cnone,  Tcurrent},
       +    {B1|B3, B1,  Cnone,  Tcurrent},
       +
       +    {B4, 0,  Cscrollupline,   Tmouse},
       +    {B5, 0,  Cscrolldownline, Tmouse},
       +
       +    {0, 0, Kend, 0}
       +};
       +
       +void
       +installdefaultchords(void)
       +{
       +    for (Defaultchord *c = defaultchords; c->state1 != 0; c++)
       +        installchord(c->state1, c->state2, c->command, c->target);
       +}
       +
       +static int
       +statetomask(const char *n, Namemapping *m)
       +{
       +    int r = 0;
       +    for (int i = 0; n[i] != 0; i++){
       +        char s[2] = {n[i], 0};
       +        int v = lookupmapping(s, m);
       +        if (v < 0)
       +            return -1;
       +        r |= v;
       +    }
       +
       +    return r;
       +}
       +
       +void
       +loadrcfile(FILE *f)
       +{
       +    char *l = NULL;
       +    size_t n = 0;
       +    ssize_t r = 0;
       +    size_t ln = 0;
       +
       +    while ((r = getline(&l, &n, f)) >= 0){
       +        char s1[6] = {0};
       +        char s2[6] = {0};
       +        char cname[100] = {0};
       +        char tname[100] = {0};
       +        char c = 0;
       +        unsigned short i = 0;
       +        int rc = 0;
       +
       +        ln++;
       +        if (r == 0 || l[0] == '\n' || l[0] == 0)
       +            continue;
       +
       +        if (sscanf(l, " chord %5[Nn12345] %5[Nn12345] %99s %99s", s1, s2, cname, tname) == 4)
       +            rc = installchord(statetomask(s1, buttonmapping), statetomask(s2, buttonmapping), nametocommand(cname), nametotarget(tname));
       +        else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s raw %hx", s1, s2, &i) == 3)
       +            rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s2), Kraw, i);
       +        else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s composed %hx", s1, s2, &i) == 3)
       +            rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s2), Kcomposed, i);
       +        else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s raw %c", s1, s2, &c) == 3)
       +            rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s2), Kraw, c);
       +        else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s composed %c", s1, s2, &c) == 3)
       +            rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s2), Kcomposed, c);
       +        else if (sscanf(l, " bind %5[ncamshNCAMSH12345] %99s command %99s", s1, s2, cname) == 3)
       +            rc = installbinding(statetomask(s1, modmapping), XStringToKeysym(s2), Kcommand, nametocommand(cname));
       +        else if (sscanf(l, " %[#]", &c) == 1)
       +            continue;
       +        else
       +            fprintf(stderr, "invalid rc line %zd\n", ln);
       +
       +        if (rc != 0)
       +            fprintf(stderr, "invalid chord/binding on rc line %zd\n", ln);
       +    }
       +
       +    free(l);
       +}
 (DIR) diff --git a/samterm/samterm.h b/samterm/samterm.h
       @@ -157,3 +157,6 @@ void    outlong(long);
        void    outvlong(void*);
        void    outsend(void);
        int getlayer(const Flayer *l, const Text *t);
       +void loadrcfile(FILE *);
       +void installdefaultbindings(void);
       +void installdefaultchords(void);