💾 Archived View for gemini.rmf-dev.com › repo › Vaati › cwm › files › a451da5e401ad595a2240d18ce878a… captured on 2023-01-29 at 04:31:41. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /* $OpenBSD$ */
1
2 /*
3 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
4 * Copyright (c) 2001 Markus Friedl. All rights reserved.
5 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
6 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21 %{
22
23 #include <sys/types.h>
24 #include <sys/queue.h>
25
26 #include <ctype.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <limits.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "calmwm.h"
36
37 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
38 static struct file {
39 TAILQ_ENTRY(file) entry;
40 FILE *stream;
41 char *name;
42 int lineno;
43 int errors;
44 } *file, *topfile;
45 struct file *pushfile(const char *, FILE *);
46 int popfile(void);
47 int yyparse(void);
48 int yylex(void);
49 int yyerror(const char *, ...)
50 __attribute__((__format__ (printf, 1, 2)))
51 __attribute__((__nonnull__ (1)));
52 int kw_cmp(const void *, const void *);
53 int lookup(char *);
54 int lgetc(int);
55 int lungetc(int);
56 int findeol(void);
57
58 static struct conf *conf;
59
60 typedef struct {
61 union {
62 int64_t number;
63 char *string;
64 } v;
65 int lineno;
66 } YYSTYPE;
67
68 %}
69
70 %token BINDKEY UNBINDKEY BINDMOUSE UNBINDMOUSE
71 %token FONTNAME STICKY GAP
72 %token AUTOGROUP COMMAND IGNORE WM
73 %token YES NO BORDERWIDTH MOVEAMOUNT HTILE VTILE
74 %token COLOR SNAPDIST
75 %token ACTIVEBORDER INACTIVEBORDER URGENCYBORDER
76 %token GROUPBORDER UNGROUPBORDER
77 %token MENUBG MENUFG
78 %token FONTCOLOR FONTSELCOLOR
79 %token ERROR
80 %token <v.string> STRING
81 %token <v.number> NUMBER
82 %type <v.number> yesno
83 %type <v.string> string numberstring
84 %%
85
86 grammar : /* empty */
87 | grammar '\n'
88 | grammar main '\n'
89 | grammar color '\n'
90 | grammar error '\n' { file->errors++; }
91 ;
92
93 string : string STRING {
94 if (asprintf(&$, "%s %s", $1, $2) == -1) {
95 free($1);
96 free($2);
97 yyerror("string: asprintf");
98 YYERROR;
99 }
100 free($1);
101 free($2);
102 }
103 | STRING
104 ;
105
106 numberstring : NUMBER {
107 char *s;
108 if (asprintf(&s, "%lld", $1) == -1) {
109 yyerror("string: asprintf");
110 YYERROR;
111 }
112 $ = s;
113 }
114 | STRING
115 ;
116
117 yesno : YES { $ = 1; }
118 | NO { $ = 0; }
119 ;
120
121 main : FONTNAME STRING {
122 free(conf->font);
123 conf->font = $2;
124 }
125 | STICKY yesno {
126 conf->stickygroups = $2;
127 }
128 | BORDERWIDTH NUMBER {
129 if ($2 < 0 || $2 > INT_MAX) {
130 yyerror("invalid borderwidth");
131 YYERROR;
132 }
133 conf->bwidth = $2;
134 }
135 | HTILE NUMBER {
136 if ($2 < 0 || $2 > 99) {
137 yyerror("invalid htile percent");
138 YYERROR;
139 }
140 conf->htile = $2;
141 }
142 | VTILE NUMBER {
143 if ($2 < 0 || $2 > 99) {
144 yyerror("invalid vtile percent");
145 YYERROR;
146 }
147 conf->vtile = $2;
148 }
149 | MOVEAMOUNT NUMBER {
150 if ($2 < 0 || $2 > INT_MAX) {
151 yyerror("invalid movemount");
152 YYERROR;
153 }
154 conf->mamount = $2;
155 }
156 | SNAPDIST NUMBER {
157 if ($2 < 0 || $2 > INT_MAX) {
158 yyerror("invalid snapdist");
159 YYERROR;
160 }
161 conf->snapdist = $2;
162 }
163 | COMMAND STRING string {
164 if (strlen($3) >= PATH_MAX) {
165 yyerror("%s command path too long", $2);
166 free($2);
167 free($3);
168 YYERROR;
169 }
170 conf_cmd_add(conf, $2, $3);
171 free($2);
172 free($3);
173 }
174 | WM STRING string {
175 if (strlen($3) >= PATH_MAX) {
176 yyerror("%s wm path too long", $2);
177 free($2);
178 free($3);
179 YYERROR;
180 }
181 conf_wm_add(conf, $2, $3);
182 free($2);
183 free($3);
184 }
185 | AUTOGROUP NUMBER STRING {
186 if ($2 < 0 || $2 > 9) {
187 yyerror("invalid autogroup");
188 free($3);
189 YYERROR;
190 }
191 conf_autogroup(conf, $2, NULL, $3);
192 free($3);
193 }
194 | AUTOGROUP NUMBER STRING ',' STRING {
195 if ($2 < 0 || $2 > 9) {
196 yyerror("invalid autogroup");
197 free($3);
198 free($5);
199 YYERROR;
200 }
201 conf_autogroup(conf, $2, $3, $5);
202 free($3);
203 free($5);
204 }
205 | IGNORE STRING {
206 conf_ignore(conf, $2);
207 free($2);
208 }
209 | GAP NUMBER NUMBER NUMBER NUMBER {
210 if ($2 < 0 || $2 > INT_MAX ||
211 $3 < 0 || $3 > INT_MAX ||
212 $4 < 0 || $4 > INT_MAX ||
213 $5 < 0 || $5 > INT_MAX) {
214 yyerror("invalid gap");
215 YYERROR;
216 }
217 conf->gap.top = $2;
218 conf->gap.bottom = $3;
219 conf->gap.left = $4;
220 conf->gap.right = $5;
221 }
222 | BINDKEY numberstring string {
223 if (!conf_bind_key(conf, $2, $3)) {
224 yyerror("invalid bind-key: %s %s", $2, $3);
225 free($2);
226 free($3);
227 YYERROR;
228 }
229 free($2);
230 free($3);
231 }
232 | UNBINDKEY numberstring {
233 if (!conf_bind_key(conf, $2, NULL)) {
234 yyerror("invalid unbind-key: %s", $2);
235 free($2);
236 YYERROR;
237 }
238 free($2);
239 }
240 | BINDMOUSE numberstring string {
241 if (!conf_bind_mouse(conf, $2, $3)) {
242 yyerror("invalid bind-mouse: %s %s", $2, $3);
243 free($2);
244 free($3);
245 YYERROR;
246 }
247 free($2);
248 free($3);
249 }
250 | UNBINDMOUSE numberstring {
251 if (!conf_bind_mouse(conf, $2, NULL)) {
252 yyerror("invalid unbind-mouse: %s", $2);
253 free($2);
254 YYERROR;
255 }
256 free($2);
257 }
258 ;
259
260 color : COLOR colors
261 ;
262
263 colors : ACTIVEBORDER STRING {
264 free(conf->color[CWM_COLOR_BORDER_ACTIVE]);
265 conf->color[CWM_COLOR_BORDER_ACTIVE] = $2;
266 }
267 | INACTIVEBORDER STRING {
268 free(conf->color[CWM_COLOR_BORDER_INACTIVE]);
269 conf->color[CWM_COLOR_BORDER_INACTIVE] = $2;
270 }
271 | URGENCYBORDER STRING {
272 free(conf->color[CWM_COLOR_BORDER_URGENCY]);
273 conf->color[CWM_COLOR_BORDER_URGENCY] = $2;
274 }
275 | GROUPBORDER STRING {
276 free(conf->color[CWM_COLOR_BORDER_GROUP]);
277 conf->color[CWM_COLOR_BORDER_GROUP] = $2;
278 }
279 | UNGROUPBORDER STRING {
280 free(conf->color[CWM_COLOR_BORDER_UNGROUP]);
281 conf->color[CWM_COLOR_BORDER_UNGROUP] = $2;
282 }
283 | MENUBG STRING {
284 free(conf->color[CWM_COLOR_MENU_BG]);
285 conf->color[CWM_COLOR_MENU_BG] = $2;
286 }
287 | MENUFG STRING {
288 free(conf->color[CWM_COLOR_MENU_FG]);
289 conf->color[CWM_COLOR_MENU_FG] = $2;
290 }
291 | FONTCOLOR STRING {
292 free(conf->color[CWM_COLOR_MENU_FONT]);
293 conf->color[CWM_COLOR_MENU_FONT] = $2;
294 }
295 | FONTSELCOLOR STRING {
296 free(conf->color[CWM_COLOR_MENU_FONT_SEL]);
297 conf->color[CWM_COLOR_MENU_FONT_SEL] = $2;
298 }
299 ;
300 %%
301
302 struct keywords {
303 const char *k_name;
304 int k_val;
305 };
306
307 int
308 yyerror(const char *fmt, ...)
309 {
310 va_list ap;
311
312 file->errors++;
313 va_start(ap, fmt);
314 fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
315 vfprintf(stderr, fmt, ap);
316 fprintf(stderr, "\n");
317 va_end(ap);
318 return (0);
319 }
320
321 int
322 kw_cmp(const void *k, const void *e)
323 {
324 return (strcmp(k, ((const struct keywords *)e)->k_name));
325 }
326
327 int
328 lookup(char *s)
329 {
330 /* this has to be sorted always */
331 static const struct keywords keywords[] = {
332 { "activeborder", ACTIVEBORDER},
333 { "autogroup", AUTOGROUP},
334 { "bind-key", BINDKEY},
335 { "bind-mouse", BINDMOUSE},
336 { "borderwidth", BORDERWIDTH},
337 { "color", COLOR},
338 { "command", COMMAND},
339 { "font", FONTCOLOR},
340 { "fontname", FONTNAME},
341 { "gap", GAP},
342 { "groupborder", GROUPBORDER},
343 { "htile", HTILE},
344 { "ignore", IGNORE},
345 { "inactiveborder", INACTIVEBORDER},
346 { "menubg", MENUBG},
347 { "menufg", MENUFG},
348 { "moveamount", MOVEAMOUNT},
349 { "no", NO},
350 { "selfont", FONTSELCOLOR},
351 { "snapdist", SNAPDIST},
352 { "sticky", STICKY},
353 { "unbind-key", UNBINDKEY},
354 { "unbind-mouse", UNBINDMOUSE},
355 { "ungroupborder", UNGROUPBORDER},
356 { "urgencyborder", URGENCYBORDER},
357 { "vtile", VTILE},
358 { "wm", WM},
359 { "yes", YES}
360 };
361 const struct keywords *p;
362
363 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
364 sizeof(keywords[0]), kw_cmp);
365
366 if (p)
367 return (p->k_val);
368 else
369 return (STRING);
370 }
371
372 #define MAXPUSHBACK 128
373
374 char *parsebuf;
375 int parseindex;
376 char pushback_buffer[MAXPUSHBACK];
377 int pushback_index = 0;
378
379 int
380 lgetc(int quotec)
381 {
382 int c, next;
383
384 if (parsebuf) {
385 /* Read character from the parsebuffer instead of input. */
386 if (parseindex >= 0) {
387 c = (unsigned char)parsebuf[parseindex++];
388 if (c != '\0')
389 return (c);
390 parsebuf = NULL;
391 } else
392 parseindex++;
393 }
394
395 if (pushback_index)
396 return ((unsigned char)pushback_buffer[--pushback_index]);
397
398 if (quotec) {
399 if ((c = getc(file->stream)) == EOF) {
400 yyerror("reached end of file while parsing "
401 "quoted string");
402 if (file == topfile || popfile() == EOF)
403 return (EOF);
404 return (quotec);
405 }
406 return (c);
407 }
408
409 while ((c = getc(file->stream)) == '\\') {
410 next = getc(file->stream);
411 if (next != '\n') {
412 c = next;
413 break;
414 }
415 yylval.lineno = file->lineno;
416 file->lineno++;
417 }
418
419 while (c == EOF) {
420 if (file == topfile || popfile() == EOF)
421 return (EOF);
422 c = getc(file->stream);
423 }
424 return (c);
425 }
426
427 int
428 lungetc(int c)
429 {
430 if (c == EOF)
431 return (EOF);
432 if (parsebuf) {
433 parseindex--;
434 if (parseindex >= 0)
435 return (c);
436 }
437 if (pushback_index + 1 >= MAXPUSHBACK)
438 return (EOF);
439 pushback_buffer[pushback_index++] = c;
440 return (c);
441 }
442
443 int
444 findeol(void)
445 {
446 int c;
447
448 parsebuf = NULL;
449
450 /* skip to either EOF or the first real EOL */
451 while (1) {
452 if (pushback_index)
453 c = (unsigned char)pushback_buffer[--pushback_index];
454 else
455 c = lgetc(0);
456 if (c == '\n') {
457 file->lineno++;
458 break;
459 }
460 if (c == EOF)
461 break;
462 }
463 return (ERROR);
464 }
465
466 int
467 yylex(void)
468 {
469 char buf[8096];
470 char *p;
471 int quotec, next, c;
472 int token;
473
474 p = buf;
475 while ((c = lgetc(0)) == ' ' || c == '\t')
476 ; /* nothing */
477
478 yylval.lineno = file->lineno;
479 if (c == '#')
480 while ((c = lgetc(0)) != '\n' && c != EOF)
481 ; /* nothing */
482
483 switch (c) {
484 case '\'':
485 case '"':
486 quotec = c;
487 while (1) {
488 if ((c = lgetc(quotec)) == EOF)
489 return (0);
490 if (c == '\n') {
491 file->lineno++;
492 continue;
493 } else if (c == '\\') {
494 if ((next = lgetc(quotec)) == EOF)
495 return (0);
496 if (next == quotec || next == ' ' ||
497 next == '\t')
498 c = next;
499 else if (next == '\n') {
500 file->lineno++;
501 continue;
502 } else
503 lungetc(next);
504 } else if (c == quotec) {
505 *p = '\0';
506 break;
507 } else if (c == '\0') {
508 yyerror("syntax error");
509 return (findeol());
510 }
511 if (p + 1 >= buf + sizeof(buf) - 1) {
512 yyerror("string too long");
513 return (findeol());
514 }
515 *p++ = c;
516 }
517 yylval.v.string = xstrdup(buf);
518 return (STRING);
519 }
520
521 #define allowed_to_end_number(x) \
522 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
523
524 if (c == '-' || isdigit(c)) {
525 do {
526 *p++ = c;
527 if ((size_t)(p-buf) >= sizeof(buf)) {
528 yyerror("string too long");
529 return (findeol());
530 }
531 } while ((c = lgetc(0)) != EOF && isdigit(c));
532 lungetc(c);
533 if (p == buf + 1 && buf[0] == '-')
534 goto nodigits;
535 if (c == EOF || allowed_to_end_number(c)) {
536 const char *errstr = NULL;
537
538 *p = '\0';
539 yylval.v.number = strtonum(buf, LLONG_MIN,
540 LLONG_MAX, &errstr);
541 if (errstr) {
542 yyerror("\"%s\" invalid number: %s",
543 buf, errstr);
544 return (findeol());
545 }
546 return (NUMBER);
547 } else {
548 nodigits:
549 while (p > buf + 1)
550 lungetc((unsigned char)*--p);
551 c = (unsigned char)*--p;
552 if (c == '-')
553 return (c);
554 }
555 }
556
557 /* Similar to other parse.y copies, but also allows '/' in strings */
558 #define allowed_in_string(x) \
559 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
560 x != '{' && x != '}' && x != '<' && x != '>' && \
561 x != '!' && x != '=' && x != '#' && x != ','))
562
563 if (isalnum(c) || c == ':' || c == '_' || c == '*' || c == '/') {
564 do {
565 *p++ = c;
566 if ((size_t)(p-buf) >= sizeof(buf)) {
567 yyerror("string too long");
568 return (findeol());
569 }
570 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
571 lungetc(c);
572 *p = '\0';
573 if ((token = lookup(buf)) == STRING)
574 yylval.v.string = xstrdup(buf);
575 return (token);
576 }
577 if (c == '\n') {
578 yylval.lineno = file->lineno;
579 file->lineno++;
580 }
581 if (c == EOF)
582 return (0);
583 return (c);
584 }
585
586 struct file *
587 pushfile(const char *name, FILE *stream)
588 {
589 struct file *nfile;
590
591 nfile = xcalloc(1, sizeof(struct file));
592 nfile->name = xstrdup(name);
593 nfile->stream = stream;
594 nfile->lineno = 1;
595 TAILQ_INSERT_TAIL(&files, nfile, entry);
596 return (nfile);
597 }
598
599 int
600 popfile(void)
601 {
602 struct file *prev;
603
604 if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
605 prev->errors += file->errors;
606
607 TAILQ_REMOVE(&files, file, entry);
608 fclose(file->stream);
609 free(file->name);
610 free(file);
611 file = prev;
612 return (file ? 0 : EOF);
613 }
614
615 int
616 parse_config(const char *filename, struct conf *xconf)
617 {
618 FILE *stream;
619 int errors = 0;
620
621 conf = xconf;
622
623 stream = fopen(filename, "r");
624 if (stream == NULL) {
625 if (errno == ENOENT)
626 return (0);
627 warn("%s", filename);
628 return (-1);
629 }
630 file = pushfile(filename, stream);
631 topfile = file;
632
633 yyparse();
634 errors = file->errors;
635 popfile();
636
637 return (errors ? -1 : 0);
638 }
639