]> git.sur5r.net Git - i3/i3/blob - src/cfgparse.y
26e00af9ebc26291f24f7f248077d52f87ba6bb8
[i3/i3] / src / cfgparse.y
1 %{
2 /*
3  * vim:ts=8:expandtab
4  *
5  */
6 #include <stdio.h>
7 #include <string.h>
8 #include <xcb/xcb.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <stdlib.h>
14 #include <errno.h>
15
16 #include "data.h"
17 #include "config.h"
18 #include "i3.h"
19 #include "util.h"
20 #include "queue.h"
21 #include "table.h"
22 #include "workspace.h"
23 #include "xcb.h"
24
25 extern int yylex(void);
26 extern FILE *yyin;
27
28 static struct bindings_head *current_bindings;
29
30 int yydebug = 1;
31
32 void yyerror(const char *str) {
33         fprintf(stderr,"error: %s\n",str);
34 }
35
36 int yywrap() {
37         return 1;
38 }
39
40 void parse_file(const char *f) {
41         SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
42         int fd, ret, read_bytes = 0;
43         struct stat stbuf;
44         char *buf;
45         FILE *fstr;
46         char buffer[1026], key[512], value[512];
47
48         if ((fd = open(f, O_RDONLY)) == -1)
49                 die("Could not open configuration file: %s\n", strerror(errno));
50
51         if (fstat(fd, &stbuf) == -1)
52                 die("Could not fstat file: %s\n", strerror(errno));
53
54         buf = smalloc(stbuf.st_size * sizeof(char));
55         while (read_bytes < stbuf.st_size) {
56                 if ((ret = read(fd, buf + read_bytes, (stbuf.st_size - read_bytes))) < 0)
57                         die("Could not read(): %s\n", strerror(errno));
58                 read_bytes += ret;
59         }
60
61         if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
62                 die("Could not lseek: %s\n", strerror(errno));
63
64         if ((fstr = fdopen(fd, "r")) == NULL)
65                 die("Could not fdopen: %s\n", strerror(errno));
66
67         while (!feof(fstr)) {
68                 if (fgets(buffer, 1024, fstr) == NULL) {
69                         if (feof(fstr))
70                                 break;
71                         die("Could not read configuration file\n");
72                 }
73
74                 /* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */
75                 if (sscanf(buffer, "%s %[^\n]", key, value) < 1 ||
76                     key[0] == '#' || strlen(key) < 3)
77                         continue;
78
79                 if (strcasecmp(key, "set") == 0) {
80                         if (value[0] != '$')
81                                 die("Malformed variable assignment, name has to start with $\n");
82
83                         /* get key/value for this variable */
84                         char *v_key = value, *v_value;
85                         if ((v_value = strstr(value, " ")) == NULL)
86                                 die("Malformed variable assignment, need a value\n");
87
88                         *(v_value++) = '\0';
89
90                         struct Variable *new = scalloc(sizeof(struct Variable));
91                         new->key = sstrdup(v_key);
92                         new->value = sstrdup(v_value);
93                         SLIST_INSERT_HEAD(&variables, new, variables);
94                         LOG("Got new variable %s = %s\n", v_key, v_value);
95                         continue;
96                 }
97         }
98
99         /* For every custom variable, see how often it occurs in the file and
100          * how much extra bytes it requires when replaced. */
101         struct Variable *current, *nearest;
102         int extra_bytes = 0;
103         SLIST_FOREACH(current, &variables, variables) {
104                 int extra = (strlen(current->value) - strlen(current->key));
105                 char *next;
106                 for (next = buf;
107                      (next = strcasestr(buf + (next - buf), current->key)) != NULL;
108                      next += strlen(current->key))
109                         extra_bytes += extra;
110         }
111
112         /* Then, allocate a new buffer and copy the file over to the new one,
113          * but replace occurences of our variables */
114         char *walk = buf, *destwalk;
115         char *new = smalloc((stbuf.st_size + extra_bytes) * sizeof(char));
116         destwalk = new;
117         while (walk < (buf + stbuf.st_size)) {
118                 /* Find the next variable */
119                 SLIST_FOREACH(current, &variables, variables)
120                         current->next_match = strcasestr(walk, current->key);
121                 nearest = NULL;
122                 int distance = stbuf.st_size;
123                 SLIST_FOREACH(current, &variables, variables) {
124                         if (current->next_match == NULL)
125                                 continue;
126                         if ((current->next_match - walk) < distance) {
127                                 distance = (current->next_match - walk);
128                                 nearest = current;
129                         }
130                 }
131                 if (nearest == NULL) {
132                         /* If there are no more variables, we just copy the rest */
133                         strncpy(destwalk, walk, (buf + stbuf.st_size) - walk);
134                         destwalk += (buf + stbuf.st_size) - walk;
135                         *destwalk = '\0';
136                         break;
137                 } else {
138                         /* Copy until the next variable, then copy its value */
139                         strncpy(destwalk, walk, distance);
140                         strncpy(destwalk + distance, nearest->value, strlen(nearest->value));
141                         walk += distance + strlen(nearest->key);
142                         destwalk += distance + strlen(nearest->value);
143                 }
144         }
145
146         yy_scan_string(new);
147
148         if (yyparse() != 0) {
149                 fprintf(stderr, "Could not parse configfile\n");
150                 exit(1);
151         }
152
153         free(new);
154         free(buf);
155 }
156
157 %}
158
159 %union {
160         int number;
161         char *string;
162         struct Colortriple *color;
163         struct Assignment *assignment;
164         struct Binding *binding;
165 }
166
167 %token <number>NUMBER
168 %token <string>WORD
169 %token <string>STR
170 %token <string>STR_NG
171 %token <string>HEX
172 %token TOKBIND
173 %token TOKTERMINAL
174 %token TOKCOMMENT
175 %token TOKFONT
176 %token TOKBINDSYM
177 %token MODIFIER
178 %token TOKCONTROL
179 %token TOKSHIFT
180 %token WHITESPACE
181 %token TOKFLOATING_MODIFIER
182 %token QUOTEDSTRING
183 %token TOKWORKSPACE
184 %token TOKSCREEN
185 %token TOKASSIGN
186 %token TOKSET
187 %token TOKIPCSOCKET
188 %token TOKEXEC
189 %token TOKCOLOR
190 %token TOKARROW
191 %token TOKMODE
192 %token TOKNEWCONTAINER
193 %token TOKCONTAINERMODE
194 %token TOKSTACKLIMIT
195
196 %%
197
198 lines: /* empty */
199         | lines WHITESPACE line
200         | lines line
201         ;
202
203 line:
204         bindline
205         | mode
206         | floating_modifier
207         | new_container
208         | workspace
209         | assign
210         | ipcsocket
211         | exec
212         | color
213         | terminal
214         | font
215         | comment
216         ;
217
218 comment:
219         TOKCOMMENT
220         ;
221
222 command:
223         STR
224         ;
225
226 bindline:
227         binding
228         {
229                 TAILQ_INSERT_TAIL(bindings, $<binding>1, bindings);
230         }
231         ;
232
233 binding:
234         TOKBIND WHITESPACE bind                 { $<binding>$ = $<binding>3; }
235         | TOKBINDSYM WHITESPACE bindsym         { $<binding>$ = $<binding>3; }
236         ;
237
238 bind:
239         binding_modifiers NUMBER WHITESPACE command
240         {
241                 printf("\tFound binding mod%d with key %d and command %s\n", $<number>1, $2, $<string>4);
242                 Binding *new = scalloc(sizeof(Binding));
243
244                 new->keycode = $<number>2;
245                 new->mods = $<number>1;
246                 new->command = sstrdup($<string>4);
247
248                 $<binding>$ = new;
249         }
250         ;
251
252 bindsym:
253         binding_modifiers word_or_number WHITESPACE command
254         {
255                 printf("\tFound symbolic mod%d with key %s and command %s\n", $<number>1, $<string>2, $<string>4);
256                 Binding *new = scalloc(sizeof(Binding));
257
258                 new->symbol = sstrdup($<string>2);
259                 new->mods = $<number>1;
260                 new->command = sstrdup($<string>4);
261
262                 $<binding>$ = new;
263         }
264         ;
265
266 word_or_number:
267         WORD
268         | NUMBER
269         {
270                 asprintf(&$<string>$, "%d", $1);
271         }
272         ;
273
274 mode:
275         TOKMODE WHITESPACE QUOTEDSTRING WHITESPACE '{' modelines '}'
276         {
277                 if (strcasecmp($<string>3, "default") == 0) {
278                         printf("You cannot use the name \"default\" for your mode\n");
279                         exit(1);
280                 }
281                 printf("\t now in mode %s\n", $<string>3);
282                 printf("\t current bindings = %p\n", current_bindings);
283                 Binding *binding;
284                 TAILQ_FOREACH(binding, current_bindings, bindings) {
285                         printf("got binding on mods %d, keycode %d, symbol %s, command %s\n",
286                                         binding->mods, binding->keycode, binding->symbol, binding->command);
287                 }
288
289                 struct Mode *mode = scalloc(sizeof(struct Mode));
290                 mode->name = strdup($<string>3);
291                 mode->bindings = current_bindings;
292                 current_bindings = NULL;
293                 SLIST_INSERT_HEAD(&modes, mode, modes);
294         }
295         ;
296
297 modelines:
298         /* empty */
299         | modelines WHITESPACE modeline
300         | modelines modeline
301         ;
302
303 modeline:
304         comment
305         | binding
306         {
307                 if (current_bindings == NULL) {
308                         current_bindings = scalloc(sizeof(struct bindings_head));
309                         TAILQ_INIT(current_bindings);
310                 }
311
312                 TAILQ_INSERT_TAIL(current_bindings, $<binding>1, bindings);
313         }
314         ;
315
316 floating_modifier:
317         TOKFLOATING_MODIFIER WHITESPACE binding_modifiers
318         {
319                 LOG("floating modifier = %d\n", $<number>3);
320                 config.floating_modifier = $<number>3;
321         }
322         ;
323
324 new_container:
325         TOKNEWCONTAINER WHITESPACE TOKCONTAINERMODE
326         {
327                 LOG("new containers will be in mode %d\n", $<number>3);
328                 config.container_mode = $<number>3;
329
330                 /* We also need to change the layout of the already existing
331                  * workspaces here. Workspaces may exist at this point because
332                  * of the other directives which are modifying workspaces
333                  * (setting the preferred screen or name). While the workspace
334                  * objects are already created, they have never been used.
335                  * Thus, the user very likely awaits the default container mode
336                  * to trigger in this case, regardless of where it is inside
337                  * his configuration file. */
338                 for (int c = 0; c < num_workspaces; c++) {
339                         if (workspaces[c].table == NULL)
340                                 continue;
341                         switch_layout_mode(global_conn,
342                                            workspaces[c].table[0][0],
343                                            config.container_mode);
344                 }
345         }
346         | TOKNEWCONTAINER WHITESPACE TOKSTACKLIMIT WHITESPACE TOKSTACKLIMIT WHITESPACE NUMBER
347         {
348                 LOG("stack-limit %d with val %d\n", $<number>5, $<number>7);
349                 config.container_stack_limit = $<number>5;
350                 config.container_stack_limit_value = $<number>7;
351
352                 /* See the comment above */
353                 for (int c = 0; c < num_workspaces; c++) {
354                         if (workspaces[c].table == NULL)
355                                 continue;
356                         Container *con = workspaces[c].table[0][0];
357                         con->stack_limit = config.container_stack_limit;
358                         con->stack_limit_value = config.container_stack_limit_value;
359                 }
360         }
361         ;
362
363 workspace:
364         TOKWORKSPACE WHITESPACE NUMBER WHITESPACE TOKSCREEN WHITESPACE screen workspace_name
365         {
366                 int ws_num = $<number>3;
367                 if (ws_num < 1) {
368                         LOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
369                 } else {
370                         Workspace *ws = workspace_get(ws_num - 1);
371                         ws->preferred_screen = sstrdup($<string>7);
372                         if ($<string>8 != NULL)
373                                 workspace_set_name(ws, $<string>8);
374                 }
375         }
376         | TOKWORKSPACE WHITESPACE NUMBER workspace_name
377         {
378                 int ws_num = $<number>3;
379                 if (ws_num < 1) {
380                         LOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num);
381                 } else {
382                         if ($<string>4 != NULL)
383                                 workspace_set_name(workspace_get(ws_num - 1), $<string>4);
384                 }
385         }
386         ;
387
388 workspace_name:
389         /* NULL */                      { $<string>$ = NULL; }
390         | WHITESPACE QUOTEDSTRING       { $<string>$ = $<string>2; }
391         | WHITESPACE STR                { $<string>$ = $<string>2; }
392         ;
393
394 screen:
395         NUMBER              { asprintf(&$<string>$, "%d", $<number>1); }
396         | NUMBER 'x'        { asprintf(&$<string>$, "%d", $<number>1); }
397         | NUMBER 'x' NUMBER { asprintf(&$<string>$, "%dx%d", $<number>1, $<number>3); }
398         | 'x' NUMBER        { asprintf(&$<string>$, "x%d", $<number>2); }
399         ;
400
401 assign:
402         TOKASSIGN WHITESPACE window_class WHITESPACE optional_arrow assign_target
403         {
404                 printf("assignment of %s to %d\n", $<string>3, $<number>6);
405
406                 struct Assignment *new = $<assignment>6;
407                 new->windowclass_title = strdup($<string>3);
408                 TAILQ_INSERT_TAIL(&assignments, new, assignments);
409         }
410         ;
411
412 assign_target:
413         NUMBER
414         {
415                 struct Assignment *new = scalloc(sizeof(struct Assignment));
416                 new->workspace = $<number>1;
417                 new->floating = ASSIGN_FLOATING_NO;
418                 $<assignment>$ = new;
419         }
420         | '~'
421         {
422                 struct Assignment *new = scalloc(sizeof(struct Assignment));
423                 new->floating = ASSIGN_FLOATING_ONLY;
424                 $<assignment>$ = new;
425         }
426         | '~' NUMBER
427         {
428                 struct Assignment *new = scalloc(sizeof(struct Assignment));
429                 new->workspace = $<number>2;
430                 new->floating = ASSIGN_FLOATING;
431                 $<assignment>$ = new;
432         }
433         ;
434
435 window_class:
436         QUOTEDSTRING
437         | STR_NG
438         ;
439
440 optional_arrow:
441         /* NULL */
442         | TOKARROW WHITESPACE
443         ;
444
445 ipcsocket:
446         TOKIPCSOCKET WHITESPACE STR
447         {
448                 config.ipc_socket_path = sstrdup($<string>3);
449         }
450         ;
451
452 exec:
453         TOKEXEC WHITESPACE STR
454         {
455                 struct Autostart *new = smalloc(sizeof(struct Autostart));
456                 new->command = sstrdup($<string>3);
457                 TAILQ_INSERT_TAIL(&autostarts, new, autostarts);
458         }
459         ;
460
461 terminal:
462         TOKTERMINAL WHITESPACE STR
463         {
464                 config.terminal = sstrdup($<string>3);
465                 printf("terminal %s\n", config.terminal);
466         }
467         ;
468
469 font:
470         TOKFONT WHITESPACE STR
471         {
472                 config.font = sstrdup($<string>3);
473                 printf("font %s\n", config.font);
474         }
475         ;
476
477
478 color:
479         TOKCOLOR WHITESPACE colorpixel WHITESPACE colorpixel WHITESPACE colorpixel
480         {
481                 struct Colortriple *dest = $<color>1;
482
483                 dest->border = $<number>3;
484                 dest->background = $<number>5;
485                 dest->text = $<number>7;
486         }
487         ;
488
489 colorpixel:
490         '#' HEX         { $<number>$ = get_colorpixel(global_conn, $<string>2); }
491         ;
492
493
494 binding_modifiers:
495         /* NULL */                               { $<number>$ = 0; }
496         | binding_modifier
497         | binding_modifiers '+' binding_modifier { $<number>$ = $<number>1 | $<number>3; }
498         | binding_modifiers '+'                  { $<number>$ = $<number>1; }
499         ;
500
501 binding_modifier:
502         MODIFIER        { $<number>$ = $<number>1; }
503         | TOKCONTROL    { $<number>$ = BIND_CONTROL; }
504         | TOKSHIFT      { $<number>$ = BIND_SHIFT; }
505         ;