*
* i3 - an improved dynamic tiling window manager
*
- * © 2009 Michael Stapelberg and contributors
+ * © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
extern Config config;
extern SLIST_HEAD(modes_head, Mode) modes;
+/**
+ * Used during the config file lexing/parsing to keep the state of the lexer
+ * in order to provide useful error messages in yyerror().
+ *
+ */
+struct context {
+ int line_number;
+ char *line_copy;
+ const char *filename;
+
+ /* These are the same as in YYLTYPE */
+ int first_column;
+ int last_column;
+};
+
/**
* Part of the struct Config. It makes sense to group colors for background,
* border and text as every element in i3 has them (window decorations, bar).
%option nounput
%option noinput
+%option noyy_top_state
+%option stack
%{
/*
#include "data.h"
#include "config.h"
+#include "log.h"
+#include "util.h"
+
+int yycolumn = 1;
+
+#define YY_DECL int yylex (struct context *context)
+
+#define YY_USER_ACTION { \
+ context->first_column = yycolumn; \
+ context->last_column = yycolumn+yyleng-1; \
+ yycolumn += yyleng; \
+}
+
%}
-%Start BIND_COND
-%Start BINDSYM_COND
-%Start BIND_AWS_COND
-%Start BINDSYM_AWS_COND
-%Start BIND_A2WS_COND
-%Start ASSIGN_COND
-%Start COLOR_COND
-%Start SCREEN_COND
-%Start SCREEN_AWS_COND
+EOL (\r?\n)
+
+%s BIND_COND
+%s BINDSYM_COND
+%s BIND_AWS_COND
+%s BINDSYM_AWS_COND
+%s BIND_A2WS_COND
+%s ASSIGN_COND
+%s COLOR_COND
+%s SCREEN_COND
+%s SCREEN_AWS_COND
+%x BUFFER_LINE
%%
+
+ {
+ /* This is called when a new line is lexed. We only want the
+ * first line to match to go into state BUFFER_LINE */
+ if (context->line_number == 0) {
+ context->line_number = 1;
+ BEGIN(INITIAL);
+ yy_push_state(BUFFER_LINE);
+ }
+ }
+
+<BUFFER_LINE>^[^\r\n]*/{EOL}? {
+ /* save whole line */
+ context->line_copy = strdup(yytext);
+
+ yyless(0);
+ yy_pop_state();
+ yy_set_bol(true);
+ yycolumn = 1;
+}
+
+
<BIND_A2WS_COND>[^\n]+ { BEGIN(INITIAL); yylval.string = strdup(yytext); return STR; }
^[ \t]*#[^\n]* { return TOKCOMMENT; }
<COLOR_COND>[0-9a-fA-F]+ { yylval.string = strdup(yytext); return HEX; }
ctrl { return TOKCONTROL; }
shift { return TOKSHIFT; }
→ { return TOKARROW; }
-\n /* ignore end of line */;
+{EOL} {
+ FREE(context->line_copy);
+ context->line_number++;
+ yy_push_state(BUFFER_LINE);
+ }
<SCREEN_AWS_COND>x { return (int)yytext[0]; }
<BIND_COND>[ \t]+ { BEGIN(BIND_AWS_COND); return WHITESPACE; }
<BINDSYM_COND>[ \t]+ { BEGIN(BINDSYM_AWS_COND); return WHITESPACE; }
<BINDSYM_AWS_COND>[a-zA-Z0-9_]+ { yylval.string = strdup(yytext); return WORD; }
[a-zA-Z]+ { yylval.string = strdup(yytext); return WORD; }
. { return (int)yytext[0]; }
+
+<<EOF>> {
+ while (yy_start_stack_ptr > 0)
+ yy_pop_state();
+ yyterminate();
+}
+
%%
#include "log.h"
typedef struct yy_buffer_state *YY_BUFFER_STATE;
-extern int yylex(void);
+extern int yylex(struct context *context);
extern int yyparse(void);
extern FILE *yyin;
YY_BUFFER_STATE yy_scan_string(const char *);
static struct bindings_head *current_bindings;
-
-int yydebug = 1;
-
-void yyerror(const char *str) {
- fprintf(stderr,"error: %s\n",str);
+static struct context *context;
+
+/* We don’t need yydebug for now, as we got decent error messages using
+ * yyerror(). Should you ever want to extend the parser, it might be handy
+ * to just comment it in again, so it stays here. */
+//int yydebug = 1;
+
+void yyerror(const char *error_message) {
+ ELOG("\n");
+ ELOG("CONFIG: %s\n", error_message);
+ ELOG("CONFIG: in file \"%s\", line %d:\n",
+ context->filename, context->line_number);
+ ELOG("CONFIG: %s\n", context->line_copy);
+ ELOG("CONFIG: ");
+ for (int c = 1; c <= context->last_column; c++)
+ if (c >= context->first_column)
+ printf("^");
+ else printf(" ");
+ printf("\n");
+ ELOG("\n");
}
int yywrap() {
yy_scan_string(new);
+ context = scalloc(sizeof(struct context));
+ context->filename = f;
+
if (yyparse() != 0) {
fprintf(stderr, "Could not parse configfile\n");
exit(1);
}
+ FREE(context->line_copy);
+ free(context);
free(new);
free(buf);
}
%expect 1
%error-verbose
+%lex-param { struct context *context }
%union {
int number;
int i, screens, opt;
char *override_configpath = NULL;
bool autostart = true;
+ bool only_check_config = false;
xcb_connection_t *conn;
xcb_property_handlers_t prophs;
xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
start_argv = argv;
- while ((opt = getopt_long(argc, argv, "c:vahld:V", long_options, &option_index)) != -1) {
+ while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) {
switch (opt) {
case 'a':
LOG("Autostart disabled using -a\n");
case 'c':
override_configpath = sstrdup(optarg);
break;
+ case 'C':
+ LOG("Checking configuration file only (-C)\n");
+ only_check_config = true;
+ break;
case 'v':
printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
exit(EXIT_SUCCESS);
die("Cannot open display\n");
load_configuration(conn, override_configpath, false);
+ if (only_check_config) {
+ LOG("Done checking configuration file. Exiting.\n");
+ exit(0);
+ }
/* Create the initial container on the first workspace. This used to
* be part of init_table, but since it possibly requires an X