40#include <xcb/xcb_xrm.h>
56#include "GENERATED_config_enums.h"
73#include "GENERATED_config_tokens.h"
81 for (
int c = 0; c < 10; c++) {
82 if (
ctx->stack[c].identifier != NULL &&
83 strcmp(
ctx->stack[c].identifier, identifier) != 0) {
86 if (
ctx->stack[c].identifier == NULL) {
88 ctx->stack[c].identifier = identifier;
90 ctx->stack[c].type = STACK_STR;
93 char *prev =
ctx->stack[c].val.str;
103 fprintf(stderr,
"BUG: config_parser stack full. This means either a bug "
104 "in the code, or a new command which contains more than "
105 "10 identified tokens.\n");
110 for (
int c = 0; c < 10; c++) {
111 if (
ctx->stack[c].identifier != NULL) {
115 ctx->stack[c].identifier = identifier;
116 ctx->stack[c].val.num = num;
117 ctx->stack[c].type = STACK_LONG;
124 fprintf(stderr,
"BUG: config_parser stack full. This means either a bug "
125 "in the code, or a new command which contains more than "
126 "10 identified tokens.\n");
131 for (
int c = 0; c < 10; c++) {
132 if (
ctx->stack[c].identifier == NULL) {
135 if (strcmp(identifier,
ctx->stack[c].identifier) == 0) {
136 return ctx->stack[c].val.str;
143 for (
int c = 0; c < 10; c++) {
144 if (
ctx->stack[c].identifier == NULL) {
147 if (strcmp(identifier,
ctx->stack[c].identifier) == 0) {
148 return ctx->stack[c].val.num;
155 for (
int c = 0; c < 10; c++) {
156 if (
ctx->stack[c].type == STACK_STR) {
157 free(
ctx->stack[c].val.str);
159 ctx->stack[c].identifier = NULL;
160 ctx->stack[c].val.str = NULL;
161 ctx->stack[c].val.num = 0;
169#include "GENERATED_config_call.h"
180 ctx->has_errors =
true;
186 ctx->state = _next_state;
187 if (
ctx->state == INITIAL) {
193 for (
int i = 0; i <
ctx->statelist_idx; i++) {
194 if ((cmdp_state)(
ctx->statelist[i]) != _next_state) {
197 ctx->statelist_idx = i + 1;
202 ctx->statelist[
ctx->statelist_idx++] = _next_state;
211 while (walk >= beginning && *walk !=
'\n' && *walk !=
'\r') {
226 char *end = strchr(result,
'\n');
236 const char *dumpwalk = input;
238 while (*dumpwalk !=
'\0') {
239 char *next_nl = strchr(dumpwalk,
'\n');
240 if (next_nl != NULL) {
241 DLOG(
"CONFIG(line %3d): %.*s\n", linecnt, (
int)(next_nl - dumpwalk), dumpwalk);
242 dumpwalk = next_nl + 1;
244 DLOG(
"CONFIG(line %3d): %s\n", linecnt, dumpwalk);
249 ctx->state = INITIAL;
250 for (
int i = 0; i < 10; i++) {
251 ctx->statelist[i] = INITIAL;
253 ctx->statelist_idx = 1;
255 const char *walk = input;
256 const size_t len = strlen(input);
271 while ((
size_t)(walk - input) <= len) {
274 while ((*walk ==
' ' || *walk ==
'\t') && *walk !=
'\0') {
279 token_handled =
false;
280 for (c = 0; c < ptr->
n; c++) {
291 token_handled =
true;
297 if (strcmp(
token->
name,
"number") == 0) {
301 long int num = strtol(walk, &end, 10);
302 if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
303 (errno != 0 && num == 0)) {
319 token_handled =
true;
323 if (strcmp(
token->
name,
"string") == 0 ||
325 const char *beginning = walk;
330 while (*walk !=
'\0' && (*walk !=
'"' || *(walk - 1) ==
'\\')) {
335 while (*walk !=
'\0' && *walk !=
'\r' && *walk !=
'\n') {
342 while (*walk !=
' ' && *walk !=
'\t' &&
343 *walk !=
']' && *walk !=
',' &&
344 *walk !=
';' && *walk !=
'\r' &&
345 *walk !=
'\n' && *walk !=
'\0') {
350 if (walk != beginning) {
351 char *str =
scalloc(walk - beginning + 1, 1);
354 for (inpos = 0, outpos = 0;
355 inpos < (walk - beginning);
360 if (beginning[inpos] ==
'\\' && beginning[inpos + 1] ==
'"') {
363 str[outpos] = beginning[inpos];
375 token_handled =
true;
381 while (*walk !=
'\0' && *walk !=
'\n' && *walk !=
'\r') {
385 token_handled =
true;
392 if (*walk ==
'\0' || *walk ==
'\n' || *walk ==
'\r') {
394 token_handled =
true;
409 if (!token_handled) {
413 for (c = 0; c < ptr->
n; c++) {
414 tokenlen += strlen(ptr->
array[c].
name) + strlen(
"'', ");
421 char *possible_tokens =
smalloc(tokenlen + 1);
422 char *tokenwalk = possible_tokens;
423 for (c = 0; c < ptr->
n; c++) {
445 if (c < (ptr->
n - 1)) {
451 sasprintf(&errormessage,
"Expected one of these tokens: %s",
453 free(possible_tokens);
460 char *position =
scalloc(strlen(error_line) + 1, 1);
461 const char *copywalk;
462 for (copywalk = error_line;
463 *copywalk !=
'\n' && *copywalk !=
'\r' && *copywalk !=
'\0';
465 position[(copywalk - error_line)] = (copywalk >= walk ?
'^' : (*copywalk ==
'\t' ?
'\t' :
' '));
467 position[(copywalk - error_line)] =
'\0';
469 ELOG(
"CONFIG: %s\n", errormessage);
475 const char *context_p1_start =
start_of_line(error_line - 2, input);
476 char *context_p1_line =
single_line(context_p1_start);
478 const char *context_p2_start =
start_of_line(context_p1_start - 2, input);
479 char *context_p2_line =
single_line(context_p2_start);
480 ELOG(
"CONFIG: Line %3d: %s\n", linecnt - 2, context_p2_line);
481 free(context_p2_line);
483 ELOG(
"CONFIG: Line %3d: %s\n", linecnt - 1, context_p1_line);
484 free(context_p1_line);
486 ELOG(
"CONFIG: Line %3d: %s\n", linecnt, error_copy);
487 ELOG(
"CONFIG: %s\n", position);
490 for (
int i = 0; i < 2; i++) {
491 char *error_line_end = strchr(error_line,
'\n');
492 if (error_line_end != NULL && *(error_line_end + 1) !=
'\0') {
493 error_line = error_line_end + 1;
495 ELOG(
"CONFIG: Line %3d: %s\n", linecnt + i + 1, error_copy);
503 while ((
size_t)(walk - input) <= len && *walk !=
'\n') {
514 bool error_token_found =
false;
515 for (
int i =
ctx->statelist_idx - 1; (i >= 0) && !error_token_found; i--) {
517 for (
int j = 0; j < errptr->
n; j++) {
518 if (strcmp(errptr->
array[j].
name,
"error") != 0) {
522 error_token_found =
true;
527 assert(error_token_found);
549 fprintf(stdout,
"# ");
550 vfprintf(stdout, fmt, args);
558 vfprintf(stderr, fmt, args);
564void cfg_criteria_init(
I3_CFG,
int _state) {
568void cfg_criteria_add(
I3_CFG,
const char *ctype,
const char *cvalue) {
571void cfg_criteria_pop_state(
I3_CFG) {
575int main(
int argc,
char *argv[]) {
577 fprintf(stderr,
"Syntax: %s <command>\n", argv[0]);
598 char *editaction, *pageraction;
599 sasprintf(&editaction,
"i3-sensible-editor \"%s\" && i3-msg reload\n", configpath);
608 (
has_errors ?
"You have an error in your i3 config file!" :
"Your config is outdated. Please fix the warnings to make sure everything works."),
613 (
has_errors ?
"show errors" :
"show warnings"),
626static void upsert_variable(
struct variables_head *variables,
char *key,
char *value) {
629 if (strcmp(current->
key,
key) != 0) {
641 struct Variable *test = NULL, *loc = NULL;
647 if (strlen(new->key) >= strlen(test->
key)) {
670 ELOG(
"Failed to open the resource database.\n");
674 database = xcb_xrm_database_from_string(
"");
681 xcb_xrm_resource_get_string(
database, name, NULL, &resource);
710 char buffer[4096], key[512], value[4096], *continuation = NULL;
712 char *old_dir = getcwd(NULL, 0);
716 if ((dir = dirname(dirbuf)) != NULL) {
717 LOG(
"Changing working directory to config file directory %s\n", dir);
718 if (chdir(dir) == -1) {
719 ELOG(
"chdir(%s) failed: %s\n", dir, strerror(errno));
725 if ((fd = open(f, O_RDONLY)) == -1) {
729 if (fstat(fd, &stbuf) == -1) {
733 buf =
scalloc(stbuf.st_size + 1, 1);
735 if ((fstr = fdopen(fd,
"r")) == NULL) {
740 if ((ssize_t)fread(included_file->
raw_contents, 1, stbuf.st_size, fstr) != stbuf.st_size) {
745 bool invalid_sets =
false;
747 while (!feof(fstr)) {
749 continuation = buffer;
751 if (fgets(continuation,
sizeof(buffer) - (continuation - buffer), fstr) == NULL) {
757 if (buffer[strlen(buffer) - 1] !=
'\n' && !feof(fstr)) {
758 ELOG(
"Your line continuation is too long, it exceeds %zd bytes\n",
sizeof(buffer));
763 const bool skip_line = (sscanf(buffer,
"%511s %4095[^\n]", key, value) < 1 || strlen(key) < 3);
764 const bool comment = (key[0] ==
'#');
767 continuation = strstr(buffer,
"\\\n");
772 DLOG(
"line continuation in comment is ignored: \"%.*s\"\n", (
int)strlen(buffer) - 1, buffer);
776 strcpy(buf + strlen(buf), buffer);
779 if (skip_line || comment) {
783 if (strcasecmp(key,
"set") == 0 && *value !=
'\0') {
785 char v_value[4096] = {
'\0'};
787 if (sscanf(value,
"%511s %4095[^\n]", v_key, v_value) < 1) {
788 ELOG(
"Failed to parse variable specification '%s', skipping it.\n", value);
793 if (v_key[0] !=
'$') {
794 ELOG(
"Malformed variable assignment, name has to start with $\n");
801 }
else if (strcasecmp(key,
"set_from_resource") == 0) {
802 char res_name[512] = {
'\0'};
804 char fallback[4096] = {
'\0'};
814 if (sscanf(value,
"%511s %511s %4095[^\n]", v_key, res_name, fallback) < 1) {
815 ELOG(
"Failed to parse resource specification '%s', skipping it.\n", value);
820 if (v_key[0] !=
'$') {
821 ELOG(
"Malformed variable assignment, name has to start with $\n");
827 if (res_value == NULL) {
828 DLOG(
"Could not get resource '%s', using fallback '%s'.\n", res_name, fallback);
854 int extra = (strlen(current->
value) - strlen(current->
key));
857 next < (bufcopy + stbuf.st_size) &&
858 (next = strcasestr(next, current->
key)) != NULL;) {
862 char *end = next + strlen(current->
key);
866 extra_bytes += extra;
873 char *walk = buf, *destwalk;
874 char *
new =
scalloc(stbuf.st_size + extra_bytes + 1, 1);
876 while (walk < (buf + stbuf.st_size)) {
882 int distance = stbuf.st_size;
887 if ((current->
next_match - walk) < distance) {
892 if (nearest == NULL) {
894 strncpy(destwalk, walk, (buf + stbuf.st_size) - walk);
895 destwalk += (buf + stbuf.st_size) - walk;
900 strncpy(destwalk, walk, distance);
901 strcpy(destwalk + distance, nearest->
value);
902 walk += distance + strlen(nearest->
key);
903 destwalk += distance + strlen(nearest->
value);
912 if (
ctx->has_errors) {
930 if (chdir(old_dir) == -1) {
931 ELOG(
"chdir(%s) failed: %s\n", old_dir, strerror(errno));
void check_for_duplicate_bindings(struct context *context)
Checks for duplicate key bindings (the same keycode or keysym is configured more than once).
static struct stack stack
static struct CommandResultIR subcommand_output
static int criteria_next_state
struct tokenptr cmdp_token_ptr
static void next_state(const cmdp_token *token, struct parser_ctx *ctx)
pid_t config_error_nagbar_pid
static const char * start_of_line(const char *walk, const char *beginning)
static char * single_line(const char *start)
static void push_string(struct stack *ctx, const char *identifier, const char *str)
static void clear_stack(struct stack *ctx)
static char * get_resource(char *name)
static void parse_config(struct parser_ctx *ctx, const char *input, struct context *context)
xcb_xrm_database_t * database
parse_file_result_t parse_file(struct parser_ctx *ctx, const char *f, IncludedFile *included_file)
Parses the given file by first replacing the variables, then calling parse_config and launching i3-na...
static void upsert_variable(struct variables_head *variables, char *key, char *value)
static long get_long(struct stack *ctx, const char *identifier)
static const char * get_string(struct stack *ctx, const char *identifier)
static void push_long(struct stack *ctx, const char *identifier, long num)
void free_variables(struct parser_ctx *ctx)
Releases the memory of all variables in ctx.
void start_config_error_nagbar(const char *configpath, bool has_errors)
Launch nagbar to indicate errors in the configuration file.
void start_nagbar(pid_t *nagbar_pid, char *argv[])
Starts an i3-nagbar instance with the given parameters.
const char * i3_version
Git commit identifier, from version.c.
static xcb_cursor_context_t * ctx
void errorlog(char *fmt,...)
void debuglog(char *fmt,...)
int main(int argc, char *argv[])
xcb_connection_t * conn
XCB connection and root screen.
#define I3_CFG
The beginning of the prototype for every cfg_ function.
@ PARSE_FILE_CONFIG_ERRORS
char * sstrdup(const char *str)
Safe-wrapper around strdup which exits if malloc returns NULL (meaning that there is no more memory a...
void * scalloc(size_t num, size_t size)
Safe-wrapper around calloc which exits if malloc returns NULL (meaning that there is no more memory a...
int sasprintf(char **strp, const char *fmt,...)
Safe-wrapper around asprintf which exits if it returns -1 (meaning that there is no more memory avail...
void * smalloc(size_t size)
Safe-wrapper around malloc which exits if malloc returns NULL (meaning that there is no more memory a...
#define SLIST_FOREACH(var, head, field)
#define SLIST_INSERT_HEAD(head, elm, field)
#define SLIST_EMPTY(head)
#define SLIST_FIRST(head)
#define SLIST_REMOVE_HEAD(head, field)
#define SLIST_INSERT_AFTER(slistelm, elm, field)
An intermediate representation of the result of a parse_config call.
Used during the config file lexing/parsing to keep the state of the lexer in order to provide useful ...
Holds a user-assigned variable for parsing the configuration file.
List entry struct for an included file.
char * variable_replaced_contents
char * pattern
The pattern/name used to load the font.