2 #define I3__FILE__ "config_parser.c"
34 #include <sys/types.h>
42 #define y(x, ...) yajl_gen_ ## x (command_output.json_gen, ##__VA_ARGS__)
43 #define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char*)str, strlen(str))
60 typedef struct token {
104 for (
int c = 0; c < 10; c++) {
105 if (
stack[c].identifier != NULL &&
106 strcmp(
stack[c].identifier, identifier) != 0)
108 if (
stack[c].identifier == NULL) {
125 fprintf(stderr,
"BUG: commands_parser stack full. This means either a bug "
126 "in the code, or a new command which contains more than "
127 "10 identified tokens.\n");
132 for (
int c = 0; c < 10; c++) {
133 if (
stack[c].identifier != NULL)
145 fprintf(stderr,
"BUG: commands_parser stack full. This means either a bug "
146 "in the code, or a new command which contains more than "
147 "10 identified tokens.\n");
153 for (
int c = 0; c < 10; c++) {
154 if (
stack[c].identifier == NULL)
156 if (strcmp(identifier,
stack[c].identifier) == 0)
163 for (
int c = 0; c < 10; c++) {
164 if (
stack[c].identifier == NULL)
166 if (strcmp(identifier,
stack[c].identifier) == 0)
173 for (
int c = 0; c < 10; c++) {
189 typedef struct criterion {
196 static
TAILQ_HEAD(criteria_head, criterion) criteria =
204 static
void push_criterion(
void *unused_criteria, const
char *type,
206 struct criterion *criterion = malloc(
sizeof(
struct criterion));
207 criterion->type = strdup(type);
208 criterion->value = strdup(value);
217 static void clear_criteria(
void *unused_criteria) {
218 struct criterion *criterion;
221 free(criterion->type);
222 free(criterion->value);
277 statelist[statelist_idx++] = _next_state;
286 while (*walk !=
'\n' && *walk !=
'\r' && walk >= beginning) {
301 char *end = strchr(result,
'\n');
310 const char *dumpwalk = input;
312 while (*dumpwalk !=
'\0') {
313 char *next_nl = strchr(dumpwalk,
'\n');
314 if (next_nl != NULL) {
315 DLOG(
"CONFIG(line %3d): %.*s\n", linecnt, (
int)(next_nl - dumpwalk), dumpwalk);
316 dumpwalk = next_nl + 1;
318 DLOG(
"CONFIG(line %3d): %s\n", linecnt, dumpwalk);
335 const char *walk = input;
336 const size_t len = strlen(input);
349 while ((walk - input) <= len) {
352 while ((*walk ==
' ' || *walk ==
'\t') && *walk !=
'\0')
358 token_handled =
false;
359 for (c = 0; c < ptr->
n; c++) {
360 token = &(ptr->
array[c]);
363 if (token->
name[0] ==
'\'') {
364 if (strncasecmp(walk, token->
name + 1, strlen(token->
name) - 1) == 0) {
367 walk += strlen(token->
name) - 1;
369 token_handled =
true;
375 if (strcmp(token->
name,
"number") == 0) {
379 long int num = strtol(walk, &end, 10);
380 if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
381 (errno != 0 && num == 0))
394 token_handled =
true;
398 if (strcmp(token->
name,
"string") == 0 ||
399 strcmp(token->
name,
"word") == 0) {
400 const char *beginning = walk;
405 while (*walk !=
'\0' && (*walk !=
'"' || *(walk-1) ==
'\\'))
408 if (token->
name[0] ==
's') {
409 while (*walk !=
'\0' && *walk !=
'\r' && *walk !=
'\n')
415 while (*walk !=
' ' && *walk !=
'\t' &&
416 *walk !=
']' && *walk !=
',' &&
417 *walk !=
';' && *walk !=
'\r' &&
418 *walk !=
'\n' && *walk !=
'\0')
422 if (walk != beginning) {
423 char *str =
scalloc(walk-beginning + 1);
426 for (inpos = 0, outpos = 0;
427 inpos < (walk-beginning);
432 if (beginning[inpos] ==
'\\' && beginning[inpos+1] ==
'"')
434 str[outpos] = beginning[inpos];
444 token_handled =
true;
449 if (strcmp(token->
name,
"line") == 0) {
450 while (*walk !=
'\0' && *walk !=
'\n' && *walk !=
'\r')
453 token_handled =
true;
459 if (strcmp(token->
name,
"end") == 0) {
461 if (*walk ==
'\0' || *walk ==
'\n' || *walk ==
'\r') {
463 token_handled =
true;
479 if (!token_handled) {
483 for (c = 0; c < ptr->
n; c++)
484 tokenlen += strlen(ptr->
array[c].
name) + strlen(
"'', ");
490 char *possible_tokens =
smalloc(tokenlen + 1);
491 char *tokenwalk = possible_tokens;
492 for (c = 0; c < ptr->
n; c++) {
493 token = &(ptr->
array[c]);
494 if (token->
name[0] ==
'\'') {
498 strcpy(tokenwalk, token->
name + 1);
499 tokenwalk += strlen(token->
name + 1);
504 if (strcmp(token->
name,
"error") == 0)
509 strcpy(tokenwalk, token->
name);
510 tokenwalk += strlen(token->
name);
513 if (c < (ptr->
n - 1)) {
519 sasprintf(&errormessage,
"Expected one of these tokens: %s",
521 free(possible_tokens);
529 char *position =
scalloc(strlen(error_line) + 1);
530 const char *copywalk;
531 for (copywalk = error_line;
532 *copywalk !=
'\n' && *copywalk !=
'\r' && *copywalk !=
'\0';
534 position[(copywalk - error_line)] = (copywalk >= walk ?
'^' : (*copywalk ==
'\t' ?
'\t' :
' '));
535 position[(copywalk - error_line)] =
'\0';
537 ELOG(
"CONFIG: %s\n", errormessage);
543 const char *context_p1_start =
start_of_line(error_line-2, input);
544 char *context_p1_line =
single_line(context_p1_start);
546 const char *context_p2_start =
start_of_line(context_p1_start-2, input);
547 char *context_p2_line =
single_line(context_p2_start);
548 ELOG(
"CONFIG: Line %3d: %s\n", linecnt - 2, context_p2_line);
549 free(context_p2_line);
551 ELOG(
"CONFIG: Line %3d: %s\n", linecnt - 1, context_p1_line);
552 free(context_p1_line);
554 ELOG(
"CONFIG: Line %3d: %s\n", linecnt, error_copy);
555 ELOG(
"CONFIG: %s\n", position);
558 for (
int i = 0; i < 2; i++) {
559 char *error_line_end = strchr(error_line,
'\n');
560 if (error_line_end != NULL && *(error_line_end + 1) !=
'\0') {
561 error_line = error_line_end + 1;
563 ELOG(
"CONFIG: Line %3d: %s\n", linecnt + i + 1, error_copy);
583 ystr(
"errorposition");
588 while ((walk - input) <= len && *walk !=
'\n')
598 bool error_token_found =
false;
599 for (
int i =
statelist_idx-1; (i >= 0) && !error_token_found; i--) {
601 for (
int j = 0; j < errptr->
n; j++) {
602 if (strcmp(errptr->
array[j].
name,
"error") != 0)
605 error_token_found =
true;
610 assert(error_token_found);
636 fprintf(stdout,
"# ");
637 vfprintf(stdout, fmt, args);
645 vfprintf(stderr, fmt, args);
651 void cfg_criteria_init(
I3_CFG,
int _state) {
655 void cfg_criteria_add(
I3_CFG,
const char *ctype,
const char *cvalue) {
658 void cfg_criteria_pop_state(
I3_CFG) {
662 int main(
int argc,
char *argv[]) {
664 fprintf(stderr,
"Syntax: %s <command>\n", argv[0]);
667 struct context context;
683 while (*walk !=
'\0') {
690 if (strncasecmp(line,
"bindcode", strlen(
"bindcode")) == 0 ||
691 strncasecmp(line,
"force_focus_wrapping", strlen(
"force_focus_wrapping")) == 0 ||
692 strncasecmp(line,
"# i3 config file (v4)", strlen(
"# i3 config file (v4)")) == 0 ||
693 strncasecmp(line,
"workspace_layout", strlen(
"workspace_layout")) == 0) {
694 printf(
"deciding for version 4 due to this line: %.*s\n", (
int)(walk-line), line);
699 if (strncasecmp(line,
"bind", strlen(
"bind")) == 0) {
700 char *bind = strchr(line,
' ');
703 while ((*bind ==
' ' || *bind ==
'\t') && *bind !=
'\0')
707 if ((bind = strchr(bind,
' ')) == NULL)
709 while ((*bind ==
' ' || *bind ==
'\t') && *bind !=
'\0')
713 if (strncasecmp(bind,
"layout", strlen(
"layout")) == 0 ||
714 strncasecmp(bind,
"floating", strlen(
"floating")) == 0 ||
715 strncasecmp(bind,
"workspace", strlen(
"workspace")) == 0 ||
716 strncasecmp(bind,
"focus left", strlen(
"focus left")) == 0 ||
717 strncasecmp(bind,
"focus right", strlen(
"focus right")) == 0 ||
718 strncasecmp(bind,
"focus up", strlen(
"focus up")) == 0 ||
719 strncasecmp(bind,
"focus down", strlen(
"focus down")) == 0 ||
720 strncasecmp(bind,
"border normal", strlen(
"border normal")) == 0 ||
721 strncasecmp(bind,
"border 1pixel", strlen(
"border 1pixel")) == 0 ||
722 strncasecmp(bind,
"border pixel", strlen(
"border pixel")) == 0 ||
723 strncasecmp(bind,
"border borderless", strlen(
"border borderless")) == 0 ||
724 strncasecmp(bind,
"--no-startup-id", strlen(
"--no-startup-id")) == 0 ||
725 strncasecmp(bind,
"bar", strlen(
"bar")) == 0) {
726 printf(
"deciding for version 4 due to this line: %.*s\n", (
int)(walk-line), line);
753 if (pipe(writepipe) != 0 ||
754 pipe(readpipe) != 0) {
755 warn(
"migrate_config: Could not create pipes");
761 warn(
"Could not fork()");
769 dup2(writepipe[0], 0);
773 dup2(readpipe[1], 1);
775 static char *argv[] = {
791 while (written < size) {
792 if ((ret = write(writepipe[1], input + written, size - written)) < 0) {
793 warn(
"Could not write to pipe");
804 int conv_size = 65535;
805 char *converted = malloc(conv_size);
808 if (read_bytes == conv_size) {
810 converted = realloc(converted, conv_size);
812 ret = read(readpipe[0], converted + read_bytes, conv_size - read_bytes);
814 warn(
"Cannot read from pipe");
824 if (!WIFEXITED(status)) {
825 fprintf(stderr,
"Child did not terminate normally, using old config file (will lead to broken behaviour)\n");
829 int returncode = WEXITSTATUS(status);
830 if (returncode != 0) {
831 fprintf(stderr,
"Migration process exit code was != 0\n");
832 if (returncode == 2) {
833 fprintf(stderr,
"could not start the migration script\n");
835 }
else if (returncode == 1) {
836 fprintf(stderr,
"This already was a v4 config. Please add the following line to your config file:\n");
837 fprintf(stderr,
"# i3 config file (v4)\n");
866 if ((bind->
symbol == NULL && current->
symbol != NULL) ||
872 if (bind->
symbol != NULL &&
885 ELOG(
"Duplicate keybinding in config file:\n modmask %d with keycode %d, command \"%s\"\n",
888 ELOG(
"Duplicate keybinding in config file:\n modmask %d with keysym %s, command \"%s\"\n",
902 int fd, ret, read_bytes = 0;
906 char buffer[1026], key[512], value[512];
908 if ((fd = open(f, O_RDONLY)) == -1)
909 die(
"Could not open configuration file: %s\n", strerror(errno));
911 if (fstat(fd, &stbuf) == -1)
912 die(
"Could not fstat file: %s\n", strerror(errno));
914 buf =
scalloc((stbuf.st_size + 1) *
sizeof(
char));
915 while (read_bytes < stbuf.st_size) {
916 if ((ret = read(fd, buf + read_bytes, (stbuf.st_size - read_bytes))) < 0)
917 die(
"Could not read(): %s\n", strerror(errno));
921 if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
922 die(
"Could not lseek: %s\n", strerror(errno));
924 if ((fstr = fdopen(fd,
"r")) == NULL)
925 die(
"Could not fdopen: %s\n", strerror(errno));
927 while (!feof(fstr)) {
928 if (fgets(buffer, 1024, fstr) == NULL) {
931 die(
"Could not read configuration file\n");
935 if (sscanf(buffer,
"%s %[^\n]", key, value) < 1 ||
936 key[0] ==
'#' || strlen(key) < 3)
939 if (strcasecmp(key,
"set") == 0) {
940 if (value[0] !=
'$') {
941 ELOG(
"Malformed variable assignment, name has to start with $\n");
946 char *v_key = value, *v_value;
947 if (strstr(value,
" ") == NULL && strstr(value,
"\t") == NULL) {
948 ELOG(
"Malformed variable assignment, need a value\n");
952 if (!(v_value = strstr(value,
" ")))
953 v_value = strstr(value,
"\t");
956 while (*v_value ==
'\t' || *v_value ==
' ')
963 DLOG(
"Got new variable %s = %s\n", v_key, v_value);
978 int extra = (strlen(current->
value) - strlen(current->
key));
981 next < (bufcopy + stbuf.st_size) &&
982 (next = strcasestr(next, current->
key)) != NULL;
983 next += strlen(current->
key)) {
985 extra_bytes += extra;
992 char *walk = buf, *destwalk;
993 char *
new =
smalloc((stbuf.st_size + extra_bytes + 1) *
sizeof(
char));
995 while (walk < (buf + stbuf.st_size)) {
1000 int distance = stbuf.st_size;
1004 if ((current->
next_match - walk) < distance) {
1009 if (nearest == NULL) {
1011 strncpy(destwalk, walk, (buf + stbuf.st_size) - walk);
1012 destwalk += (buf + stbuf.st_size) - walk;
1017 strncpy(destwalk, walk, distance);
1018 strncpy(destwalk + distance, nearest->
value, strlen(nearest->
value));
1019 walk += distance + strlen(nearest->
key);
1020 destwalk += distance + strlen(nearest->
value);
1030 if (converted != NULL) {
1032 ELOG(
"****************************************************************\n");
1033 ELOG(
"NOTE: Automatically converted configuration file from v3 to v4.\n");
1035 ELOG(
"Please convert your config file to v4. You can use this command:\n");
1036 ELOG(
" mv %s %s.O\n", f, f);
1037 ELOG(
" i3-migrate-config-to-v4 %s.O > %s\n", f, f);
1038 ELOG(
"****************************************************************\n");
1044 printf(
"**********************************************************************\n");
1045 printf(
"ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4\n");
1046 printf(
"was not correctly installed on your system?\n");
1047 printf(
"**********************************************************************\n");
1053 context =
scalloc(
sizeof(
struct context));
1057 yajl_gen_free(config_output->
json_gen);
1062 ELOG(
"FYI: You are using i3 version " I3_VERSION
"\n");
1064 ELOG(
"Please convert your configfile first, then fix any remaining errors (see above).\n");
1068 sasprintf(&editaction,
"i3-sensible-editor \"%s\" && i3-msg reload\n", f);
1078 "You have an error in your i3 config file!" :
1079 "Your config is outdated. Please fix the warnings to make sure everything works."),
1084 (context->
has_errors ?
"show errors" :
"show warnings"),