URI: 
       tconfig.c - ltk - Socket-based GUI for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tconfig.c (21569B)
       ---
            1 /* FIXME: This really is horrible. */
            2 
            3 #include <stdio.h>
            4 #include <ctype.h>
            5 #include <string.h>
            6 #include <limits.h>
            7 
            8 #include "util.h"
            9 #include "keys.h"
           10 #include "memory.h"
           11 #include "config.h"
           12 
           13 static int parse_keysym(char *text, size_t len, ltk_keysym *sym_ret);
           14 
           15 ltk_config *global_config = NULL;
           16 
           17 enum toktype {
           18         STRING,
           19         SECTION,
           20         EQUALS,
           21         NEWLINE,
           22         ERROR,
           23         END
           24 };
           25 
           26 #if 0
           27 static const char *
           28 toktype_str(enum toktype type) {
           29         switch (type) {
           30         case STRING:
           31                 return "string";
           32                 break;
           33         case SECTION:
           34                 return "section";
           35                 break;
           36         case EQUALS:
           37                 return "equals";
           38                 break;
           39         case NEWLINE:
           40                 return "newline";
           41                 break;
           42         case ERROR:
           43                 return "error";
           44                 break;
           45         case END:
           46                 return "end of file";
           47                 break;
           48         default:
           49                 return "unknown";
           50         }
           51 }
           52 #endif
           53 
           54 struct token {
           55         char *text;
           56         size_t len;
           57         enum toktype type;
           58         size_t line;        /* line in original input */
           59         size_t line_offset; /* offset from start of line */
           60 };
           61 
           62 struct lexstate {
           63         const char *filename;
           64         char *text;
           65         size_t len;        /* length of text */
           66         size_t cur;        /* current byte position */
           67         size_t cur_line;   /* current line */
           68         size_t line_start; /* byte offset of start of current line */
           69 };
           70 
           71 static struct token
           72 next_token(struct lexstate *s) {
           73         char c;
           74         struct token tok;
           75         int finished = 0;
           76         while (1) {
           77                 if (s->cur >= s->len)
           78                         return (struct token){NULL, 0, END, s->cur_line, s->cur - s->line_start + 1};
           79                 while (isspace(c = s->text[s->cur])) {
           80                         s->cur++;
           81                         if (c == '\n') {
           82                                 struct token tok = (struct token){s->text + s->cur - 1, 1, NEWLINE, s->cur_line, s->cur - s->line_start};
           83                                 s->cur_line++;
           84                                 s->line_start = s->cur;
           85                                 return tok;
           86                         }
           87                         if (s->cur >= s->len)
           88                                 return (struct token){NULL, 0, END, s->cur_line, s->cur - s->line_start + 1};
           89                 }
           90 
           91                 switch (s->text[s->cur]) {
           92                 case '#':
           93                         s->cur++;
           94                         while (s->cur < s->len && s->text[s->cur] != '\n')
           95                                 s->cur++;
           96                         continue;
           97                 case '[':
           98                         s->cur++;
           99                         tok = (struct token){s->text + s->cur, 0, SECTION, s->cur_line, s->cur - s->line_start + 1};
          100                         finished = 0;
          101                         while (s->cur < s->len) {
          102                                 char c = s->text[s->cur];
          103                                 if (c == '\n') {
          104                                         break;
          105                                 } else if (c == ']') {
          106                                         s->cur++;
          107                                         finished = 1;
          108                                         break;
          109                                 } else  {
          110                                         tok.len++;
          111                                 }
          112                                 s->cur++;
          113                         }
          114                         if (!finished) {
          115                                 tok.text = "Unfinished section name";
          116                                 tok.len = strlen("Unfinished section name");
          117                                 tok.type = ERROR;
          118                         }
          119                         break;
          120                 case '=':
          121                         tok = (struct token){s->text + s->cur, 1, EQUALS, s->cur_line, s->cur - s->line_start + 1};
          122                         s->cur++;
          123                         break;
          124                 case '"':
          125                         /* FIXME: error if next char is not whitespace or end */
          126                         s->cur++;
          127                         tok = (struct token){s->text + s->cur, 0, STRING, s->cur_line, s->cur - s->line_start + 1};
          128                         size_t shift = 0, bs = 0;
          129                         finished = 0;
          130                         while (s->cur < s->len) {
          131                                 char c = s->text[s->cur];
          132                                 if (c == '\n') {
          133                                         break;
          134                                 } else if (c == '\\') {
          135                                         shift += bs;
          136                                         tok.len += bs;
          137                                         bs = (bs + 1) % 2;
          138                                 } else if (c == '"') {
          139                                         if (bs) {
          140                                                 shift++;
          141                                                 tok.len++;
          142                                                 bs = 0;
          143                                         } else {
          144                                                 s->cur++;
          145                                                 finished = 1;
          146                                                 break;
          147                                         }
          148                                 } else {
          149                                         tok.len++;
          150                                 }
          151                                 s->text[s->cur - shift] = s->text[s->cur];
          152                                 s->cur++;
          153                         }
          154                         if (!finished) {
          155                                 tok.text = "Unfinished string";
          156                                 tok.len = strlen("Unfinished string");
          157                                 tok.type = ERROR;
          158                         }
          159                         break;
          160                 default:
          161                         tok = (struct token){s->text + s->cur, 1, STRING, s->cur_line, s->cur - s->line_start + 1};
          162                         s->cur++;
          163                         while (s->cur < s->len) {
          164                                 char c = s->text[s->cur];
          165                                 if (isspace(c) || c == '=') {
          166                                         break;
          167                                 } else if (c == '"') {
          168                                         tok.text = "Unexpected start of string";
          169                                         tok.len = strlen("Unexpected start of string");
          170                                         tok.type = ERROR;
          171                                         tok.line_offset = s->cur - s->line_start + 1;
          172                                 } else if (c == '[' || c == ']') {
          173                                         tok.text = "Unexpected start or end of section name";
          174                                         tok.len = strlen("Unexpected start or end of section name");
          175                                         tok.type = ERROR;
          176                                         tok.line_offset = s->cur - s->line_start + 1;
          177                                 }
          178                                 tok.len++;
          179                                 s->cur++;
          180                         }
          181                 }
          182                 return tok;
          183         }
          184 }
          185 
          186 #undef MIN
          187 #define MIN(a, b) ((a) < (b) ? (a) : (b))
          188 
          189 static int
          190 parse_modmask(char *modmask_str, size_t len, ltk_mod_type *mask_ret) {
          191         size_t cur = 0;
          192         *mask_ret = 0;
          193         while (cur < len) {
          194                 if (str_array_equal("shift", modmask_str + cur, MIN(5, len - cur))) {
          195                         cur += 5;
          196                         *mask_ret |= LTK_MOD_SHIFT;
          197                 } else if (str_array_equal("ctrl", modmask_str + cur, MIN(4, len - cur))) {
          198                         cur += 4;
          199                         *mask_ret |= LTK_MOD_CTRL;
          200                 } else if (str_array_equal("alt", modmask_str + cur, MIN(3, len - cur))) {
          201                         cur += 3;
          202                         *mask_ret |= LTK_MOD_ALT;
          203                 } else if (str_array_equal("super", modmask_str + cur, MIN(5, len - cur))) {
          204                         cur += 5;
          205                         *mask_ret |= LTK_MOD_SUPER;
          206                 } else if (str_array_equal("any", modmask_str + cur, MIN(3, len - cur))) {
          207                         cur += 3;
          208                         *mask_ret = UINT_MAX;
          209                 } else {
          210                         return 1;
          211                 }
          212                 if (cur < len && modmask_str[cur] != '|')
          213                         return 1;
          214                 else
          215                         cur++;
          216         }
          217         return 0;
          218 }
          219 
          220 static int
          221 parse_flags(char *text, size_t len, ltk_key_binding_flags *flags_ret) {
          222         if (str_array_equal("run-always", text, len))
          223                 *flags_ret = LTK_KEY_BINDING_RUN_ALWAYS;
          224         else
          225                 return 1;
          226         return 0;
          227 }
          228 
          229 static int
          230 parse_keypress_binding(
          231     struct lexstate *s, struct token *tok,
          232     ltk_keypress_binding *binding_ret,
          233     char **func_name_ret, size_t *func_len_ret,
          234     char **errstr) {
          235         ltk_keypress_binding b = {NULL, NULL, LTK_KEY_NONE, LTK_MOD_NONE, LTK_KEY_BINDING_NOFLAGS};
          236         *tok = next_token(s);
          237         char *msg = NULL;
          238         if (tok->type != STRING) {
          239                 msg = "Invalid token type";
          240                 goto error;
          241         }
          242         *func_name_ret = tok->text;
          243         *func_len_ret = tok->len;
          244         struct token prevtok;
          245         int text_init = 0, rawtext_init = 0, sym_init = 0, mods_init = 0, flags_init = 0;
          246         while (1) {
          247                 *tok = next_token(s);
          248                 if (tok->type == NEWLINE || tok->type == END)
          249                         break;
          250                 prevtok = *tok;
          251                 *tok = next_token(s);
          252                 if (prevtok.type != STRING) {
          253                         msg = "Invalid token type";
          254                         *tok = prevtok;
          255                         goto error;
          256                 } else if (tok->type != STRING) {
          257                         msg = "Invalid token type";
          258                         goto error;
          259                 } else if (str_array_equal("text", prevtok.text, prevtok.len)) {
          260                         if (rawtext_init) {
          261                                 msg = "Rawtext already specified";
          262                                 goto error;
          263                         } else if (sym_init) {
          264                                 msg = "Keysym already specified";
          265                                 goto error;
          266                         } else if (text_init) {
          267                                 msg = "Duplicate text specification";
          268                                 goto error;
          269                         }
          270                         b.text = ltk_strndup(tok->text, tok->len);
          271                         text_init = 1;
          272                 } else if (str_array_equal("rawtext", prevtok.text, prevtok.len)) {
          273                         if (text_init) {
          274                                 msg = "Text already specified";
          275                                 goto error;
          276                         } else if (sym_init) {
          277                                 msg = "Keysym already specified";
          278                                 goto error;
          279                         } else if (rawtext_init) {
          280                                 msg = "Duplicate rawtext specification";
          281                                 goto error;
          282                         }
          283                         b.rawtext = ltk_strndup(tok->text, tok->len);
          284                         rawtext_init = 1;
          285                 } else if (str_array_equal("sym", prevtok.text, prevtok.len)) {
          286                         if (text_init) {
          287                                 msg = "Text already specified";
          288                                 goto error;
          289                         } else if (rawtext_init) {
          290                                 msg = "Rawtext already specified";
          291                                 goto error;
          292                         } else if (sym_init) {
          293                                 msg = "Duplicate keysym specification";
          294                                 goto error;
          295                         } else if (parse_keysym(tok->text, tok->len, &b.sym)) {
          296                                 msg = "Invalid keysym specification";
          297                                 goto error;
          298                         }
          299                         sym_init = 1;
          300                 } else if (str_array_equal("mods", prevtok.text, prevtok.len)) {
          301                         if (mods_init) {
          302                                 msg = "Duplicate mods specification";
          303                                 goto error;
          304                         } else if (parse_modmask(tok->text, tok->len, &b.mods)) {
          305                                 msg = "Invalid mods specification";
          306                                 goto error;
          307                         }
          308                         mods_init = 1;
          309                 } else if (str_array_equal("flags", prevtok.text, prevtok.len)) {
          310                         if (flags_init) {
          311                                 msg = "Duplicate flags specification";
          312                                 goto error;
          313                         } else if (parse_flags(tok->text, tok->len, &b.flags)) {
          314                                 msg = "Invalid flags specification";
          315                                 goto error;
          316                         }
          317                         flags_init = 1;
          318                 } else {
          319                         msg = "Invalid keyword";
          320                         *tok = prevtok;
          321                         goto error;
          322                 }
          323         };
          324         if (!text_init && !rawtext_init && !sym_init) {
          325                 msg = "One of text, rawtext, and sym must be initialized";
          326                 goto error;
          327         }
          328         *binding_ret = b;
          329         return 0;
          330 error:
          331         free(b.text);
          332         free(b.rawtext);
          333         if (msg) {
          334                 *errstr = ltk_print_fmt(
          335                     "%s, line %zu, offset %zu: %s", s->filename, tok->line, tok->line_offset, msg
          336                 );
          337         } else {
          338                 *errstr = NULL;
          339         }
          340         return 1;
          341 }
          342 
          343 static int
          344 parse_keybinding(
          345     struct lexstate *s,
          346     struct token *tok,
          347     char *widget,
          348     size_t len,
          349     keypress_binding_handler press_handler,
          350     keyrelease_binding_handler release_handler,
          351     char **errstr) {
          352         char *msg = NULL;
          353         *tok = next_token(s);
          354         if (tok->type == SECTION || tok->type == END) {
          355                 return -1;
          356         } else if (tok->type == NEWLINE) {
          357                 return 0; /* empty line */
          358         } else if (tok->type != STRING) {
          359                 msg = "Invalid token type";
          360                 goto error;
          361         } else if (str_array_equal("bind-keypress", tok->text, tok->len)) {
          362                 ltk_keypress_binding b;
          363                 char *name;
          364                 size_t nlen;
          365                 if (parse_keypress_binding(s, tok, &b, &name, &nlen, errstr))
          366                         return 1;
          367                 if (press_handler(widget, len, name, nlen, b)) {
          368                         msg = "Invalid key binding";
          369                         goto error;
          370                 }
          371         } else if (str_array_equal("bind-keyrelease", tok->text, tok->len)) {
          372                 ltk_keypress_binding b;
          373                 char *name;
          374                 size_t nlen;
          375                 if (parse_keypress_binding(s, tok, &b, &name, &nlen, errstr))
          376                         return 1;
          377                 if (b.text || b.rawtext) {
          378                         free(b.text);
          379                         free(b.rawtext);
          380                         msg = "Text and rawtext may only be specified for keypress bindings";
          381                         goto error;
          382                 }
          383                 if (release_handler(widget, len, name, nlen, (ltk_keyrelease_binding){b.sym, b.mods, b.flags})) {
          384                         msg = "Invalid key binding";
          385                         goto error;
          386                 }
          387         } else {
          388                 msg = "Invalid statement";
          389                 goto error;
          390         }
          391         return 0;
          392 error:
          393         if (msg) {
          394                 *errstr = ltk_print_fmt(
          395                     "%s, line %zu, offset %zu: %s", s->filename, tok->line, tok->line_offset, msg
          396                 );
          397         }
          398         return 1;
          399 }
          400 
          401 static void
          402 push_lang_mapping(ltk_config *c) {
          403         if (c->mappings_alloc == c->mappings_len) {
          404                 c->mappings_alloc = ideal_array_size(c->mappings_alloc, c->mappings_len + 1);
          405                 c->mappings = ltk_reallocarray(c->mappings, c->mappings_alloc, sizeof(ltk_language_mapping));
          406         }
          407         c->mappings[c->mappings_len].lang = NULL;
          408         c->mappings[c->mappings_len].mappings = NULL;
          409         c->mappings[c->mappings_len].mappings_alloc = 0;
          410         c->mappings[c->mappings_len].mappings_len = 0;
          411         c->mappings_len++;
          412 }
          413 
          414 static void
          415 push_text_mapping(ltk_config *c, char *text1, size_t len1, char *text2, size_t len2) {
          416         if (c->mappings_len == 0)
          417                 return; /* I guess just fail silently... */
          418         ltk_language_mapping *m = &c->mappings[c->mappings_len - 1];
          419         if (m->mappings_alloc == m->mappings_len) {
          420                 m->mappings_alloc = ideal_array_size(m->mappings_alloc, m->mappings_len + 1);
          421                 m->mappings = ltk_reallocarray(m->mappings, m->mappings_alloc, sizeof(ltk_keytext_mapping));
          422         }
          423         m->mappings[m->mappings_len].from = ltk_strndup(text1, len1);
          424         m->mappings[m->mappings_len].to = ltk_strndup(text2, len2);
          425         m->mappings_len++;
          426 }
          427 
          428 static void
          429 destroy_config(ltk_config *c) {
          430         for (size_t i = 0; i < c->mappings_len; i++) {
          431                 ltk_free(c->mappings[i].lang);
          432                 for (size_t j = 0; j < c->mappings[i].mappings_len; j++) {
          433                         ltk_free(c->mappings[i].mappings[j].from);
          434                         ltk_free(c->mappings[i].mappings[j].to);
          435                 }
          436                 ltk_free(c->mappings[i].mappings);
          437         }
          438         ltk_free(c->general.line_editor);
          439         ltk_free(c->mappings);
          440         ltk_free(c);
          441 }
          442 
          443 void
          444 ltk_config_cleanup(void) {
          445         if (global_config)
          446                 destroy_config(global_config);
          447         global_config = NULL;
          448 }
          449 
          450 ltk_config *
          451 ltk_config_get(void) {
          452         return global_config;
          453 }
          454 
          455 int
          456 ltk_config_get_language_index(char *lang, size_t *idx_ret) {
          457         if (!global_config)
          458                 return 1;
          459         for (size_t i = 0; i < global_config->mappings_len; i++) {
          460                 if (!strcmp(lang, global_config->mappings[i].lang)) {
          461                         *idx_ret = i;
          462                         return 0;
          463                 }
          464         }
          465         return 1;
          466 }
          467 
          468 ltk_language_mapping *
          469 ltk_config_get_language_mapping(size_t idx) {
          470         if (!global_config || idx >= global_config->mappings_len)
          471                 return NULL;
          472         return &global_config->mappings[idx];
          473 }
          474 
          475 int
          476 str_array_prefix(const char *str, const char *ar, size_t len) {
          477         size_t slen = strlen(str);
          478         if (len < slen)
          479                 return 0;
          480         return !strncmp(str, ar, slen);
          481 }
          482 
          483 /* WARNING: errstr must be freed! */
          484 /* FIXME: make ltk_load_file give size_t; handle errors there (copy from ledit) */
          485 static int
          486 load_from_text(
          487     const char *filename,
          488     char *file_contents,
          489     size_t len,
          490     keypress_binding_handler press_handler,
          491     keyrelease_binding_handler release_handler,
          492     char **errstr) {
          493         ltk_config *config = ltk_malloc(sizeof(ltk_config));
          494         config->mappings = NULL;
          495         config->mappings_alloc = config->mappings_len = 0;
          496         config->general.explicit_focus = 0;
          497         config->general.all_activatable = 0;
          498         config->general.line_editor = NULL;
          499 
          500         struct lexstate s = {filename, file_contents, len, 0, 1, 0};
          501         struct token tok = next_token(&s);
          502         int start_of_line = 1;
          503         char *msg = NULL;
          504         struct token secttok;
          505         while (tok.type != END) {
          506                 switch (tok.type) {
          507                 case SECTION:
          508                         if (!start_of_line) {
          509                                 msg = "Section can only start at new line";
          510                                 goto error;
          511                         }
          512                         secttok = tok;
          513                         tok = next_token(&s);
          514                         if (tok.type != NEWLINE && tok.type != END) {
          515                                 msg = "Section must be alone on line";
          516                                 goto error;
          517                         }
          518                         /* FIXME: generalize (at least once more options are added) */
          519                         if (str_array_equal("general", secttok.text, secttok.len)) {
          520                                 struct token prev1tok, prev2tok;
          521                                 while (1) {
          522                                         tok = next_token(&s);
          523                                         if (tok.type == SECTION || tok.type == END)
          524                                                 break;
          525                                         else if (tok.type == NEWLINE)
          526                                                 continue;
          527                                         prev2tok = tok;
          528                                         tok = next_token(&s);
          529                                         prev1tok = tok;
          530                                         tok = next_token(&s);
          531                                         if (prev2tok.type != STRING || prev1tok.type != EQUALS || tok.type != STRING) {
          532                                                 msg = "Invalid assignment statement";
          533                                                 goto error;
          534                                         }
          535                                         if (str_array_equal("explicit-focus", prev2tok.text, prev2tok.len)) {
          536                                                 if (str_array_equal("true", tok.text, tok.len)) {
          537                                                         config->general.explicit_focus = 1;
          538                                                 } else if (str_array_equal("false", tok.text, tok.len)) {
          539                                                         config->general.explicit_focus = 0;
          540                                                 } else {
          541                                                         msg = "Invalid boolean setting";
          542                                                         goto error;
          543                                                 }
          544                                         } else if (str_array_equal("all-activatable", prev2tok.text, prev2tok.len)) {
          545                                                 if (str_array_equal("true", tok.text, tok.len)) {
          546                                                         config->general.all_activatable = 1;
          547                                                 } else if (str_array_equal("false", tok.text, tok.len)) {
          548                                                         config->general.all_activatable = 0;
          549                                                 } else {
          550                                                         msg = "Invalid boolean setting";
          551                                                         goto error;
          552                                                 }
          553                                         } else if (str_array_equal("line-editor", prev2tok.text, prev2tok.len)) {
          554                                                 config->general.line_editor = ltk_strndup(tok.text, tok.len);
          555                                         } else {
          556                                                 msg = "Invalid setting";
          557                                                 goto error;
          558                                         }
          559                                         tok = next_token(&s);
          560                                         if (tok.type == END) {
          561                                                 break;
          562                                         } else if (tok.type != NEWLINE) {
          563                                                 msg = "Invalid assignment statement";
          564                                                 goto error;
          565                                         }
          566                                         start_of_line = 1;
          567                                 }
          568                         } else if (str_array_prefix("key-binding:", secttok.text, secttok.len)) {
          569                                 int ret = 0;
          570                                 char *widget = secttok.text + strlen("key-binding:");
          571                                 size_t len = secttok.len - strlen("key-binding:");
          572                                 while (1) {
          573                                         if ((ret = parse_keybinding(&s, &tok, widget, len, press_handler, release_handler, errstr)) > 0) {
          574                                                 goto errornomsg;
          575                                         } else if (ret < 0) {
          576                                                 start_of_line = 1;
          577                                                 break;
          578                                         }
          579                                 }
          580                         } else if (str_array_equal("key-mapping", secttok.text, secttok.len)) {
          581                                 int lang_init = 0;
          582                                 push_lang_mapping(config);
          583                                 struct token prev1tok, prev2tok;
          584                                 while (1) {
          585                                         tok = next_token(&s);
          586                                         if (tok.type == SECTION || tok.type == END)
          587                                                 break;
          588                                         else if (tok.type == NEWLINE)
          589                                                 continue;
          590                                         prev2tok = tok;
          591                                         tok = next_token(&s);
          592                                         prev1tok = tok;
          593                                         tok = next_token(&s);
          594                                         if (prev2tok.type != STRING) {
          595                                                 msg = "Invalid statement in language mapping";
          596                                                 goto error;
          597                                         }
          598                                         if (str_array_equal("language", prev2tok.text, prev2tok.len)) {
          599                                                 if (prev1tok.type != EQUALS || tok.type != STRING) {
          600                                                         msg = "Invalid language assignment";
          601                                                         goto error;
          602                                                 } else if (lang_init) {
          603                                                         msg = "Language already set";
          604                                                         goto error;
          605                                                 }
          606                                                 config->mappings[config->mappings_len - 1].lang = ltk_strndup(tok.text, tok.len);
          607                                                 lang_init = 1;
          608                                         } else if (str_array_equal("map", prev2tok.text, prev2tok.len)) {
          609                                                 if (prev1tok.type != STRING || tok.type != STRING) {
          610                                                         msg = "Invalid map statement";
          611                                                         goto error;
          612                                                 }
          613                                                 push_text_mapping(config, prev1tok.text, prev1tok.len, tok.text, tok.len);
          614                                         } else {
          615                                                 msg = "Invalid statement in language mapping";
          616                                                 goto error;
          617                                         }
          618                                         tok = next_token(&s);
          619                                         if (tok.type == END) {
          620                                                 break;
          621                                         } else if (tok.type != NEWLINE) {
          622                                                 msg = "Invalid statement in language mapping";
          623                                                 goto error;
          624                                         }
          625                                         start_of_line = 1;
          626                                 }
          627                                 if (!lang_init) {
          628                                         msg = "Language not set for language mapping";
          629                                         goto error;
          630                                 }
          631                         } else {
          632                                 msg = "Invalid section";
          633                                 goto error;
          634                         }
          635                         break;
          636                 case NEWLINE:
          637                         start_of_line = 1;
          638                         break;
          639                 default:
          640                         msg = "Invalid token";
          641                         goto error;
          642                         break;
          643                 }
          644         }
          645         global_config = config;
          646         return 0;
          647 error:
          648         if (msg) {
          649                 *errstr = ltk_print_fmt(
          650                     "%s, line %zu, offset %zu: %s", filename, tok.line, tok.line_offset, msg
          651                 );
          652         }
          653 errornomsg:
          654         destroy_config(config);
          655         return 1;
          656 }
          657 
          658 int
          659 ltk_config_parsefile(
          660     const char *filename,
          661     keypress_binding_handler press_handler,
          662     keyrelease_binding_handler release_handler,
          663     char **errstr) {
          664         unsigned long len = 0;
          665         char *ferrstr = NULL;
          666         char *file_contents = ltk_read_file(filename, &len, &ferrstr);
          667         if (!file_contents) {
          668                 *errstr = ltk_print_fmt("Unable to open file \"%s\": %s", filename, ferrstr);
          669                 return 1;
          670         }
          671         int ret = load_from_text(filename, file_contents, len, press_handler, release_handler, errstr);
          672         ltk_free(file_contents);
          673         return ret;
          674 }
          675 
          676 /* FIXME: update this */
          677 const char *default_config = "[general]\n"
          678 "explicit-focus = true\n"
          679 "all-activatable = true\n"
          680 "[key-binding:widget]\n"
          681 "bind-keypress move-next sym tab\n"
          682 "bind-keypress move-prev sym tab mods shift\n"
          683 "bind-keypress move-next text n\n"
          684 "bind-keypress move-prev text p\n"
          685 "bind-keypress focus-active sym return\n"
          686 "bind-keypress unfocus-active sym escape\n"
          687 "bind-keypress set-pressed sym return flags run-always\n"
          688 "bind-keyrelease unset-pressed sym return flags run-always\n"
          689 "[key-mapping]\n"
          690 "language = \"English (US)\"\n";
          691 
          692 /* FIXME: improve this configuration */
          693 int
          694 ltk_config_load_default(
          695     keypress_binding_handler press_handler,
          696     keyrelease_binding_handler release_handler,
          697     char **errstr) {
          698         char *config_copied = ltk_strdup(default_config);
          699         int ret = load_from_text("<default config>", config_copied, strlen(config_copied), press_handler, release_handler, errstr);
          700         free(config_copied);
          701         return ret;
          702 }
          703 
          704 void
          705 ltk_keypress_binding_destroy(ltk_keypress_binding b) {
          706         ltk_free(b.text);
          707         ltk_free(b.rawtext);
          708 }
          709 
          710 /* FIXME: which additional ones are needed here? */
          711 static struct keysym_mapping {
          712         char *name;
          713         ltk_keysym keysym;
          714 } keysym_map[] = {
          715         {"backspace", LTK_KEY_BACKSPACE},
          716         {"begin", LTK_KEY_BEGIN},
          717         {"break", LTK_KEY_BREAK},
          718         {"cancel", LTK_KEY_CANCEL},
          719         {"clear", LTK_KEY_CLEAR},
          720         {"delete", LTK_KEY_DELETE},
          721         {"down", LTK_KEY_DOWN},
          722         {"end", LTK_KEY_END},
          723         {"escape", LTK_KEY_ESCAPE},
          724         {"execute", LTK_KEY_EXECUTE},
          725 
          726         {"f1", LTK_KEY_F1},
          727         {"f10", LTK_KEY_F10},
          728         {"f11", LTK_KEY_F11},
          729         {"f12", LTK_KEY_F12},
          730         {"f2", LTK_KEY_F2},
          731         {"f3", LTK_KEY_F3},
          732         {"f4", LTK_KEY_F4},
          733         {"f5", LTK_KEY_F5},
          734         {"f6", LTK_KEY_F6},
          735         {"f7", LTK_KEY_F7},
          736         {"f8", LTK_KEY_F8},
          737         {"f9", LTK_KEY_F9},
          738 
          739         {"find", LTK_KEY_FIND},
          740         {"help", LTK_KEY_HELP},
          741         {"home", LTK_KEY_HOME},
          742         {"insert", LTK_KEY_INSERT},
          743 
          744         {"kp-0", LTK_KEY_KP_0},
          745         {"kp-1", LTK_KEY_KP_1},
          746         {"kp-2", LTK_KEY_KP_2},
          747         {"kp-3", LTK_KEY_KP_3},
          748         {"kp-4", LTK_KEY_KP_4},
          749         {"kp-5", LTK_KEY_KP_5},
          750         {"kp-6", LTK_KEY_KP_6},
          751         {"kp-7", LTK_KEY_KP_7},
          752         {"kp-8", LTK_KEY_KP_8},
          753         {"kp-9", LTK_KEY_KP_9},
          754         {"kp-add", LTK_KEY_KP_ADD},
          755         {"kp-begin", LTK_KEY_KP_BEGIN},
          756         {"kp-decimal", LTK_KEY_KP_DECIMAL},
          757         {"kp-delete", LTK_KEY_KP_DELETE},
          758         {"kp-divide", LTK_KEY_KP_DIVIDE},
          759         {"kp-down", LTK_KEY_KP_DOWN},
          760         {"kp-end", LTK_KEY_KP_END},
          761         {"kp-enter", LTK_KEY_KP_ENTER},
          762         {"kp-equal", LTK_KEY_KP_EQUAL},
          763         {"kp-home", LTK_KEY_KP_HOME},
          764         {"kp-insert", LTK_KEY_KP_INSERT},
          765         {"kp-left", LTK_KEY_KP_LEFT},
          766         {"kp-multiply", LTK_KEY_KP_MULTIPLY},
          767         {"kp-next", LTK_KEY_KP_NEXT},
          768         {"kp-page-down", LTK_KEY_KP_PAGE_DOWN},
          769         {"kp-page-up", LTK_KEY_KP_PAGE_UP},
          770         {"kp-prior", LTK_KEY_KP_PRIOR},
          771         {"kp-right", LTK_KEY_KP_RIGHT},
          772         {"kp-separator", LTK_KEY_KP_SEPARATOR},
          773         {"kp-space", LTK_KEY_KP_SPACE},
          774         {"kp-subtract", LTK_KEY_KP_SUBTRACT},
          775         {"kp-tab", LTK_KEY_KP_TAB},
          776         {"kp-up", LTK_KEY_KP_UP},
          777 
          778         {"left", LTK_KEY_LEFT},
          779         {"linefeed", LTK_KEY_LINEFEED},
          780         {"menu", LTK_KEY_MENU},
          781         {"mode-switch", LTK_KEY_MODE_SWITCH},
          782         {"next", LTK_KEY_NEXT},
          783         {"num-lock", LTK_KEY_NUM_LOCK},
          784         {"page-down", LTK_KEY_PAGE_DOWN},
          785         {"page-up", LTK_KEY_PAGE_UP},
          786         {"pause", LTK_KEY_PAUSE},
          787         {"print", LTK_KEY_PRINT},
          788         {"prior", LTK_KEY_PRIOR},
          789 
          790         {"redo", LTK_KEY_REDO},
          791         {"return", LTK_KEY_RETURN},
          792         {"right", LTK_KEY_RIGHT},
          793         {"script-switch", LTK_KEY_SCRIPT_SWITCH},
          794         {"scroll-lock", LTK_KEY_SCROLL_LOCK},
          795         {"select", LTK_KEY_SELECT},
          796         {"space", LTK_KEY_SPACE},
          797         {"sysreq", LTK_KEY_SYS_REQ},
          798         {"tab", LTK_KEY_TAB},
          799         {"up", LTK_KEY_UP},
          800         {"undo", LTK_KEY_UNDO},
          801 };
          802 
          803 GEN_CB_MAP_HELPERS(keysym_map, struct keysym_mapping, name)
          804 
          805 static int
          806 parse_keysym(char *keysym_str, size_t len, ltk_keysym *sym) {
          807         struct keysym_mapping *km = keysym_map_get_entry(keysym_str, len);
          808         if (!km)
          809                 return 1;
          810         *sym = km->keysym;
          811         return 0;
          812 }