diff options
Diffstat (limited to 'src/parser.c')
-rw-r--r-- | src/parser.c | 508 |
1 files changed, 282 insertions, 226 deletions
diff --git a/src/parser.c b/src/parser.c index d2efba7..7cf7b18 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1,267 +1,323 @@ -/* See LICENSE for more information on use */ -#define _POSIX_C_SOURCE 200809L /* for strdup */ +#define _POSIX_C_SOURCE 200809L #include <ctype.h> -#include <linux/limits.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - #include <X11/keysym.h> -#include <X11/keysymdef.h> #include "parser.h" -#define MAX_LINE 512 +#include "defs.h" -static const struct { const char *name; void (*fn)(void); } call_table[] = { - { "close_window", close_focused }, - { "decrease_gaps", dec_gaps }, - { "focus_next", focus_next }, - { "focus_previous", focus_prev }, - { "increase_gaps", inc_gaps }, - { "master_next", move_master_next }, - { "master_previous", move_master_prev }, - { "quit", quit }, - { "reload_config", reload_config }, - { "master_increase", resize_master_add }, - { "master_decrease", resize_master_sub }, - { "toggle_floating", toggle_floating }, - { "global_floating", toggle_floating_global }, - { "fullscreen", toggle_fullscreen }, - { NULL, NULL } -}; +static const struct { + const char *name; + void (*fn)(void); +} call_table[] = {{"close_window", close_focused}, + {"decrease_gaps", dec_gaps}, + {"focus_next", focus_next}, + {"focus_prev", focus_prev}, + {"increase_gaps", inc_gaps}, + {"master_next", move_master_next}, + {"master_previous", move_master_prev}, + {"quit", quit}, + {"reload_config", reload_config}, + {"master_increase", resize_master_add}, + {"master_decrease", resize_master_sub}, + {"toggle_floating", toggle_floating}, + {"global_floating", toggle_floating_global}, + {"fullscreen", toggle_fullscreen}, + {NULL, NULL}}; -/* helpers */ -static char* -strip(char *s) +static void remap_and_dedupe_binds(Config *cfg) { - while (isspace((unsigned char)*s)) s++; - if (!*s) return s; - char *end = s + strlen(s) - 1; - while (end > s && isspace((unsigned char)*end)) *end-- = '\0'; - return s; + for (int i = 0; i < cfg->bindsn; i++) { + Binding *b = &cfg->binds[i]; + if (b->mods & (Mod1Mask | Mod4Mask)) { + unsigned others = b->mods & ~(Mod1Mask | Mod4Mask); + b->mods = others | cfg->modkey; + } + } + + for (int i = 0; i < cfg->bindsn; i++) { + for (int j = i + 1; j < cfg->bindsn; j++) { + if (cfg->binds[i].mods == cfg->binds[j].mods && cfg->binds[i].keysym == cfg->binds[j].keysym) { + memmove(&cfg->binds[j], &cfg->binds[j + 1], sizeof(Binding) * (cfg->bindsn - j - 1)); + cfg->bindsn--; + j--; + } + } + } } -static char* -strip_quotes(char *s) +static char *strip(char *s) { - size_t len = strlen(s); - if (len > 0 && s[0] == '"') { - s++; - len--; - } - if (len > 0 && s[len-1] == '"') { - s[len-1] = '\0'; - } - return s; + while (*s && isspace((unsigned char)*s)) { + s++; + } + char *e = s + strlen(s) - 1; + while (e > s && isspace((unsigned char)*e)) { + *e-- = '\0'; + } + return s; } -static const char** build_argv(char *cmdline) +static char *strip_quotes(char *s) { - char **argv = calloc(MAX_ARGS + 1, sizeof(char *)); - if (!argv) return NULL; - - int argc = 0; - char *tok = strtok(cmdline, " \t"); - while (tok && argc < MAX_ARGS) { - argv[argc++] = strdup(tok); - tok = strtok(NULL, " \t"); - } - argv[argc] = NULL; - return (const char **)argv; + size_t L = strlen(s); + if (L > 0 && s[0] == '"') { + s++; + L--; + } + if (L > 0 && s[L - 1] == '"') { + s[L - 1] = '\0'; + } + return s; } -unsigned int -parse_mods(const char *mods, Config *user_config) +static Binding *alloc_bind(Config *cfg, unsigned mods, KeySym ks) { - unsigned int m = 0; - char buf[MAX_LINE]; - strncpy(buf, mods, sizeof(buf)-1); - buf[sizeof(buf)-1] = '\0'; - - for (char *tok = strtok(buf, "+"); tok; tok = strtok(NULL, "+")) { - for (char *p = tok; *p; p++) *p = tolower((unsigned char)*p); - if (strcmp(tok, "mod") == 0) m |= user_config->modkey; - else if (strcmp(tok, "shift") == 0) m |= ShiftMask; - else if (strcmp(tok, "ctrl") == 0) m |= ControlMask; - else if (strcmp(tok, "alt") == 0) m |= Mod1Mask; - else if (strcmp(tok, "super") == 0) m |= Mod4Mask; - } - return m; + for (int i = 0; i < cfg->bindsn; i++) { + if (cfg->binds[i].mods == (int)mods && cfg->binds[i].keysym == ks) { + return &cfg->binds[i]; + } + } + if (cfg->bindsn >= 256) { + return NULL; + } + Binding *b = &cfg->binds[cfg->bindsn++]; + b->mods = mods; + b->keysym = ks; + return b; } -KeySym -parse_keysym(const char *key) +static unsigned parse_combo(const char *combo, Config *cfg, KeySym *out_ks) { - char buf[64]; - size_t len = strlen(key); - if (len >= sizeof(buf)) len = sizeof(buf) - 1; - - if (len == 1) { - buf[0] = key[0]; - buf[1] = '\0'; - } else { - buf[0] = toupper((unsigned char)key[0]); - for (size_t i = 1; i < len; i++) - buf[i] = tolower((unsigned char)key[i]); - buf[len] = '\0'; - } - - KeySym ks = XStringToKeysym(buf); - if (ks == NoSymbol) { - for (size_t i = 0; i < len; i++) - buf[i] = toupper((unsigned char)key[i]); - buf[len] = '\0'; - ks = XStringToKeysym(buf); - } - if (ks == NoSymbol) - fprintf(stderr, "sxwmrc: unknown keysym '%s'\n", key); - return ks; + unsigned m = 0; + KeySym ks = NoSymbol; + char buf[256]; + strncpy(buf, combo, sizeof buf - 1); + for (char *p = buf; *p; p++) { + if (*p == '+' || isspace((unsigned char)*p)) { + *p = '+'; + } + } + buf[sizeof buf - 1] = '\0'; + for (char *tok = strtok(buf, "+"); tok; tok = strtok(NULL, "+")) { + for (char *q = tok; *q; q++) { + *q = tolower((unsigned char)*q); + } + if (!strcmp(tok, "mod")) { + m |= cfg->modkey; + } else if (!strcmp(tok, "shift")) { + m |= ShiftMask; + } else if (!strcmp(tok, "ctrl")) { + m |= ControlMask; + } else if (!strcmp(tok, "alt")) { + m |= Mod1Mask; + } else if (!strcmp(tok, "super")) { + m |= Mod4Mask; + } else { + ks = parse_keysym(tok); + } + } + *out_ks = ks; + return m; } -void -handler(char *command, char *arg, int mods, KeySym keysym, Action action, - Bool is_func, Config *user_config) +int parser(Config *cfg) { - if (strcmp(command, "bind") == 0) { - /* check if binding already exists */ - int slot = user_config->bindsn; - for (int i = 0; i < user_config->bindsn; i++) { - if (user_config->binds[i].mods == mods && - user_config->binds[i].keysym == keysym) { - slot = i; - break; - } - } + char path[PATH_MAX]; + const char *home = getenv("HOME"); + if (!home) { + fputs("sxwmrc: HOME not set\n", stderr); + return -1; + } + snprintf(path, sizeof path, "%s/.config/sxwmrc", home); + FILE *f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "sxwmrc: cannot open %s\n", path); + return -1; + } - if (slot == user_config->bindsn) - user_config->bindsn++; + char line[512]; + int lineno = 0; + while (fgets(line, sizeof line, f)) { + lineno++; + char *s = strip(line); + if (!*s || *s == '#') { + continue; + } - Binding *b = &user_config->binds[slot]; - b->mods = mods; - b->keysym = keysym; - b->is_func = is_func; + char *sep = strchr(s, ':'); + if (!sep) { + fprintf(stderr, "sxwmrc:%d: missing ':'\n", lineno); + continue; + } + *sep = '\0'; + char *key = strip(s); + char *rest = strip(sep + 1); - if (is_func) { - for (size_t j = 0; call_table[j].name; j++) { - if (strcmp(arg, call_table[j].name) == 0) { - b->action.fn = call_table[j].fn; - break; - } - } - } else { - b->action.cmd = action.cmd; - } - return; - } + if (!strcmp(key, "mod_key")) { + unsigned m = parse_mods(rest, cfg); + if (m & (Mod1Mask | Mod4Mask)) { + cfg->modkey = m; + } else { + fprintf(stderr, "sxwmrc:%d: unknown mod_key '%s'\n", lineno, rest); + } + } else if (!strcmp(key, "gaps")) { + cfg->gaps = atoi(rest); + } else if (!strcmp(key, "border_width")) { + cfg->border_width = atoi(rest); + } else if (!strcmp(key, "focused_border_colour")) { + cfg->border_foc_col = parse_col(rest); + } else if (!strcmp(key, "unfocused_border_colour")) { + cfg->border_ufoc_col = parse_col(rest); + } else if (!strcmp(key, "swap_border_colour")) { + cfg->border_swap_col = parse_col(rest); + } else if (!strcmp(key, "master_width")) { + cfg->master_width = atoi(rest) / 100.0f; + } else if (!strcmp(key, "motion_throttle")) { + cfg->motion_throttle = atoi(rest); + } else if (!strcmp(key, "resize_master_amount")) { + cfg->resize_master_amt = atoi(rest); + } else if (!strcmp(key, "snap_distance")) { + cfg->snap_distance = atoi(rest); + } else if (!strcmp(key, "call") || !strcmp(key, "bind")) { + char *mid = strchr(rest, ':'); + if (!mid) { + fprintf(stderr, "sxwmrc:%d: '%s' missing action\n", lineno, key); + continue; + } + *mid = '\0'; + char *combo = strip(rest); + char *act = strip(mid + 1); - if (strcmp(command, "gaps") == 0) user_config->gaps = atoi(arg); - else if (strcmp(command, "border_width") == 0) user_config->border_width = atoi(arg); - else if (strcmp(command, "focused_border_colour") == 0) user_config->border_foc_col = parse_col(arg); - else if (strcmp(command, "unfocused_border_colour") == 0) user_config->border_ufoc_col = parse_col(arg); - else if (strcmp(command, "swap_border_colour") == 0) user_config->border_swap_col = parse_col(arg); - else if (strcmp(command, "master_width") == 0) user_config->master_width = atoi(arg) / 100.0f; - else if (strcmp(command, "motion_throttle") == 0) user_config->motion_throttle = atoi(arg); - else if (strcmp(command, "resize_master_amount") == 0) user_config->resize_master_amt = atoi(arg); - else if (strcmp(command, "snap_distance") == 0) user_config->snap_distance = atoi(arg); - else if (strcmp(command, "mod_key") == 0) user_config->modkey = parse_mods(arg, user_config); - else - fprintf(stderr, "sxwmrc: unknown setting '%s'\n", command); -} -int -parser(Config *user_config) -{ - char *home = getenv("HOME"); - if (!home) { - fputs("sxwmrc: HOME not set\n", stderr); - return -1; - } + KeySym ks; + unsigned mods = parse_combo(combo, cfg, &ks); + if (ks == NoSymbol) { + fprintf(stderr, "sxwmrc:%d: bad key in '%s'\n", lineno, combo); + continue; + } + Binding *b = alloc_bind(cfg, mods, ks); + if (!b) { + fputs("sxwm: too many binds\n", stderr); + break; + } - char path[PATH_MAX]; - snprintf(path, sizeof(path), "%s/.config/sxwmrc", home); - FILE *f = fopen(path, "r"); - if (!f) { - fprintf(stderr, "sxwmrc: cannot open '%s'\n", path); - return -1; - } + if (*act == '"' && !strcmp(key, "bind")) { + b->type = TYPE_CMD; + b->action.cmd = build_argv(strip_quotes(act)); + } else { + b->type = TYPE_FUNC; + Bool found = False; + for (int i = 0; call_table[i].name; i++) { + if (!strcmp(act, call_table[i].name)) { + b->action.fn = call_table[i].fn; + found = True; + break; + } + } + if (!found) { + fprintf(stderr, "sxwmrc:%d: unknown function '%s'\n", lineno, act); + } + } + } else if (!strcmp(key, "workspace")) { + char *mid = strchr(rest, ':'); + if (!mid) { + fprintf(stderr, "sxwmrc:%d: workspace missing action\n", lineno); + continue; + } + *mid = '\0'; + char *combo = strip(rest); + char *act = strip(mid + 1); - char line[MAX_LINE]; - int lineno = 0; - while (fgets(line, sizeof(line), f)) { - lineno++; - char *s = strip(line); - if (!*s || *s == '#') continue; + KeySym ks; + unsigned mods = parse_combo(combo, cfg, &ks); + if (ks == NoSymbol) { + fprintf(stderr, "sxwmrc:%d: bad key in '%s'\n", lineno, combo); + continue; + } + Binding *b = alloc_bind(cfg, mods, ks); + if (!b) { + fputs("sxwm: too many binds\n", stderr); + break; + } - /* split key : value */ - char *sep = strchr(s, ':'); - if (!sep) { - fprintf(stderr, "sxwmrc:%d: missing ':'\n", lineno); - continue; - } - *sep = '\0'; - char *key = strip(s); - char *val = strip(sep + 1); + int n; + if (sscanf(act, "move %d", &n) == 1 && n >= 1 && n <= NUM_WORKSPACES) { + b->type = TYPE_CWKSP; + b->action.ws = n - 1; + } else if (sscanf(act, "swap %d", &n) == 1 && n >= 1 && n <= NUM_WORKSPACES) { + b->type = TYPE_MWKSP; + b->action.ws = n - 1; + } else { + fprintf(stderr, "sxwmrc:%d: invalid workspace action '%s'\n", lineno, act); + } + } else { + fprintf(stderr, "sxwmrc:%d: unknown option '%s'\n", lineno, key); + } + } - if (strcmp(key, "bind") == 0) { - /* bind parsing */ - char *mid = strchr(val, ':'); - if (!mid) { - fprintf(stderr, "sxwmrc:%d: bind missing action\n", lineno); - continue; - } - *mid = '\0'; - char *combo = strip(val); - char *act = strip(mid + 1); + fclose(f); + remap_and_dedupe_binds(cfg); + return 0; +} - if (*combo == '[') { - combo++; - } - size_t L = strlen(combo); - if (L && combo[L-1] == ']') { - combo[L-1] = '\0'; - } +int parse_mods(const char *mods, Config *cfg) +{ + KeySym dummy; + return parse_combo(mods, cfg, &dummy); +} - /* parse mods & key */ - unsigned int mods = 0; - char key_part[64] = {0}; - char tokbuf[MAX_LINE]; - strncpy(tokbuf, combo, sizeof(tokbuf)-1); - tokbuf[sizeof(tokbuf)-1] = '\0'; +KeySym parse_keysym(const char *key) +{ + KeySym ks = XStringToKeysym(key); + if (ks != NoSymbol) { + return ks; + } - for (char *tok = strtok(tokbuf, "+"); tok; tok = strtok(NULL, "+")) { - char *p = strip(tok); - for (char *c = p; *c; c++) *c = tolower((unsigned char)*c); - if (strcmp(p, "mod") == 0) mods |= user_config->modkey; - else if (strcmp(p, "shift") == 0) mods |= ShiftMask; - else if (strcmp(p, "ctrl") == 0) mods |= ControlMask; - else if (strcmp(p, "alt") == 0) mods |= Mod1Mask; - else if (strcmp(p, "super") == 0) mods |= Mod4Mask; - else strncpy(key_part, p, sizeof(key_part)-1); - } + char buf[64]; + size_t n = strlen(key); + if (n >= sizeof buf) { + n = sizeof buf - 1; + } - KeySym ks = parse_keysym(key_part); - if (!ks) continue; + buf[0] = toupper((unsigned char)key[0]); + for (size_t i = 1; i < n; i++) { + buf[i] = tolower((unsigned char)key[i]); + } + buf[n] = '\0'; + ks = XStringToKeysym(buf); + if (ks != NoSymbol) { + return ks; + } - Action a = { .cmd = NULL }; - Bool is_fn = False; - if (*act == '"') { - char act_buf[MAX_LINE]; - strncpy(act_buf, strip_quotes(act), sizeof(act_buf)-1); - act_buf[sizeof(act_buf)-1] = '\0'; - a.cmd = build_argv(act_buf); - fprintf(stderr, "[DEBUG parser] Parsed bind: mods=0x%x, keysym=0x%lx, cmd='%s'\n", mods, ks, act); - } else { - is_fn = True; - fprintf(stderr, "[DEBUG parser] Parsed bind: mods=0x%x, keysym=0x%lx, func='%s'\n", mods, ks, act); - } + for (size_t i = 0; i < n; i++) { + buf[i] = toupper((unsigned char)key[i]); + } + buf[n] = '\0'; + ks = XStringToKeysym(buf); + if (ks != NoSymbol) { + return ks; + } - handler("bind", act, mods, ks, a, is_fn, user_config); + fprintf(stderr, "sxwmrc: unknown keysym '%s'\n", key); + return NoSymbol; +} - } else { - /* normal settings */ - handler(key, val, 0, 0, (Action){ .cmd = NULL }, False, user_config); - } - } +const char **build_argv(const char *cmd) +{ + char *dup = strdup(cmd); + char *saveptr, *tok; + const char **argv = malloc(MAX_ARGS * sizeof *argv); + int i = 0; - fclose(f); - return 0; -} + for (tok = strtok_r(dup, " \t", &saveptr); tok && i < MAX_ARGS - 1; tok = strtok_r(NULL, " \t", &saveptr)) { + if (*tok) { + argv[i++] = strdup(tok); + } + } + argv[i] = NULL; + free(dup); + return argv; +}
\ No newline at end of file |