include $(TOPDIR)/common.mk
# Depend on the object files of all source-files in src/*.c and on all header files
-AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c
+AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c src/cmdparse.tab.c src/cmdparse.yy.c
FILES:=src/ipc.c src/nc.c src/log.c src/util.c src/tree.c src/xcb.c src/manage.c src/workspace.c src/x.c src/floating.c src/click.c src/config.c src/handlers.c src/randr.c src/xinerama.c src/con.c src/load_layout.c src/render.c src/window.c
FILES:=$(FILES:.c=.o)
HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h))
echo "CC $<"
$(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/$(shell basename $< .c)/ { print NR }' loglevels.tmp))" -c -o $@ $<
-all: src/cfgparse.y.o src/cfgparse.yy.o ${FILES}
+all: src/cfgparse.y.o src/cfgparse.yy.o src/cmdparse.y.o src/cmdparse.yy.o ${FILES}
echo "LINK i3"
$(CC) -o i3 $^ $(LDFLAGS)
flex -i -o$(@:.o=.c) $<
$(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c)
+src/cmdparse.yy.o: src/cmdparse.l src/cmdparse.y.o ${HEADERS}
+ echo "LEX $<"
+ flex -P cmdyy -i -o$(@:.o=.c) $<
+ $(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cmdparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c)
+
+
src/cfgparse.y.o: src/cfgparse.y ${HEADERS}
echo "YACC $<"
bison --debug --verbose -b $(basename $< .y) -d $<
$(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c)
+src/cmdparse.y.o: src/cmdparse.y ${HEADERS}
+ echo "YACC $<"
+ bison -p cmdyy --debug --verbose -b $(basename $< .y) -d $<
+ $(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cmdparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c)
+
+
install: all
echo "INSTALL"
$(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
--- /dev/null
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * cmdparse.l: the lexer for commands you send to i3 (or bind on keys)
+ *
+ */
+%option nounput
+%option noinput
+%option noyy_top_state
+%option stack
+
+%{
+#include <stdio.h>
+#include <string.h>
+#include "cmdparse.tab.h"
+
+#include "config.h"
+#include "util.h"
+
+int cmdyycolumn = 1;
+
+#define YY_DECL int yylex (struct context *context)
+
+#define YY_USER_ACTION { \
+ context->first_column = cmdyycolumn; \
+ context->last_column = cmdyycolumn+yyleng-1; \
+ cmdyycolumn += yyleng; \
+}
+
+%}
+
+EOL (\r?\n)
+
+/* handle everything up to \n as a string */
+%s WANT_STRING
+/* first expect a whitespace, then a string */
+%s WANT_WS_STRING
+/* handle a quoted string or everything up to the next whitespace */
+%s WANT_QSTRING
+
+%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 = sstrdup(yytext);
+
+ yyless(0);
+ yy_pop_state();
+ yy_set_bol(true);
+ cmdyycolumn = 1;
+}
+
+<WANT_STRING>[^\n]+ { BEGIN(INITIAL); cmdyylval.string = sstrdup(yytext); return STR; }
+<WANT_WS_STRING>[ \t]* { BEGIN(WANT_STRING); return WHITESPACE; }
+<WANT_QSTRING>\"[^\"]+\" {
+ BEGIN(INITIAL);
+ /* strip quotes */
+ char *copy = sstrdup(yytext+1);
+ copy[strlen(copy)-1] = '\0';
+ cmdyylval.string = copy;
+ return STR;
+ }
+
+[ \t]* { return WHITESPACE; }
+attach { return TOK_ATTACH; }
+exec { BEGIN(WANT_WS_STRING); return TOK_EXEC; }
+exit { return TOK_EXIT; }
+reload { return TOK_RELOAD; }
+restart { return TOK_RESTART; }
+kill { return TOK_KILL; }
+fullscreen { return TOK_FULLSCREEN; }
+global { return TOK_GLOBAL; }
+layout { return TOK_LAYOUT; }
+default { return TOK_DEFAULT; }
+stacked { return TOK_STACKED; }
+tabbed { return TOK_TABBED; }
+border { return TOK_BORDER; }
+none { return TOK_NONE; }
+1pixel { return TOK_1PIXEL; }
+mode { return TOK_MODE; }
+tiling { return TOK_TILING; }
+floating { return TOK_FLOATING; }
+workspace { return TOK_WORKSPACE; }
+focus { return TOK_FOCUS; }
+move { return TOK_MOVE; }
+
+class { BEGIN(WANT_QSTRING); return TOK_CLASS; }
+
+. { return (int)yytext[0]; }
+
+<<EOF>> {
+ while (yy_start_stack_ptr > 0)
+ yy_pop_state();
+ yyterminate();
+}
+
+%%
--- /dev/null
+%{
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * cmdparse.y: the parser for commands you send to i3 (or bind on keys)
+ *
+
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "all.h"
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+extern int cmdyylex(struct context *context);
+extern int cmdyyparse(void);
+extern FILE *cmdyyin;
+YY_BUFFER_STATE cmdyy_scan_string(const char *);
+
+static struct bindings_head *current_bindings;
+static struct context *context;
+static Match current_match;
+
+/*
+ * Helper data structure for an operation window (window on which the operation
+ * will be performed). Used to build the TAILQ owindows.
+ *
+ */
+typedef struct owindow {
+ Con *con;
+ TAILQ_ENTRY(owindow) owindows;
+} owindow;
+static TAILQ_HEAD(owindows_head, owindow) owindows;
+
+/* 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 cmdyydebug = 1;
+
+void cmdyyerror(const char *error_message) {
+ ELOG("\n");
+ ELOG("CMD: %s\n", error_message);
+ ELOG("CMD: in file \"%s\", line %d:\n",
+ context->filename, context->line_number);
+ ELOG("CMD: %s\n", context->line_copy);
+ ELOG("CMD: ");
+ for (int c = 1; c <= context->last_column; c++)
+ if (c >= context->first_column)
+ printf("^");
+ else printf(" ");
+ printf("\n");
+ ELOG("\n");
+}
+
+int cmdyywrap() {
+ return 1;
+}
+
+void parse_cmd(const char *new) {
+
+ //const char *new = "[level-up workspace] attach $output, focus";
+
+ cmdyy_scan_string(new);
+
+ context = scalloc(sizeof(struct context));
+ context->filename = "cmd";
+ if (cmdyyparse() != 0) {
+ fprintf(stderr, "Could not parse configfile\n");
+ exit(1);
+ }
+ printf("done\n");
+
+ FREE(context->line_copy);
+ free(context);
+}
+
+%}
+
+%error-verbose
+%lex-param { struct context *context }
+
+%union {
+ char *string;
+}
+
+%token TOK_ATTACH "attach"
+%token TOK_EXEC "exec"
+%token TOK_EXIT "exit"
+%token TOK_RELOAD "reload"
+%token TOK_RESTART "restart"
+%token TOK_KILL "kill"
+%token TOK_FULLSCREEN "fullscreen"
+%token TOK_GLOBAL "global"
+%token TOK_LAYOUT "layout"
+%token TOK_DEFAULT "default"
+%token TOK_STACKED "stacked"
+%token TOK_TABBED "tabbed"
+%token TOK_BORDER "border"
+%token TOK_NONE "none"
+%token TOK_1PIXEL "1pixel"
+%token TOK_MODE "mode"
+%token TOK_TILING "tiling"
+%token TOK_FLOATING "floating"
+%token TOK_WORKSPACE "workspace"
+%token TOK_FOCUS "focus"
+%token TOK_MOVE "move"
+
+%token TOK_CLASS "class"
+
+%token WHITESPACE "<whitespace>"
+%token STR "<string>"
+
+%%
+
+commands: /* empty */
+ | commands optwhitespace ';' optwhitespace command
+ | command
+ {
+ owindow *current;
+
+ printf("single command completely parsed, dropping state...\n");
+ while (!TAILQ_EMPTY(&owindows)) {
+ current = TAILQ_FIRST(&owindows);
+ TAILQ_REMOVE(&owindows, current, owindows);
+ free(current);
+ }
+ }
+ ;
+
+optwhitespace:
+ | WHITESPACE
+ ;
+
+command:
+ match optwhitespace operations
+ ;
+
+match:
+ | matchstart optwhitespace criteria optwhitespace matchend
+ {
+ printf("match parsed\n");
+ }
+ ;
+
+matchstart:
+ '['
+ {
+ printf("start\n");
+ memset(¤t_match, '\0', sizeof(Match));
+ TAILQ_INIT(&owindows);
+ /* copy all_cons */
+ Con *con;
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
+ if (con->window == NULL)
+ continue;
+
+ owindow *ow = smalloc(sizeof(owindow));
+ ow->con = con;
+ TAILQ_INSERT_TAIL(&owindows, ow, owindows);
+ }
+ }
+ ;
+
+matchend:
+ ']'
+ {
+ owindow *next, *current;
+
+ printf("match specification finished, matching...\n");
+ /* copy the old list head to iterate through it and start with a fresh
+ * list which will contain only matching windows */
+ struct owindows_head old = owindows;
+ TAILQ_INIT(&owindows);
+ for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
+ /* make a copy of the next pointer and advance the pointer to the
+ * next element as we are going to invalidate the element’s
+ * next/prev pointers by calling TAILQ_INSERT_TAIL later */
+ current = next;
+ next = TAILQ_NEXT(next, owindows);
+
+ printf("checking if con %p / %s matches\n", current->con, current->con->name);
+ if (match_matches_window(¤t_match, current->con->window)) {
+ printf("matches!\n");
+ TAILQ_INSERT_TAIL(&owindows, current, owindows);
+ } else {
+ printf("doesnt match\n");
+ free(current);
+ }
+ }
+
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ printf("matching: %p / %s\n", current->con, current->con->name);
+ }
+
+ }
+ ;
+
+criteria:
+ TOK_CLASS '=' STR
+ {
+ printf("criteria: class = %s\n", $<string>3);
+ current_match.class = $<string>3;
+ }
+ ;
+
+operations:
+ operation
+ | operation optwhitespace
+ | operations ',' optwhitespace operation
+ ;
+
+operation:
+ exec
+ | exit
+ /*| reload
+ | restart
+ | mark
+ | fullscreen
+ | layout
+ | border
+ | mode
+ | workspace
+ | move*/
+ | attach
+ | focus
+ | kill
+ ;
+
+exec:
+ TOK_EXEC WHITESPACE STR
+ {
+ printf("should execute %s\n", $<string>3);
+ }
+ ;
+
+exit:
+ TOK_EXIT
+ {
+ printf("exit, bye bye\n");
+ }
+ ;
+
+attach:
+ TOK_ATTACH
+ {
+ printf("should attach\n");
+ }
+ ;
+
+focus:
+ TOK_FOCUS
+ {
+ printf("should focus\n");
+ }
+ ;
+
+kill:
+ TOK_KILL
+ {
+ owindow *current;
+
+ printf("killing!\n");
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ printf("matching: %p / %s\n", current->con, current->con->name);
+ tree_close(current->con);
+ }
+
+ }
+ ;