Blob


1 /*
2 * Much of the design is taken from doas (parse.y rev 1.29)
3 *
4 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
5 * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 */
21 %{
23 #include "telescope.h"
25 #include <ctype.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 typedef struct {
33 union {
34 char *str;
35 int num;
36 };
37 int lineno;
38 int colno;
39 } yystype;
40 #define YYSTYPE yystype
42 static char *current_style;
43 static const char *path;
45 FILE *yyfp;
47 int parse_errors = 0;
49 static void yyerror(const char *, ...);
50 static int yylex(void);
51 static void setprfx(int, const char *);
52 static void setvari(char *, int);
53 static void setvars(char *, char *);
55 %}
57 %token TSET
58 %token TSTYLE TPRFX TCONT TBG TFG TATTR TBOLD TUNDERLINE
59 %token TBIND TUNBIND
61 %token <str> TSTRING
62 %token <num> TNUMBER
64 %%
66 grammar : /* empty */
67 | grammar '\n'
68 | grammar rule '\n'
69 | error '\n'
70 ;
72 rule : set
73 | style { free(current_style); current_style = NULL; }
74 | bind
75 | unbind
76 ;
78 set : TSET TSTRING '=' TSTRING { setvars($2, $4); }
79 | TSET TSTRING '=' TNUMBER { setvari($2, $4); }
80 ;
82 style : TSTYLE TSTRING { current_style = $2; } styleopt
83 | TSTYLE TSTRING { current_style = $2; } '{' styleopts '}'
84 ;
86 styleopts : /* empty */
87 | styleopts '\n'
88 | styleopts styleopt '\n'
89 ;
91 styleopt : TPRFX TSTRING { setprfx(0, $2); }
92 | TCONT TSTRING { setprfx(1, $2); }
93 | TBG TSTRING { printf("style background setted to \"%s\"\n", $2); }
94 | TFG TSTRING { printf("style foreground setted to \"%s\"\n", $2); }
95 | TATTR TBOLD { printf("style attr setted to bold\n"); }
96 | TATTR TUNDERLINE { printf("style attr setted to underline\n"); }
97 ;
99 bind : TBIND TSTRING TSTRING TSTRING { printf("TODO: bind %s %s %s\n", $2, $3, $4); }
102 unbind : TUNBIND TSTRING TSTRING { printf("TODO: unbind %s %s\n", $2, $3); }
105 %%
107 void
108 yyerror(const char *fmt, ...)
110 va_list va;
112 fprintf(stderr, "%s:%d ", path, yylval.lineno+1);
113 va_start(va, fmt);
114 vfprintf(stderr, fmt, va);
115 va_end(va);
116 fprintf(stderr, "\n");
117 parse_errors++;
120 static struct keyword {
121 const char *word;
122 int token;
123 } keywords[] = {
124 { "set", TSET },
125 { "style", TSTYLE },
126 { "prefix", TPRFX },
127 { "cont", TCONT },
128 { "background", TBG },
129 { "foreground", TFG },
130 { "attr", TATTR },
131 { "bold", TBOLD },
132 { "underline", TUNDERLINE },
133 { "bind", TBIND },
134 { "unbind", TUNBIND },
135 };
137 int
138 yylex(void)
140 char buf[1024], *ebuf, *p, *str;
141 const char *errstr;
142 int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
143 size_t i;
145 p = buf;
146 ebuf = buf + sizeof(buf);
148 repeat:
149 /* skip whitespace first */
150 for (c = getc(yyfp); c == ' ' || c == '\t' || c == '\f'; c = getc(yyfp))
151 yylval.colno++;
153 /* check for special one-character constructions */
154 switch (c) {
155 case '\n':
156 yylval.colno = 0;
157 yylval.lineno++;
158 /* fallthrough */
159 case '{':
160 case '}':
161 case '=':
162 return c;
163 case '#':
164 /* skip comments; NUL is allowed; no continuation */
165 while ((c = getc(yyfp)) != '\n')
166 if (c == EOF)
167 goto eof;
168 yylval.colno = 0;
169 yylval.lineno++;
170 return c;
171 case EOF:
172 goto eof;
175 /* parsing next word */
176 for (;; c = getc(yyfp), yylval.colno++) {
177 switch (c) {
178 case '\0':
179 yyerror("unallowed character NULL in column %d",
180 yylval.colno+1);
181 escape = 0;
182 continue;
183 case '\\':
184 escape = !escape;
185 if (escape)
186 continue;
187 break;
188 case '\n':
189 if (quotes)
190 yyerror("unterminated quotes in column %d",
191 yylval.colno+1);
192 if (escape) {
193 nonkw = 1;
194 escape = 0;
195 yylval.colno = 0;
196 yylval.lineno++;
198 goto eow;
199 case EOF:
200 if (escape)
201 yyerror("unterminated escape in column %d",
202 yylval.colno);
203 if (quotes)
204 yyerror("unterminated quotes in column %d",
205 qpos + 1);
206 goto eow;
207 case '{':
208 case '}':
209 case '=':
210 case '#':
211 case ' ':
212 case '\t':
213 if (!escape && !quotes)
214 goto eow;
215 break;
216 case '"':
217 if (!escape) {
218 quotes = !quotes;
219 if (quotes) {
220 nonkw = 1;
221 qpos = yylval.colno;
223 continue;
226 *p++ = c;
227 if (p == ebuf) {
228 yyerror("line too long");
229 p = buf;
231 escape = 0;
234 eow:
235 *p = 0;
236 if (c != EOF)
237 ungetc(c, yyfp);
238 if (p == buf) {
239 /*
240 * There could be a number of reason for empty buffer,
241 * and we handle all of them here, to avoid cluttering
242 * the main loop.
243 */
244 if (c == EOF)
245 goto eof;
246 else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
247 goto repeat;
249 if (!nonkw) {
250 for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
251 if (strcmp(buf, keywords[i].word) == 0)
252 return keywords[i].token;
255 c = *buf;
256 if (!nonkw && (c == '-' || isdigit(c))) {
257 yylval.num = strtonum(buf, INT_MIN, INT_MAX, &errstr);
258 if (errstr != NULL)
259 yyerror("number is %s: %s", errstr, buf);
260 return TNUMBER;
262 if ((str = strdup(buf)) == NULL)
263 err(1, "%s", __func__);
264 yylval.str = str;
265 return TSTRING;
267 eof:
268 if (ferror(yyfp))
269 yyerror("input error reading config");
270 return 0;
273 static void
274 setprfx(int cont, const char *name)
276 if (current_style == NULL) {
277 warnx("current_style = NULL!");
278 abort();
281 if (!config_setprfx(current_style, cont, name))
282 yyerror("invalid style %s", name);
285 static void
286 setvari(char *var, int val)
288 if (!config_setvari(var, val))
289 yyerror("invalid variable or value: %s = %d",
290 var, val);
292 free(var);
295 static void
296 setvars(char *var, char *val)
298 if (!config_setvars(var, val))
299 yyerror("invalid variable or value: %s = \"%s\"",
300 var, val);
302 free(var);
305 void
306 parseconfig(const char *filename, int fonf)
308 if ((yyfp = fopen(filename, "r")) == NULL) {
309 if (fonf)
310 err(1, "%s", filename);
311 return;
314 path = filename;
315 yyparse();
316 fclose(yyfp);
317 if (parse_errors)
318 exit(1);