summaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c939
1 files changed, 376 insertions, 563 deletions
diff --git a/src/parser.c b/src/parser.c
index 4c1ae24..36df589 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -9,55 +9,106 @@
#include <X11/XF86keysym.h>
#include <X11/Xlib.h>
-#ifdef __linux__
-#include <wordexp.h>
-#endif
-
-#include "parser.h"
-#include "extern.h"
#include "defs.h"
+#include "extern.h"
+#include "parser.h"
+
+static Binding *alloc_bind(Config *cfg, unsigned mods, KeySym ks);
+static char **alloc_str_pair(const char *a, const char *b);
+static void dedupe_binds(Config *cfg);
+static int find_free_slot(char **arr[], int max);
+static FILE *open_config(char *path, size_t pathsz);
+static Binding *parse_bind_line(Config *cfg, char *rest, int lineno, const char *ctx, char **out_act);
+static unsigned parse_combo(const char *combo, Config *cfg, KeySym *out_ks);
+static int parse_csv_to_array(char *rest, char **arr[], int *idx, int max, int alloc_pair);
+static char **split_cmd(const char *cmd, int *out_argc);
+static char *strip(char *s);
+static char *strip_comment(char *s);
+static char *strip_quotes(char *s);
static const CommandEntry call_table[] = {
- {"centre_window", centre_window},
- {"close_window", close_focused},
- {"decrease_gaps", dec_gaps},
- {"focus_next", focus_next},
- {"focus_prev", focus_prev},
- {"focus_next_mon", focus_next_mon},
- {"focus_prev_mon", focus_prev_mon},
- {"fullscreen", toggle_fullscreen},
- {"global_floating", toggle_floating_global},
- {"increase_gaps", inc_gaps},
- {"master_next", move_master_next},
- {"master_prev", move_master_prev},
- {"master_increase", resize_master_add},
- {"master_decrease", resize_master_sub},
- {"move_next_mon", move_next_mon},
- {"move_prev_mon", move_prev_mon},
- {"move_win_up", move_win_up},
- {"move_win_down", move_win_down},
- {"move_win_left", move_win_left},
- {"move_win_right", move_win_right},
- {"quit", quit},
- {"reload_config", reload_config},
- {"resize_win_up", resize_win_up},
- {"resize_win_down", resize_win_down},
- {"resize_win_left", resize_win_left},
- {"resize_win_right", resize_win_right},
- {"stack_increase", resize_stack_add},
- {"stack_decrease", resize_stack_sub},
+ {"centre_window", centre_window},
+ {"close_window", close_focused},
+ {"decrease_gaps", dec_gaps},
+ {"focus_next", focus_next},
+ {"focus_prev", focus_prev},
+ {"focus_next_mon", focus_next_mon},
+ {"focus_prev_mon", focus_prev_mon},
+ {"fullscreen", toggle_fullscreen},
+ {"global_floating", toggle_floating_global},
+ {"increase_gaps", inc_gaps},
+ {"master_next", move_master_next},
+ {"master_prev", move_master_prev},
+ {"master_increase", resize_master_add},
+ {"master_decrease", resize_master_sub},
+ {"move_next_mon", move_next_mon},
+ {"move_prev_mon", move_prev_mon},
+ {"move_win_up", move_win_up},
+ {"move_win_down", move_win_down},
+ {"move_win_left", move_win_left},
+ {"move_win_right", move_win_right},
+ {"quit", quit},
+ {"reload_config", reload_config},
+ {"resize_win_up", resize_win_up},
+ {"resize_win_down", resize_win_down},
+ {"resize_win_left", resize_win_left},
+ {"resize_win_right", resize_win_right},
+ {"stack_increase", resize_stack_add},
+ {"stack_decrease", resize_stack_sub},
{"switch_previous_workspace", switch_previous_workspace},
- {"toggle_floating", toggle_floating},
- {"toggle_monocle", toggle_monocle},
+ {"toggle_floating", toggle_floating},
+ {"toggle_monocle", toggle_monocle},
{NULL, NULL},
};
-static void remap_and_dedupe_binds(Config *cfg)
+static Binding *alloc_bind(Config *cfg, unsigned mods, KeySym ks)
+{
+ for (int i = 0; i < cfg->n_binds; i++) {
+ if (cfg->binds[i].mods == (int)mods && cfg->binds[i].keysym == ks)
+ return &cfg->binds[i];
+ }
+
+ if (cfg->n_binds >= MAX_BINDS)
+ return NULL;
+
+ Binding *b = &cfg->binds[cfg->n_binds++];
+ b->mods = mods;
+ b->keysym = ks;
+ return b;
+}
+
+static char **alloc_str_pair(const char *a, const char *b)
+{
+ char **p = malloc(2 * sizeof(char *));
+ if (!p)
+ return NULL;
+
+ p[0] = a ? strdup(a) : NULL;
+ p[1] = b ? strdup(b) : NULL;
+ return p;
+}
+
+const char **build_argv(const char *cmd)
+{
+ int argc = 0;
+ char **tmp = split_cmd(cmd, &argc);
+ if (!tmp)
+ return NULL;
+
+ return (const char **)tmp;
+}
+
+static void dedupe_binds(Config *cfg)
{
for (int i = 0; i < cfg->n_binds; i++) {
for (int j = i + 1; j < cfg->n_binds; 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->n_binds - j - 1));
+ Bool dup = cfg->binds[i].mods == cfg->binds[j].mods &&
+ cfg->binds[i].keysym == cfg->binds[j].keysym;
+ if (dup) {
+ memmove(
+ &cfg->binds[j], &cfg->binds[j + 1],
+ sizeof(Binding) * (cfg->n_binds - j - 1)
+ );
cfg->n_binds--;
j--;
}
@@ -65,53 +116,82 @@ static void remap_and_dedupe_binds(Config *cfg)
}
}
-static char *strip(char *s)
+static int find_free_slot(char **arr[], int max)
{
- while (*s && isspace((unsigned char)*s)) {
- s++;
- }
- if (*s == '\0') {
- return s;
+ for (int i = 0; i < max; i++) {
+ if (!arr[i])
+ return i;
}
- char *e = s + strlen(s) - 1;
- while (e > s && isspace((unsigned char)*e)) {
- *e-- = '\0';
- }
- return s;
+ return -1;
}
-static char *strip_quotes(char *s)
+static FILE *open_config(char *path, size_t pathsz)
{
- size_t L = strlen(s);
- if (L > 0 && s[0] == '"') {
- s++;
- L--;
+ const char *home = getenv("HOME");
+ if (!home) {
+ fputs("sxwmrc: HOME not set\n", stderr);
+ return NULL;
}
- if (L > 0 && s[L - 1] == '"') {
- s[L - 1] = '\0';
+
+ const char *xdg = getenv("XDG_CONFIG_HOME");
+ const char *paths[] = {
+ "%s/sxwmrc",
+ "%s/sxwm/sxwmrc",
+ };
+
+ if (xdg) {
+ for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
+ snprintf(path, pathsz, paths[i], xdg);
+ if (access(path, R_OK) == 0)
+ goto found;
+ }
}
- return s;
+
+ snprintf(path, pathsz, "%s/.config/sxwmrc", home);
+ if (access(path, R_OK) == 0)
+ goto found;
+
+ snprintf(path, pathsz, "/usr/local/share/sxwmrc");
+ if (access(path, R_OK) == 0)
+ goto found;
+
+ fprintf(stderr, "sxwmrc: no configuration file found\n");
+ return NULL;
+
+found:
+ printf("sxwmrc: using configuration file %s\n", path);
+ FILE *f = fopen(path, "r");
+ if (!f)
+ fprintf(stderr, "sxwmrc: cannot open %s\n", path);
+
+ return f;
}
-static Binding *alloc_bind(Config *cfg, unsigned mods, KeySym ks)
+static Binding *parse_bind_line(Config *cfg, char *rest, int lineno, const char *ctx, char **out_act)
{
- for (int i = 0; i < cfg->n_binds; i++) {
- if (cfg->binds[i].mods == (int)mods && cfg->binds[i].keysym == ks) {
- return &cfg->binds[i];
- }
+ char *mid = strchr(rest, ':');
+ if (!mid) {
+ fprintf(stderr, "sxwmrc:%d: %s missing action\n", lineno, ctx);
+ return NULL;
}
- if (cfg->n_binds >= MAX_BINDS) {
+ *mid = '\0';
+ *out_act = strip(mid + 1);
+
+ KeySym ks;
+ unsigned mods = parse_combo(strip(rest), cfg, &ks);
+ if (ks == NoSymbol) {
+ fprintf(stderr, "sxwmrc:%d: bad key in '%s'\n", lineno, rest);
return NULL;
}
- Binding *b = &cfg->binds[cfg->n_binds++];
- b->mods = mods;
- b->keysym = ks;
+
+ Binding *b = alloc_bind(cfg, mods, ks);
+ if (!b)
+ fputs("sxwm: too many binds\n", stderr);
+
return b;
}
-KeySym parse_keysym(const char *tok);
-
static unsigned parse_combo(const char *combo, Config *cfg, KeySym *out_ks)
{
unsigned m = 0;
@@ -122,235 +202,125 @@ static unsigned parse_combo(const char *combo, Config *cfg, KeySym *out_ks)
buf[sizeof(buf) - 1] = '\0';
for (char *p = buf; *p; p++) {
- if (*p == '+' || isspace((unsigned char)*p)) {
+ if (*p == '+' || isspace((unsigned char)*p))
*p = '+';
- }
}
for (char *tok = strtok(buf, "+"); tok; tok = strtok(NULL, "+")) {
- 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;
- }
+ 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 = XStringToKeysym(tok);
- if (ks == NoSymbol) {
+ if (ks == NoSymbol)
ks = parse_keysym(tok);
- }
}
}
-
*out_ks = ks;
return m;
}
-int parser(Config *cfg)
+static int parse_csv_to_array(char *rest, char **arr[], int *idx, int max, int alloc_pair)
{
- char path[PATH_MAX];
- const char *home = getenv("HOME");
- if (!home) {
- fputs("sxwmrc: HOME not set\n", stderr);
- return -1;
- }
+ char *save, *tok;
+ for (tok = strtok_r(rest, ",", &save); tok && *idx < max; tok = strtok_r(NULL, ",", &save)) {
+ char *item = strip_quotes(strip(tok));
+ if (!*item)
+ continue;
- /* determine config file path */
- const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
- if (xdg_config_home) {
- snprintf(path, sizeof path, "%s/sxwmrc", xdg_config_home);
- if (access(path, R_OK) == 0) {
- goto found;
+ if (alloc_pair) {
+ arr[*idx] = alloc_str_pair(item, NULL);
+ if (!arr[*idx])
+ return -1;
}
-
- snprintf(path, sizeof path, "%s/sxwm/sxwmrc", xdg_config_home);
- if (access(path, R_OK) == 0) {
- goto found;
+ else {
+ if (!arr[*idx])
+ arr[*idx] = calloc(2, sizeof(char *));
+ if (!arr[*idx])
+ return -1;
+ arr[*idx][0] = strdup(item);
}
+ (*idx)++;
}
+ return 0;
+}
- snprintf(path, sizeof path, "%s/.config/sxwmrc", home);
- if (access(path, R_OK) == 0) {
- goto found;
- }
+KeySym parse_keysym(const char *key)
+{
+ KeySym ks = XStringToKeysym(key);
+ if (ks != NoSymbol)
+ return ks;
- snprintf(path, sizeof path, "/usr/local/share/sxwmrc");
- if (access(path, R_OK) == 0) {
- goto found;
- }
+ char buf[64];
+ size_t n = strlen(key);
+ if (n >= sizeof buf)
+ n = sizeof buf - 1;
- /* Nothing found */
- fprintf(stderr, "sxwmrc: no configuration file found\n");
- return -1;
+ /* try Capitalized */
+ 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';
+ if ((ks = XStringToKeysym(buf)) != NoSymbol)
+ return ks;
-found:
- printf("sxwmrc: using configuration file %s\n", path);
- FILE *f = fopen(path, "r");
- if (!f) {
- fprintf(stderr, "sxwmrc: cannot open %s\n", path);
+ /* try UPPERCASE */
+ for (size_t i = 0; i < n; i++)
+ buf[i] = toupper((unsigned char)key[i]);
+ buf[n] = '\0';
+ if ((ks = XStringToKeysym(buf)) != NoSymbol)
+ return ks;
+
+ fprintf(stderr, "sxwmrc: unknown keysym '%s'\n", key);
+ return NoSymbol;
+}
+
+int parse_mods(const char *mods, Config *cfg)
+{
+ KeySym dummy;
+ return parse_combo(mods, cfg, &dummy);
+}
+
+int parser(Config *cfg)
+{
+ char path[PATH_MAX];
+ FILE *f = open_config(path, sizeof(path));
+ if (!f)
return -1;
- }
char line[512];
- int lineno = 0;
- int should_floatn = 0;
- int to_run = 0;
+ int lineno = 0, should_floatn = 0, to_run = 0;
- /* Initialize should_float matrix */
for (int j = 0; j < MAX_ITEMS; j++) {
cfg->should_float[j] = calloc(2, sizeof(char *));
- if (!cfg->should_float[j]) {
- goto cleanup_file;
- }
+ if (!cfg->should_float[j])
+ goto cleanup;
}
while (fgets(line, sizeof line, f)) {
lineno++;
char *s = strip(line);
- if (!*s || *s == '#') {
+ if (!*s || *s == '#')
continue;
- }
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 (!strcmp(key, "mod_key")) {
- unsigned m = parse_mods(rest, cfg);
- if (m & (Mod1Mask | Mod4Mask | ShiftMask | ControlMask)) {
- 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")) {
+ 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, "new_win_focus")) {
- cfg->new_win_focus = !strcmp(rest, "true") ? True : False;
- }
- else if (!strcmp(key, "warp_cursor")) {
- cfg->warp_cursor = !strcmp(rest, "true") ? True : False;
- }
- else if (!strcmp(key, "master_width")) {
- float mf = (float)atoi(rest) / 100.0f;
- for (int i = 0; i < MAX_MONITORS; i++) {
- cfg->master_width[i] = mf;
- }
- }
- 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, "floating_on_top")) {
- cfg->floating_on_top = !strcmp(rest, "true") ? True : False;
- }
- else if (!strcmp(key, "resize_stack_amount")) {
- cfg->resize_stack_amt = atoi(rest);
- }
- else if (!strcmp(key, "move_window_amount")) {
- cfg->move_window_amt = atoi(rest);
- }
- else if (!strcmp(key, "resize_window_amount")) {
- cfg->resize_window_amt = atoi(rest);
- }
- else if (!strcmp(key, "snap_distance")) {
- cfg->snap_distance = atoi(rest);
- }
- else if (!strcmp(key, "should_float")) {
- if (should_floatn >= MAX_ITEMS) {
- fprintf(stderr, "sxwmrc:%d: too many should_float entries\n", lineno);
- continue;
- }
-
- char *comment = strchr(rest, '#');
- size_t len = comment ? (size_t)(comment - rest) : strlen(rest);
- if (len >= sizeof(line)) {
- len = sizeof(line) - 1;
- }
-
- char win[sizeof(line)];
- snprintf(win, sizeof(win), "%.*s", (int)len, rest);
-
- char *final = strip(win);
- char *comma_ptr;
- char *comma = strtok_r(final, ",", &comma_ptr);
-
- /* store each comma separated value in a seperate row */
- while (comma && should_floatn < MAX_ITEMS) {
- comma = strip(comma);
- if (*comma == '"') {
- comma++;
- }
- char *end = comma + strlen(comma) - 1;
- if (*end == '"') {
- *end = '\0';
- }
-
- char *dup = strdup(comma);
- if (!dup) {
- fprintf(stderr, "sxwmrc:%d: failed to allocate memory\n", lineno);
- goto cleanup_file;
- }
-
- /* store each programs name in its own row at index 0 */
- cfg->should_float[should_floatn][0] = dup;
- should_floatn++;
- comma = strtok_r(NULL, ",", &comma_ptr);
- }
- }
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);
-
- KeySym ks;
- unsigned mods = parse_combo(combo, cfg, &ks);
- if (ks == NoSymbol) {
- fprintf(stderr, "sxwmrc:%d: bad key in '%s'\n", lineno, combo);
+ char *act;
+ Binding *b = parse_bind_line(cfg, rest, lineno, key, &act);
+ if (!b)
continue;
- }
-
- Binding *b = alloc_bind(cfg, mods, ks);
- if (!b) {
- fputs("sxwm: too many binds\n", stderr);
- goto cleanup_file;
- }
if (*act == '"' && !strcmp(key, "bind")) {
b->type = TYPE_CMD;
@@ -362,249 +332,158 @@ found:
}
else {
b->type = TYPE_FUNC;
- Bool found = False;
+ b->action.fn = NULL;
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) {
+ if (!b->action.fn)
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);
-
- 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;
- }
-
- int n;
- if (sscanf(act, "move %d", &n) == 1 && n >= 1 && n <= NUM_WORKSPACES) {
- b->type = TYPE_WS_CHANGE;
- b->action.ws = n - 1;
- }
- else if (sscanf(act, "swap %d", &n) == 1 && n >= 1 && n <= NUM_WORKSPACES) {
- b->type = TYPE_WS_MOVE;
- b->action.ws = n - 1;
- }
- else {
- fprintf(stderr, "sxwmrc:%d: invalid workspace action '%s'\n", lineno, act);
- }
+ else if (!strcmp(key, "can_be_swallowed")) {
+ int idx = find_free_slot(cfg->can_be_swallowed, MAX_ITEMS);
+ if (idx < 0)
+ idx = 0;
+ parse_csv_to_array(rest, cfg->can_be_swallowed, &idx, MAX_ITEMS, 1);
}
- else if (!strcmp(key, "scratchpad")) {
- char *mid = strchr(rest, ':');
- if (!mid) {
- fprintf(stderr, "sxwmrc:%d: scratchpad missing action\n", lineno);
- continue;
- }
- *mid = '\0';
- char *combo = strip(rest);
- char *act = strip(mid + 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);
- goto cleanup_file;
- }
-
- int padnum = -1;
-
- if (sscanf(act, "create %d", &padnum) == 1 && padnum >= 1 && padnum <= MAX_SCRATCHPADS) {
- b->type = TYPE_SP_CREATE;
- b->action.sp = padnum - 1;
- }
- else if (sscanf(act, "toggle %d", &padnum) == 1 && padnum >= 1 && padnum <= MAX_SCRATCHPADS) {
- b->type = TYPE_SP_TOGGLE;
- b->action.sp = padnum - 1;
- }
- else if (sscanf(act, "remove %d", &padnum) == 1 && padnum >= 1 && padnum <= MAX_SCRATCHPADS) {
- b->type = TYPE_SP_REMOVE;
- b->action.sp = padnum - 1;
- }
- else {
- fprintf(stderr, "sxwmrc:%d: invalid scratchpad action '%s'\n", lineno, act);
- }
+ else if (!strcmp(key, "can_swallow")) {
+ int idx = find_free_slot(cfg->can_swallow, MAX_ITEMS);
+ if (idx < 0)
+ idx = 0;
+ parse_csv_to_array(rest, cfg->can_swallow, &idx, MAX_ITEMS, 1);
}
else if (!strcmp(key, "exec")) {
if (to_run >= MAX_ITEMS) {
fprintf(stderr, "sxwmrc:%d: too many exec commands\n", lineno);
continue;
}
-
- char *comment = strchr(rest, '#');
- if (comment) {
- *comment = '\0';
- }
-
- char *cmd = strip(rest);
- cmd = strip_quotes(cmd);
-
+ char *cmd = strip_quotes(strip_comment(rest));
if (!*cmd) {
fprintf(stderr, "sxwmrc:%d: empty exec command\n", lineno);
continue;
}
-
cfg->to_run[to_run] = strdup(cmd);
- if (!cfg->to_run[to_run]) {
- fprintf(stderr, "sxwmrc:%d: failed to allocate memory for exec command\n", lineno);
- goto cleanup_file;
- }
+ if (!cfg->to_run[to_run])
+ goto cleanup;
to_run++;
}
- else if (!strcmp(key, "can_swallow")) {
- char *token = strtok(rest, ",");
- int i = 0;
- while (token && i < MAX_ITEMS) {
- char *item = strip_quotes(strip(token));
- if (*item) {
- cfg->can_swallow[i] = malloc(2 * sizeof(char *));
- if (!cfg->can_swallow[i]) {
- fprintf(stderr, "sxwmrc:%d: malloc failed\n", lineno);
- break;
- }
- cfg->can_swallow[i][0] = strdup(item);
- cfg->can_swallow[i][1] = NULL;
- i++;
- }
- token = strtok(NULL, ",");
- }
- }
- else if (!strcmp(key, "can_be_swallowed")) {
- char *token = strtok(rest, ",");
- int i = 0;
- while (token && i < MAX_ITEMS) {
- char *item = strip_quotes(strip(token));
- if (*item) {
- cfg->can_be_swallowed[i] = malloc(2 * sizeof(char *));
- if (!cfg->can_be_swallowed[i]) {
- break;
- }
- cfg->can_be_swallowed[i][0] = strdup(item);
- cfg->can_be_swallowed[i][1] = NULL;
- i++;
- }
- token = strtok(NULL, ",");
- }
+ else if (!strcmp(key, "floating_on_top"))
+ cfg->floating_on_top = !strcmp(rest, "true");
+ else if (!strcmp(key, "focused_border_colour"))
+ cfg->border_foc_col = parse_col(rest);
+ else if (!strcmp(key, "gaps"))
+ cfg->gaps = atoi(rest);
+ else if (!strcmp(key, "master_width")) {
+ float mf = (float)atoi(rest) / 100.0f;
+ for (int i = 0; i < MAX_MONITORS; i++)
+ cfg->master_width[i] = mf;
}
- else if (!strcmp(key, "new_win_master")) {
- cfg->new_win_master = !strcmp(rest, "true") ? True : False;
+ else if (!strcmp(key, "mod_key")) {
+ unsigned m = parse_mods(rest, cfg);
+ if (m & (Mod1Mask | Mod4Mask | ShiftMask | ControlMask))
+ cfg->modkey = m;
+ else
+ fprintf(stderr, "sxwmrc:%d: unknown mod_key '%s'\n", lineno, rest);
}
+ else if (!strcmp(key, "motion_throttle"))
+ cfg->motion_throttle = atoi(rest);
+ else if (!strcmp(key, "move_window_amount"))
+ cfg->move_window_amt = atoi(rest);
+ else if (!strcmp(key, "new_win_focus"))
+ cfg->new_win_focus = !strcmp(rest, "true");
+ else if (!strcmp(key, "new_win_master"))
+ cfg->new_win_master = !strcmp(rest, "true");
else if (!strcmp(key, "open_in_workspace")) {
char *mid = strchr(rest, ':');
if (!mid) {
- fprintf(stderr, "sxwmrc:%d: open_in_workspace missing workspace number\n", lineno);
+ fprintf(stderr, "sxwmrc:%d: open_in_workspace missing workspace\n", lineno);
continue;
}
*mid = '\0';
- char *class_name = strip(rest);
- char *ws_str = strip(mid + 1);
-
- class_name = strip_quotes(class_name);
-
- int ws = atoi(ws_str);
+ char *cls = strip_quotes(strip(rest));
+ int ws = atoi(strip(mid + 1));
if (ws < 1 || ws > NUM_WORKSPACES) {
fprintf(stderr, "sxwmrc:%d: invalid workspace number %d\n", lineno, ws);
continue;
}
-
- /* find free slot in open_in_workspace */
- int slot = -1;
- for (int i = 0; i < MAX_ITEMS; i++) {
- if (!cfg->open_in_workspace[i]) {
- slot = i;
- break;
- }
- }
-
+ int slot = find_free_slot(cfg->open_in_workspace, MAX_ITEMS);
if (slot >= 0) {
- cfg->open_in_workspace[slot] = malloc(2 * sizeof(char *));
- if (cfg->open_in_workspace[slot]) {
- cfg->open_in_workspace[slot][0] = strdup(class_name); /* class name */
- char ws_buf[16];
- snprintf(ws_buf, sizeof(ws_buf), "%d", ws - 1); /* 0-indexed workspace */
- cfg->open_in_workspace[slot][1] = strdup(ws_buf); /* workspace number */
- }
+ char ws_buf[16];
+ snprintf(ws_buf, sizeof(ws_buf), "%d", ws - 1);
+ cfg->open_in_workspace[slot] = alloc_str_pair(cls, ws_buf);
}
}
- else if (!strcmp(key, "start_fullscreen")) {
- char *comment = strchr(rest, '#');
- size_t len = comment ? (size_t)(comment - rest) : strlen(rest);
- if (len >= sizeof(line)) {
- len = sizeof(line) - 1;
- }
-
- char win[sizeof(line)];
- snprintf(win, sizeof(win), "%.*s", (int)len, rest);
-
- char *final = strip(win);
- char *comma_ptr;
- char *comma = strtok_r(final, ",", &comma_ptr);
-
- int start_fullscreen_idx = 0;
- /* find first empty slot */
- for (int i = 0; i < MAX_ITEMS; i++) {
- if (!cfg->start_fullscreen[i]) {
- start_fullscreen_idx = i;
+ else if (!strcmp(key, "resize_master_amount"))
+ cfg->resize_master_amt = atoi(rest);
+ else if (!strcmp(key, "resize_stack_amount"))
+ cfg->resize_stack_amt = atoi(rest);
+ else if (!strcmp(key, "resize_window_amount"))
+ cfg->resize_window_amt = atoi(rest);
+ else if (!strcmp(key, "scratchpad")) {
+ char *act;
+ Binding *b = parse_bind_line(cfg, rest, lineno, "scratchpad", &act);
+ if (!b)
+ goto cleanup;
+
+ int n, found = 0;
+ static const struct { const char *fmt; int type; } sp_acts[] = {
+ {"create %d", TYPE_SP_CREATE},
+ {"toggle %d", TYPE_SP_TOGGLE},
+ {"remove %d", TYPE_SP_REMOVE},
+ };
+ for (size_t i = 0; i < sizeof(sp_acts) / sizeof(sp_acts[0]); i++) {
+ if (sscanf(act, sp_acts[i].fmt, &n) == 1 && n >= 1 && n <= MAX_SCRATCHPADS) {
+ b->type = sp_acts[i].type;
+ b->action.sp = n - 1;
+ found = 1;
break;
}
}
+ if (!found)
+ fprintf(stderr, "sxwmrc:%d: invalid scratchpad action '%s'\n", lineno, act);
+ }
+ else if (!strcmp(key, "should_float")) {
+ char *clean = strip_comment(rest);
+ if (parse_csv_to_array(clean, cfg->should_float, &should_floatn, MAX_ITEMS, 0) < 0)
+ goto cleanup;
+ }
+ else if (!strcmp(key, "snap_distance"))
+ cfg->snap_distance = atoi(rest);
+ else if (!strcmp(key, "start_fullscreen")) {
+ int idx = find_free_slot(cfg->start_fullscreen, MAX_ITEMS);
+ if (idx < 0)
+ idx = 0;
- /* store each comma separated value in a separate row */
- while (comma && start_fullscreen_idx < MAX_ITEMS) {
- comma = strip(comma);
- if (*comma == '"') {
- comma++;
- }
- char *end = comma + strlen(comma) - 1;
- if (*end == '"') {
- *end = '\0';
- }
-
- char *dup = strdup(comma);
- if (!dup) {
- fprintf(stderr, "sxwmrc:%d: failed to allocate memory\n", lineno);
- goto cleanup_file;
- }
-
- cfg->start_fullscreen[start_fullscreen_idx] = malloc(2 * sizeof(char *));
- if (!cfg->start_fullscreen[start_fullscreen_idx]) {
- free(dup);
- fprintf(stderr, "sxwmrc:%d: failed to allocate memory\n", lineno);
- goto cleanup_file;
- }
+ char *clean = strip_comment(rest);
+ if (parse_csv_to_array(clean, cfg->start_fullscreen, &idx, MAX_ITEMS, 1) < 0)
+ goto cleanup;
+ }
+ else if (!strcmp(key, "swap_border_colour"))
+ cfg->border_swap_col = parse_col(rest);
+ else if (!strcmp(key, "unfocused_border_colour"))
+ cfg->border_ufoc_col = parse_col(rest);
+ else if (!strcmp(key, "warp_cursor"))
+ cfg->warp_cursor = !strcmp(rest, "true");
+ else if (!strcmp(key, "workspace")) {
+ char *act;
+ Binding *b = parse_bind_line(cfg, rest, lineno, "workspace", &act);
+ if (!b)
+ continue;
- /* store each program's name in its own row at index 0 */
- cfg->start_fullscreen[start_fullscreen_idx][0] = dup;
- cfg->start_fullscreen[start_fullscreen_idx][1] = NULL;
- start_fullscreen_idx++;
- comma = strtok_r(NULL, ",", &comma_ptr);
+ int n;
+ if (sscanf(act, "move %d", &n) == 1 && n >= 1 && n <= NUM_WORKSPACES) {
+ b->type = TYPE_WS_CHANGE;
+ b->action.ws = n - 1;
+ }
+ else if (sscanf(act, "swap %d", &n) == 1 && n >= 1 && n <= NUM_WORKSPACES) {
+ b->type = TYPE_WS_MOVE;
+ b->action.ws = n - 1;
+ }
+ else {
+ fprintf(stderr, "sxwmrc:%d: invalid workspace action '%s'\n", lineno, act);
}
}
else {
@@ -613,115 +492,69 @@ found:
}
fclose(f);
- remap_and_dedupe_binds(cfg);
+ dedupe_binds(cfg);
return 0;
-cleanup_file:
- if (f) {
- fclose(f);
- }
+cleanup:
+ fclose(f);
for (int j = 0; j < MAX_ITEMS; j++) {
if (cfg->should_float[j]) {
free(cfg->should_float[j][0]);
free(cfg->should_float[j]);
}
+
if (cfg->can_swallow[j]) {
free(cfg->can_swallow[j][0]);
free(cfg->can_swallow[j]);
}
+
if (cfg->can_be_swallowed[j]) {
free(cfg->can_be_swallowed[j][0]);
free(cfg->can_be_swallowed[j]);
}
+
if (cfg->open_in_workspace[j]) {
free(cfg->open_in_workspace[j][0]);
free(cfg->open_in_workspace[j][1]);
free(cfg->open_in_workspace[j]);
}
}
- for (int i = 0; i < to_run; i++) {
+ for (int i = 0; i < to_run; i++)
free(cfg->to_run[i]);
- }
- return -1;
-}
-
-int parse_mods(const char *mods, Config *cfg)
-{
- KeySym dummy;
- return parse_combo(mods, cfg, &dummy);
-}
-
-KeySym parse_keysym(const char *key)
-{
- KeySym ks = XStringToKeysym(key);
- if (ks != NoSymbol) {
- return ks;
- }
-
- char buf[64];
- size_t n = strlen(key);
- if (n >= sizeof buf) {
- n = sizeof buf - 1;
- }
-
- 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;
- }
-
- 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;
- }
- fprintf(stderr, "sxwmrc: unknown keysym '%s'\n", key);
- return NoSymbol;
+ return -1;
}
-#ifndef __linux__
-char **split_cmd(const char *cmd, int *out_argc)
+static char **split_cmd(const char *cmd, int *out_argc)
{
enum { NORMAL, IN_QUOTE } state = NORMAL;
- const char *p = cmd;
size_t cap = 8, argc = 0, toklen = 0;
char *token = malloc(strlen(cmd) + 1);
char **argv = malloc(cap * sizeof *argv);
- if (!token || !argv) {
+
+ if (!token || !argv)
goto err;
- }
- while (*p) {
+ for (const char *p = cmd; *p; p++) {
if (state == NORMAL && isspace((unsigned char)*p)) {
if (toklen) {
token[toklen] = '\0';
if (argc + 1 >= cap) {
cap *= 2;
- char **new_argv = realloc(argv, cap * sizeof *argv);
- if (!new_argv) {
+ char **tmp = realloc(argv, cap * sizeof *argv);
+ if (!tmp)
goto err;
- }
- argv = new_argv;
+
+ argv = tmp;
}
argv[argc++] = strdup(token);
toklen = 0;
}
}
- else if (*p == '"') {
+ else if (*p == '"')
state = (state == NORMAL) ? IN_QUOTE : NORMAL;
- }
- else {
+ else
token[toklen++] = *p;
- }
- p++;
}
if (toklen) {
@@ -734,63 +567,43 @@ char **split_cmd(const char *cmd, int *out_argc)
return argv;
err:
- if (token) {
- free(token);
- }
+ free(token);
if (argv) {
- for (size_t i = 0; i < argc; i++) {
+ for (size_t i = 0; i < argc; i++)
free(argv[i]);
- }
free(argv);
}
return NULL;
}
-#endif
-const char **build_argv(const char *cmd)
+static char *strip(char *s)
{
-#ifdef __linux__
- wordexp_t p;
- if (wordexp(cmd, &p, 0) != 0 || p.we_wordc == 0) {
- fprintf(stderr, "sxwm: wordexp failed for cmd: '%s'\n", cmd);
- return NULL;
- }
-
- const char **argv = malloc((p.we_wordc + 1) * sizeof *argv);
- if (!argv) {
- wordfree(&p);
- return NULL;
- }
-
- for (size_t i = 0; i < p.we_wordc; i++) {
- argv[i] = strdup(p.we_wordv[i]);
- }
- argv[p.we_wordc] = NULL;
- wordfree(&p);
- return argv;
-
-#else
- int argc = 0;
- char **tmp = split_cmd(cmd, &argc);
- if (!tmp) {
- return NULL;
- }
+ while (*s && isspace((unsigned char)*s))
+ s++;
+ if (!*s)
+ return s;
+ char *e = s + strlen(s) - 1;
+ while (e > s && isspace((unsigned char)*e))
+ *e-- = '\0';
+ return s;
+}
- const char **argv = malloc((argc + 1) * sizeof *argv);
- if (!argv) {
- for (int i = 0; i < argc; i++) {
- free(tmp[i]);
- }
- free(tmp);
- return NULL;
- }
+static char *strip_comment(char *s)
+{
+ char *c = strchr(s, '#');
+ if (c)
+ *c = '\0';
+ return strip(s);
+}
- for (int i = 0; i < argc; i++) {
- argv[i] = tmp[i];
+static char *strip_quotes(char *s)
+{
+ size_t len = strlen(s);
+ if (len > 0 && s[0] == '"') {
+ s++;
+ len--;
}
- argv[argc] = NULL;
- free(tmp);
- return argv;
-#endif
+ if (len > 0 && s[len - 1] == '"')
+ s[len - 1] = '\0';
+ return s;
}
-