summaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c508
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