]> git.sur5r.net Git - i3/i3/blob - src/cfgparse.y
Bugfix: Attach new cons at the correct place when a floating con is focused (+test...
[i3/i3] / src / cfgparse.y
1 %{
2 /*
3  * vim:ts=4:sw=4:expandtab
4  *
5  */
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10
11 #include "all.h"
12
13 typedef struct yy_buffer_state *YY_BUFFER_STATE;
14 extern int yylex(struct context *context);
15 extern int yyparse(void);
16 extern FILE *yyin;
17 YY_BUFFER_STATE yy_scan_string(const char *);
18
19 static struct bindings_head *current_bindings;
20 static struct context *context;
21
22 /* We don’t need yydebug for now, as we got decent error messages using
23  * yyerror(). Should you ever want to extend the parser, it might be handy
24  * to just comment it in again, so it stays here. */
25 //int yydebug = 1;
26
27 void yyerror(const char *error_message) {
28     ELOG("\n");
29     ELOG("CONFIG: %s\n", error_message);
30     ELOG("CONFIG: in file \"%s\", line %d:\n",
31         context->filename, context->line_number);
32     ELOG("CONFIG:   %s\n", context->line_copy);
33     ELOG("CONFIG:   ");
34     for (int c = 1; c <= context->last_column; c++)
35         if (c >= context->first_column)
36             printf("^");
37         else printf(" ");
38     printf("\n");
39     ELOG("\n");
40 }
41
42 int yywrap() {
43     return 1;
44 }
45
46 void parse_file(const char *f) {
47     SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
48     int fd, ret, read_bytes = 0;
49     struct stat stbuf;
50     char *buf;
51     FILE *fstr;
52     char buffer[1026], key[512], value[512];
53
54     if ((fd = open(f, O_RDONLY)) == -1)
55         die("Could not open configuration file: %s\n", strerror(errno));
56
57     if (fstat(fd, &stbuf) == -1)
58         die("Could not fstat file: %s\n", strerror(errno));
59
60     buf = scalloc((stbuf.st_size + 1) * sizeof(char));
61     while (read_bytes < stbuf.st_size) {
62         if ((ret = read(fd, buf + read_bytes, (stbuf.st_size - read_bytes))) < 0)
63             die("Could not read(): %s\n", strerror(errno));
64         read_bytes += ret;
65     }
66
67     if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
68         die("Could not lseek: %s\n", strerror(errno));
69
70     if ((fstr = fdopen(fd, "r")) == NULL)
71         die("Could not fdopen: %s\n", strerror(errno));
72
73     while (!feof(fstr)) {
74         if (fgets(buffer, 1024, fstr) == NULL) {
75             if (feof(fstr))
76                 break;
77             die("Could not read configuration file\n");
78         }
79
80         /* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */
81         if (sscanf(buffer, "%s %[^\n]", key, value) < 1 ||
82             key[0] == '#' || strlen(key) < 3)
83             continue;
84
85         if (strcasecmp(key, "set") == 0) {
86             if (value[0] != '$')
87                 die("Malformed variable assignment, name has to start with $\n");
88
89             /* get key/value for this variable */
90             char *v_key = value, *v_value;
91             if ((v_value = strstr(value, " ")) == NULL)
92                 die("Malformed variable assignment, need a value\n");
93
94             *(v_value++) = '\0';
95
96             struct Variable *new = scalloc(sizeof(struct Variable));
97             new->key = sstrdup(v_key);
98             new->value = sstrdup(v_value);
99             SLIST_INSERT_HEAD(&variables, new, variables);
100             DLOG("Got new variable %s = %s\n", v_key, v_value);
101             continue;
102         }
103     }
104     fclose(fstr);
105
106     /* For every custom variable, see how often it occurs in the file and
107      * how much extra bytes it requires when replaced. */
108     struct Variable *current, *nearest;
109     int extra_bytes = 0;
110     /* We need to copy the buffer because we need to invalidate the
111      * variables (otherwise we will count them twice, which is bad when
112      * 'extra' is negative) */
113     char *bufcopy = sstrdup(buf);
114     SLIST_FOREACH(current, &variables, variables) {
115         int extra = (strlen(current->value) - strlen(current->key));
116         char *next;
117         for (next = bufcopy;
118              (next = strcasestr(bufcopy + (next - bufcopy), current->key)) != NULL;
119              next += strlen(current->key)) {
120             *next = '_';
121             extra_bytes += extra;
122         }
123     }
124     FREE(bufcopy);
125
126     /* Then, allocate a new buffer and copy the file over to the new one,
127      * but replace occurences of our variables */
128     char *walk = buf, *destwalk;
129     char *new = smalloc((stbuf.st_size + extra_bytes + 1) * sizeof(char));
130     destwalk = new;
131     while (walk < (buf + stbuf.st_size)) {
132         /* Find the next variable */
133         SLIST_FOREACH(current, &variables, variables)
134             current->next_match = strcasestr(walk, current->key);
135         nearest = NULL;
136         int distance = stbuf.st_size;
137         SLIST_FOREACH(current, &variables, variables) {
138             if (current->next_match == NULL)
139                 continue;
140             if ((current->next_match - walk) < distance) {
141                 distance = (current->next_match - walk);
142                 nearest = current;
143             }
144         }
145         if (nearest == NULL) {
146             /* If there are no more variables, we just copy the rest */
147             strncpy(destwalk, walk, (buf + stbuf.st_size) - walk);
148             destwalk += (buf + stbuf.st_size) - walk;
149             *destwalk = '\0';
150             break;
151         } else {
152             /* Copy until the next variable, then copy its value */
153             strncpy(destwalk, walk, distance);
154             strncpy(destwalk + distance, nearest->value, strlen(nearest->value));
155             walk += distance + strlen(nearest->key);
156             destwalk += distance + strlen(nearest->value);
157         }
158     }
159
160     yy_scan_string(new);
161
162     context = scalloc(sizeof(struct context));
163     context->filename = f;
164
165     if (yyparse() != 0) {
166         fprintf(stderr, "Could not parse configfile\n");
167         exit(1);
168     }
169
170     FREE(context->line_copy);
171     free(context);
172     free(new);
173     free(buf);
174
175     while (!SLIST_EMPTY(&variables)) {
176         current = SLIST_FIRST(&variables);
177         FREE(current->key);
178         FREE(current->value);
179         SLIST_REMOVE_HEAD(&variables, variables);
180         FREE(current);
181     }
182 }
183
184 %}
185
186 %expect 1
187 %error-verbose
188 %lex-param { struct context *context }
189
190 %union {
191     int number;
192     char *string;
193     uint32_t *single_color;
194     struct Colortriple *color;
195     Match *match;
196     struct Binding *binding;
197 }
198
199 %token  <number>        NUMBER                      "<number>"
200 %token  <string>        WORD                        "<word>"
201 %token  <string>        STR                         "<string>"
202 %token  <string>        STR_NG                      "<string (non-greedy)>"
203 %token  <string>        HEX                         "<hex>"
204 %token  <string>        OUTPUT                      "<RandR output>"
205 %token                  TOKBINDCODE
206 %token                  TOKTERMINAL
207 %token                  TOKCOMMENT                  "<comment>"
208 %token                  TOKFONT                     "font"
209 %token                  TOKBINDSYM                  "bindsym"
210 %token  <number>        MODIFIER                    "<modifier>"
211 %token                  TOKCONTROL                  "control"
212 %token                  TOKSHIFT                    "shift"
213 %token                  WHITESPACE                  "<whitespace>"
214 %token                  TOKFLOATING_MODIFIER        "floating_modifier"
215 %token  <string>        QUOTEDSTRING                "<quoted string>"
216 %token                  TOKWORKSPACE                "workspace"
217 %token                  TOKOUTPUT                   "output"
218 %token                  TOKASSIGN                   "assign"
219 %token                  TOKSET
220 %token                  TOKIPCSOCKET                "ipc_socket"
221 %token                  TOKRESTARTSTATE             "restart_state"
222 %token                  TOKEXEC                     "exec"
223 %token  <single_color>  TOKSINGLECOLOR
224 %token  <color>         TOKCOLOR
225 %token                  TOKARROW                    "→"
226 %token                  TOKMODE                     "mode"
227 %token                  TOK_ORIENTATION             "default_orientation"
228 %token                  TOK_HORIZ                   "horizontal"
229 %token                  TOK_VERT                    "vertical"
230 %token                  TOK_AUTO                    "auto"
231 %token                  TOK_WORKSPACE_LAYOUT        "workspace_layout"
232 %token                  TOKNEWWINDOW                "new_window"
233 %token                  TOK_NORMAL                  "normal"
234 %token                  TOK_NONE                    "none"
235 %token                  TOK_1PIXEL                  "1pixel"
236 %token                  TOKFOCUSFOLLOWSMOUSE        "focus_follows_mouse"
237 %token                  TOKWORKSPACEBAR             "workspace_bar"
238 %token                  TOK_DEFAULT                 "default"
239 %token                  TOK_STACKING                "stacking"
240 %token                  TOK_TABBED                  "tabbed"
241 %token  <number>        TOKSTACKLIMIT               "stack-limit"
242 %token                  TOK_POPUP_DURING_FULLSCREEN "popup_during_fullscreen"
243 %token                  TOK_IGNORE                  "ignore"
244 %token                  TOK_LEAVE_FULLSCREEN        "leave_fullscreen"
245
246 %type   <binding>       binding
247 %type   <binding>       bindcode
248 %type   <binding>       bindsym
249 %type   <number>        binding_modifiers
250 %type   <number>        binding_modifier
251 %type   <number>        direction
252 %type   <number>        layout_mode
253 %type   <number>        border_style
254 %type   <number>        new_window
255 %type   <number>        colorpixel
256 %type   <number>        bool
257 %type   <number>        popup_setting
258 %type   <string>        command
259 %type   <string>        word_or_number
260 %type   <string>        optional_workspace_name
261 %type   <string>        workspace_name
262 %type   <string>        window_class
263 %type   <match>         assign_target
264
265 %%
266
267 lines: /* empty */
268     | lines WHITESPACE line
269     | lines error
270     | lines line
271     ;
272
273 line:
274     bindline
275     | mode
276     | floating_modifier
277     | orientation
278     | workspace_layout
279     | new_window
280     | focus_follows_mouse
281     | workspace_bar
282     | workspace
283     | assign
284     | ipcsocket
285     | restart_state
286     | exec
287     | single_color
288     | color
289     | terminal
290     | font
291     | comment
292     | popup_during_fullscreen
293     ;
294
295 comment:
296     TOKCOMMENT
297     ;
298
299 command:
300     STR
301     ;
302
303 bindline:
304     binding
305     {
306         TAILQ_INSERT_TAIL(bindings, $1, bindings);
307     }
308     ;
309
310 binding:
311     TOKBINDCODE WHITESPACE bindcode         { $$ = $3; }
312     | TOKBINDSYM WHITESPACE bindsym         { $$ = $3; }
313     ;
314
315 bindcode:
316     binding_modifiers NUMBER WHITESPACE command
317     {
318         printf("\tFound keycode binding mod%d with key %d and command %s\n", $1, $2, $4);
319         Binding *new = scalloc(sizeof(Binding));
320
321         new->keycode = $2;
322         new->mods = $1;
323         new->command = $4;
324
325         $$ = new;
326     }
327     ;
328
329 bindsym:
330     binding_modifiers word_or_number WHITESPACE command
331     {
332         printf("\tFound keysym binding mod%d with key %s and command %s\n", $1, $2, $4);
333         Binding *new = scalloc(sizeof(Binding));
334
335         new->symbol = $2;
336         new->mods = $1;
337         new->command = $4;
338
339         $$ = new;
340     }
341     ;
342
343 word_or_number:
344     WORD
345     | NUMBER
346     {
347         asprintf(&$$, "%d", $1);
348     }
349     ;
350
351 mode:
352     TOKMODE WHITESPACE QUOTEDSTRING WHITESPACE '{' modelines '}'
353     {
354         if (strcasecmp($3, "default") == 0) {
355             printf("You cannot use the name \"default\" for your mode\n");
356             exit(1);
357         }
358         printf("\t now in mode %s\n", $3);
359         printf("\t current bindings = %p\n", current_bindings);
360         Binding *binding;
361         TAILQ_FOREACH(binding, current_bindings, bindings) {
362             printf("got binding on mods %d, keycode %d, symbol %s, command %s\n",
363                             binding->mods, binding->keycode, binding->symbol, binding->command);
364         }
365
366         struct Mode *mode = scalloc(sizeof(struct Mode));
367         mode->name = $3;
368         mode->bindings = current_bindings;
369         current_bindings = NULL;
370         SLIST_INSERT_HEAD(&modes, mode, modes);
371     }
372     ;
373
374
375 modelines:
376     /* empty */
377     | modelines modeline
378     ;
379
380 modeline:
381     WHITESPACE
382     | comment
383     | binding
384     {
385         if (current_bindings == NULL) {
386             current_bindings = scalloc(sizeof(struct bindings_head));
387             TAILQ_INIT(current_bindings);
388         }
389
390         TAILQ_INSERT_TAIL(current_bindings, $1, bindings);
391     }
392     ;
393
394 floating_modifier:
395     TOKFLOATING_MODIFIER WHITESPACE binding_modifiers
396     {
397         DLOG("floating modifier = %d\n", $3);
398         config.floating_modifier = $3;
399     }
400     ;
401
402 orientation:
403     TOK_ORIENTATION WHITESPACE direction
404     {
405         DLOG("New containers should start with split direction %d\n", $3);
406         config.default_orientation = $3;
407     }
408     ;
409
410 direction:
411     TOK_HORIZ       { $$ = HORIZ; }
412     | TOK_VERT      { $$ = VERT; }
413     | TOK_AUTO      { $$ = NO_ORIENTATION; }
414     ;
415
416 workspace_layout:
417     TOK_WORKSPACE_LAYOUT WHITESPACE layout_mode
418     {
419         DLOG("new containers will be in mode %d\n", $3);
420         config.default_layout = $3;
421
422 #if 0
423         /* We also need to change the layout of the already existing
424          * workspaces here. Workspaces may exist at this point because
425          * of the other directives which are modifying workspaces
426          * (setting the preferred screen or name). While the workspace
427          * objects are already created, they have never been used.
428          * Thus, the user very likely awaits the default container mode
429          * to trigger in this case, regardless of where it is inside
430          * his configuration file. */
431         Workspace *ws;
432         TAILQ_FOREACH(ws, workspaces, workspaces) {
433                 if (ws->table == NULL)
434                         continue;
435                 switch_layout_mode(global_conn,
436                                    ws->table[0][0],
437                                    config.container_mode);
438         }
439 #endif
440     }
441     | TOK_WORKSPACE_LAYOUT WHITESPACE TOKSTACKLIMIT WHITESPACE TOKSTACKLIMIT WHITESPACE NUMBER
442     {
443         DLOG("stack-limit %d with val %d\n", $5, $7);
444         config.container_stack_limit = $5;
445         config.container_stack_limit_value = $7;
446
447 #if 0
448         /* See the comment above */
449         Workspace *ws;
450         TAILQ_FOREACH(ws, workspaces, workspaces) {
451                 if (ws->table == NULL)
452                         continue;
453                 Container *con = ws->table[0][0];
454                 con->stack_limit = config.container_stack_limit;
455                 con->stack_limit_value = config.container_stack_limit_value;
456         }
457 #endif
458     }
459     ;
460
461 layout_mode:
462     TOK_DEFAULT       { $$ = L_DEFAULT; }
463     | TOK_STACKING    { $$ = L_STACKED; }
464     | TOK_TABBED      { $$ = L_TABBED; }
465     ;
466
467 new_window:
468     TOKNEWWINDOW WHITESPACE border_style
469     {
470         DLOG("new windows should start with border style %d\n", $3);
471         config.default_border = $3;
472     }
473     ;
474
475 border_style:
476     TOK_NORMAL      { $$ = BS_NORMAL; }
477     | TOK_NONE      { $$ = BS_NONE; }
478     | TOK_1PIXEL    { $$ = BS_1PIXEL; }
479     ;
480
481 bool:
482     NUMBER
483     {
484         $$ = ($1 == 1);
485     }
486     | WORD
487     {
488         DLOG("checking word \"%s\"\n", $1);
489         $$ = (strcasecmp($1, "yes") == 0 ||
490               strcasecmp($1, "true") == 0 ||
491               strcasecmp($1, "on") == 0 ||
492               strcasecmp($1, "enable") == 0 ||
493               strcasecmp($1, "active") == 0);
494     }
495     ;
496
497 focus_follows_mouse:
498     TOKFOCUSFOLLOWSMOUSE WHITESPACE bool
499     {
500         DLOG("focus follows mouse = %d\n", $3);
501         config.disable_focus_follows_mouse = !($3);
502     }
503     ;
504
505 workspace_bar:
506     TOKWORKSPACEBAR WHITESPACE bool
507     {
508         DLOG("workspace bar = %d\n", $3);
509         config.disable_workspace_bar = !($3);
510     }
511     ;
512
513 workspace:
514     TOKWORKSPACE WHITESPACE NUMBER WHITESPACE TOKOUTPUT WHITESPACE OUTPUT optional_workspace_name
515     {
516         int ws_num = $3;
517         if (ws_num < 1) {
518             DLOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
519         } else {
520 #if 0
521             Workspace *ws = workspace_get(ws_num - 1);
522             ws->preferred_output = $<string>7;
523             if ($<string>8 != NULL) {
524                     workspace_set_name(ws, $<string>8);
525                     free($<string>8);
526             }
527 #endif
528         }
529     }
530     | TOKWORKSPACE WHITESPACE NUMBER WHITESPACE workspace_name
531     {
532         int ws_num = $3;
533         if (ws_num < 1) {
534             DLOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
535         } else {
536             DLOG("workspace name to: %s\n", $5);
537 #if 0
538             if ($<string>5 != NULL) {
539                     workspace_set_name(workspace_get(ws_num - 1), $<string>5);
540                     free($<string>5);
541             }
542 #endif
543         }
544     }
545     ;
546
547 optional_workspace_name:
548     /* empty */                     { $$ = NULL; }
549     | WHITESPACE workspace_name     { $$ = $2; }
550     ;
551
552 workspace_name:
553     QUOTEDSTRING         { $$ = $1; }
554     | STR                { $$ = $1; }
555     | WORD               { $$ = $1; }
556     ;
557
558 assign:
559     TOKASSIGN WHITESPACE window_class WHITESPACE optional_arrow assign_target
560     {
561         printf("assignment of %s\n", $3);
562
563         struct Match *match = $6;
564
565         char *separator = NULL;
566         if ((separator = strchr($3, '/')) != NULL) {
567             *(separator++) = '\0';
568             match->title = sstrdup(separator);
569         }
570         if (*$3 != '\0')
571             match->class = sstrdup($3);
572         free($3);
573
574         printf("  class = %s\n", match->class);
575         printf("  title = %s\n", match->title);
576         if (match->insert_where == M_ASSIGN_WS)
577             printf("  to ws %s\n", match->target_ws);
578         TAILQ_INSERT_TAIL(&assignments, match, assignments);
579     }
580     ;
581
582 assign_target:
583     NUMBER
584     {
585         /* TODO: named workspaces */
586         Match *match = smalloc(sizeof(Match));
587         match_init(match);
588         match->insert_where = M_ASSIGN_WS;
589         asprintf(&(match->target_ws), "%d", $1);
590         $$ = match;
591     }
592     | '~'
593     {
594         /* TODO: compatiblity */
595 #if 0
596         struct Assignment *new = scalloc(sizeof(struct Assignment));
597         new->floating = ASSIGN_FLOATING_ONLY;
598         $<assignment>$ = new;
599 #endif
600     }
601     | '~' NUMBER
602     {
603         /* TODO: compatiblity */
604 #if 0
605         struct Assignment *new = scalloc(sizeof(struct Assignment));
606         new->workspace = $<number>2;
607         new->floating = ASSIGN_FLOATING;
608         $<assignment>$ = new;
609 #endif
610     }
611     ;
612
613 window_class:
614     QUOTEDSTRING
615     | STR_NG
616     ;
617
618 optional_arrow:
619     /* NULL */
620     | TOKARROW WHITESPACE
621     ;
622
623 ipcsocket:
624     TOKIPCSOCKET WHITESPACE STR
625     {
626         config.ipc_socket_path = $3;
627     }
628     ;
629
630 restart_state:
631     TOKRESTARTSTATE WHITESPACE STR
632     {
633         config.restart_state_path = $3;
634     }
635     ;
636
637 exec:
638     TOKEXEC WHITESPACE STR
639     {
640         struct Autostart *new = smalloc(sizeof(struct Autostart));
641         new->command = $3;
642         TAILQ_INSERT_TAIL(&autostarts, new, autostarts);
643     }
644     ;
645
646 terminal:
647     TOKTERMINAL WHITESPACE STR
648     {
649         ELOG("The terminal option is DEPRECATED and has no effect. "
650             "Please remove it from your configuration file.\n");
651     }
652     ;
653
654 font:
655     TOKFONT WHITESPACE STR
656     {
657         config.font = load_font($3, true);
658         printf("font %s\n", $3);
659     }
660     ;
661
662 single_color:
663     TOKSINGLECOLOR WHITESPACE colorpixel
664     {
665         uint32_t *dest = $1;
666         *dest = $3;
667     }
668     ;
669
670 color:
671     TOKCOLOR WHITESPACE colorpixel WHITESPACE colorpixel WHITESPACE colorpixel
672     {
673         struct Colortriple *dest = $1;
674
675         dest->border = $3;
676         dest->background = $5;
677         dest->text = $7;
678     }
679     ;
680
681 colorpixel:
682     '#' HEX
683     {
684         char *hex;
685         if (asprintf(&hex, "#%s", $2) == -1)
686             die("asprintf()");
687         $$ = get_colorpixel(hex);
688         free(hex);
689     }
690     ;
691
692
693 binding_modifiers:
694     /* NULL */                               { $$ = 0; }
695     | binding_modifier
696     | binding_modifiers '+' binding_modifier { $$ = $1 | $3; }
697     | binding_modifiers '+'                  { $$ = $1; }
698     ;
699
700 binding_modifier:
701     MODIFIER        { $$ = $1; }
702     | TOKCONTROL    { $$ = BIND_CONTROL; }
703     | TOKSHIFT      { $$ = BIND_SHIFT; }
704     ;
705
706 popup_during_fullscreen:
707     TOK_POPUP_DURING_FULLSCREEN WHITESPACE popup_setting
708     {
709         DLOG("popup_during_fullscreen setting: %d\n", $3);
710         config.popup_during_fullscreen = $3;
711     }
712     ;
713
714 popup_setting:
715     TOK_IGNORE              { $$ = PDF_IGNORE; }
716     | TOK_LEAVE_FULLSCREEN  { $$ = PDF_LEAVE_FULLSCREEN; }
717     ;