1 %{
2 /* +-------------------------------------------------------------------+ */
3 /* | Copyright 1991, David Koblas.                                     | */
4 /* |   Permission to use, copy, modify, and distribute this software   | */
5 /* |   and its documentation for any purpose and without fee is hereby | */
6 /* |   granted, provided that the above copyright notice appear in all | */
7 /* |   copies and that both that copyright notice and this permission  | */
8 /* |   notice appear in supporting documentation.  This software is    | */
9 /* |   provided "as is" without express or implied warranty.           | */
10 /* +-------------------------------------------------------------------+ */
11 
12 #include "defs.h"
13 #include <syslog.h>
14 
15 #ifndef NDEBUG
16 #define _msg(x)
17 #else
18 #define _msg(x) msg x
19 static void msg(const char *format, ...);
20 #endif
21 static char *expandvars(const char *instr);
22 static void preprocerror(const char *str);
23 static void preproc(const char *str);
24 static void addvar(const char *str);
25 static void addquotedarg(int state, cmd_t * cmd, const char *instr);
26 static void addarg(int state, cmd_t * cmd, const char *instr);
27 static cmd_t *alloccmd(const char *name);
28 static void freecmd(cmd_t * cmd);
29 static cmd_t *newcmd(const char *name);
30 #ifdef NUNUSED
31 static int cmpopts(const char *a, const char *b);
32 static void sortopts(cmd_t * cmd);
33 #endif
34 
35 int yyline = 1;
36 
37 #define YY_NO_INPUT
38 
39 %}
40 
41 WS		[ \t]*
42 NWS		[^ \n\t;]+
43 
44 %s ARGS
45 %a 2700
46 %o 3700
47 
48 %option nounput
49 
50 %%
51 	int	state = 0;
52 	cmd_t	*cmd = NULL;
53 
54 #[^\n]*			;
55 ^%(((if|elsif)[ \t]+.*)|(else|endif))		{ preproc(yytext); }
56 ^%.*			{ preprocerror(yytext); }
57 \n			{ yyline++; BEGIN 0; }
58 ^[A-Z_][A-Z_0-9]*=[^\n\r]*	{ addvar(yytext); }
59 ^[^ \n\t]+		{ cmd = newcmd(yytext);
60 				state = strcmp(yytext, "DEFAULT") == 0 ? 1 : 0;
61 				BEGIN ARGS; }
62 ^{WS}			BEGIN ARGS;
63 <ARGS>";"		state++;
64 <ARGS>([^ \n\t'"]*'(\\.|[^'])*')+	addquotedarg(state, cmd, yytext);
65 <ARGS>([^ \n\t'"]*\"(\\.|[^\"])*\")+	addquotedarg(state, cmd, yytext);
66 <ARGS>{NWS}		addarg(state, cmd, yytext);
67 <ARGS>{WS}		;
68 %%
69 /* ' for emacs */
70 
71 #include <sys/types.h>
72 #include <sys/stat.h>
73 #include <string.h>
74 
75 #ifdef NDEBUG
76 static void
77 msg(const char *format, ...)
78 {
79     va_list ap;
80     char *s;
81 
82     va_start(ap);
83     s = va_arg(ap, char *);
84     fprintf(stderr, "line %d: ", yyline);
85     /* Flawfinder: ignore (vfprintf) */
86     vfprintf(stderr, s, ap);
87     fputc('\n', stderr);
88     va_end(ap);
89 }
90 #endif
91 
92 static char *
93 expandvars(const char *instr)
94 {
95     char *str = (char *)malloc(VAR_EXPAND_LEN);
96     size_t i = 0;
97 
98     if (str == NULL)
99 	fatal(1, "Unable to allocate variable expansion buffer");
100 
101     while (*instr)
102 	if (isupper((int)*instr) || *instr == '_') {
103 	    const char *mark = instr;
104 	    var_t *var;
105 
106 	    while (*instr &&
107 		   (isupper((int)*instr) || isdigit((int)*instr) ||
108 		    *instr == '_'))
109 		++instr;
110 	    for (var = Variables; var != 0; var = var->next) {
111 		/* Flawfinder: ignore (strlen) */
112 		size_t l = strlen(var->name);
113 
114 		if ((size_t) (instr - mark) > l)
115 		    l = (size_t) (instr - mark);
116 		if (!strncmp(mark, var->name, l)) {
117 		    str[i] = 0;
118 		    /* Flawfinder: fix (strcat) */
119 		    strlcat(str, var->value, VAR_EXPAND_LEN);
120 		    /* Flawfinder: ignore (strlen) */
121 		    i += strlen(var->value);
122 		    break;
123 		}
124 	    }
125 	    if (!var) {
126 		instr = mark + 1;
127 		str[i++] = *mark;
128 	    }
129 	} else
130 	    str[i++] = *instr++;
131     str[i] = 0;
132     return str;
133 }
134 
135 static void
136 preprocerror(const char *str)
137 {
138     fprintf(stderr, "Invalid preprocessor command '%s'\n", str);
139     exit(1);
140 }
141 
142 static void
143 preproc(const char *str)
144 {
145     if (!strncmp(str, "%if", 3)) {
146     } else if (!strncmp(str, "%elseif", 7)) {
147     } else if (!strcmp(str, "%else")) {
148     } else if (!strcmp(str, "%endif")) {
149     } else
150 	preprocerror(str);
151 }
152 
153 static void
154 addvar(const char *str)
155 {
156     /* Flawfinder: ignore (char) */
157     /* cppcheck-suppress variableScope */
158     char name[VAR_NAME_LEN], value[VAR_EXPAND_LEN];
159     const char *eq = strchr(str, '=');
160 
161     if (eq && str - eq < VAR_NAME_LEN) {
162 	size_t i, o, len;
163 	var_t *var;
164 
165 	/* Flawfinder: fix (strncpy) */
166 	strlcpy(name, str, MIN((size_t) (eq - str + 1), sizeof(name)));
167 
168 	for (o = 0, i = (size_t) (eq - str + 1);
169 	     o < VAR_EXPAND_LEN - 1 && str[i]; ++i)
170 	    if (str[i] == '\\') {
171 		switch (str[++i]) {
172 		case 'n':
173 		    value[o++] = '\n';
174 		    break;
175 		case 'r':
176 		    value[o++] = '\r';
177 		    break;
178 		case 't':
179 		    value[o++] = '\t';
180 		    break;
181 		case 'a':
182 		    value[o++] = '\a';
183 		    break;
184 		case 'b':
185 		    value[o++] = '\b';
186 		    break;
187 		default:
188 		    value[o++] = str[i];
189 		    break;
190 		}
191 	    } else if (str[i] == '"')
192 		break;
193 	    else
194 		value[o++] = str[i];
195 	value[o++] = 0;
196 
197 	if ((var = (var_t *) malloc(sizeof(var_t))) == NULL)
198 	    fatal(1, "Unable to allocate var_t");
199 	/* Flawfinder: fix (strlen) */
200 	len = strlen(name) + 1;
201 	if ((var->name = malloc(len)) == NULL)	/* expected-warning */
202 	    fatal(1, "Unable to allocate variable name");
203 	/* Flawfinder: fix (strcpy) */
204 	strlcpy(var->name, name, len);
205 	var->value = expandvars(value);
206 	var->next = 0;
207 
208 	if (Variables) {
209 	    var_t *v;
210 
211 	    for (v = Variables; v->next; v = v->next) ;
212 	    v->next = var;
213 	} else
214 	    Variables = var;
215     } else
216 	fatal(1, "Invalid alias");
217 }
218 
219 static void
220 addquotedarg(int state, cmd_t * cmd, const char *instr)
221 {
222     /* Flawfinder: ignore (char) */
223     char buffer[MAXSTRLEN];
224     size_t i, o, q;
225 
226     /* Flawfinder: ignore (strlen) */
227     if (strlen(instr) + 2 > MAXSTRLEN) {
228 	fatal(1, "Quoted argument too long\n");
229 	exit(1);
230     }
231     for (o = 0; !strchr("'\"", instr[o]); ++o)
232 	buffer[o] = instr[o];
233     q = o;
234 
235     for (i = o + 1; instr[i] && instr[i] != instr[q]; ++i, ++o) {
236 	if (instr[i] == '\\') {
237 	    int c = instr[++i];
238 
239 	    if (strchr("'\"", c)) {
240 		buffer[o] = (char)c;
241 	    } else {
242 		buffer[o++] = '\\';
243 		buffer[o] = (char)c;
244 	    }
245 	} else
246 	    buffer[o] = instr[i];
247     }
248     buffer[o] = 0;
249     addarg(state, cmd, buffer);
250 }
251 
252 static void
253 addarg(int state, cmd_t * cmd, const char *instr)
254 {
255     char *str = expandvars(instr);
256 
257     if (state == 0) {
258 	_msg(("cmd='%s' add arg '%s'", cmd->name, str));
259 	if (cmd->margs == cmd->nargs) {
260 	    cmd->margs += cmd->margs;
261 	    cmd->args = (char **)realloc(cmd->args,
262 					 sizeof(char *) * cmd->margs);
263 	    if (cmd->args == NULL)
264 		fatal(1, "Unable to groupw args");
265 	}
266 	cmd->args[cmd->nargs++] = savestr(str);
267     } else if (state == 1) {
268 	_msg(("cmd='%s' add opt '%s'", cmd->name, str));
269 	if (cmd->mopts == cmd->nopts) {
270 	    cmd->mopts += cmd->mopts;
271 	    cmd->opts = (char **)realloc(cmd->opts,
272 					 sizeof(char *) * cmd->mopts);
273 	    if (cmd->opts == NULL)
274 		fatal(1, "Unable to groupw opts");
275 	}
276 	cmd->opts[cmd->nopts++] = savestr(str);
277     } else {
278 	fatal(1, "bad state (%d) received\n", state);
279     }
280     free(str);
281 }
282 
283 char *
284 savestr(const char *str)
285 {
286     /* Flawfinder: ignore (strlen) */
287     size_t len = strlen(str) + 1;
288     char *s = (char *)malloc(len);
289 
290     if (s == NULL)
291 	fatal(1, "No string space");
292 
293     /* Flawfinder: fix (strcpy) */
294     strlcpy(s, str, len);
295     return s;
296 }
297 
298 static cmd_t *
299 alloccmd(const char *name)
300 {
301     cmd_t *cmd = (cmd_t *) malloc(sizeof(cmd_t));
302 
303     if (cmd == NULL)
304 	fatal(1, "Unable to alloc space for new command");
305 
306     cmd->name = savestr(name);
307     cmd->nargs = 0;
308     cmd->margs = 16;
309     cmd->nopts = 0;
310     cmd->mopts = 16;
311     cmd->args = (char **)malloc(sizeof(char *) * cmd->margs);
312     cmd->opts = (char **)malloc(sizeof(char *) * cmd->mopts);
313 
314     if (cmd->args == NULL || cmd->opts == NULL)
315 	fatal(1, "Unable to alloc args/opts");
316 
317     return cmd;
318 }
319 
320 static void
321 freecmd(cmd_t * cmd)
322 {
323     if (cmd == NULL)
324 	return;
325 
326     free(cmd->name);
327     free(cmd->args);
328     free(cmd->opts);
329     free(cmd);
330 }
331 
332 static cmd_t *
333 newcmd(const char *name)
334 {
335     cmd_t *cmd = alloccmd(name);
336 
337     cmd->next = First;
338     First = cmd;
339 
340     return cmd;
341 }
342 
343 int
344 ReadFile(const char *file)
345 {
346     struct stat statbuf;
347     FILE *fd;
348 
349     if ((stat(file, &statbuf) < 0))
350 	return 0;
351     if ((statbuf.st_uid != 0) ||	/* Owned by root */
352 	((statbuf.st_mode & 0077) != 0)) {	/* SD - no perm */
353 	logger(LOG_ERR, "Permission problems on %s", file);
354 	return 0;
355     }
356     /* Flawfinder: ignore (fopen) race condition */
357     if ((fd = fopen(file, "r")) == NULL)
358 	return 0;
359 
360     yyin = fd;
361     yylex();
362 
363     return 1;
364 }
365 
366 int
367 CountArgs(cmd_t * cmd)
368 {
369     size_t i;
370     /* NOLINTNEXTLINE(runtime/int) */
371     long val, max = 0;
372     int wild = 0;
373     /* Flawfinder: ignore (char) */
374     /* cppcheck-suppress variableScope */
375     char *cp, *np, str[MAXSTRLEN];
376 
377     for (i = 0; i < cmd->nargs; i++) {
378 	np = cmd->args[i];
379 
380 	while ((cp = strchr(np, '$')) != NULL) {
381 	    if ((cp != cmd->args[i]) && (*(cp - 1) == '\\'))
382 		np = cp + 1;
383 	    else {
384 		if (*(cp + 1) == '*') {
385 		    wild = 1;
386 		    ++cp;
387 		    np = cp;
388 		    continue;
389 		}
390 		cp++;
391 		np = cp;
392 
393 		while (isdigit((int)*cp))
394 		    cp++;
395 		if ((cp - np) == 0)
396 		    continue;
397 		/* Flawfinder: fix (strncpy) */
398 		strlcpy(str, np, MIN((size_t) (cp - np + 1), sizeof(str)));
399 		/* Flawfinder: fix (atoi -> strtolong) */
400 		val = strtolong(str, 10);
401 		if (val > max)
402 		    max = val;
403 	    }
404 	}
405     }
406 
407     if (wild)
408 	return (int)-max;
409     return (int)max;
410 }
411 
412 #ifdef NUNUSED
413 static int
414 cmpopts(const char *a, const char *b)
415 {
416     char *cp_a, *cp_b;
417     /* NOLINTNEXTLINE(runtime/int) */
418     long val_a, val_b;
419     /* Flawfinder: ignore (char) */
420     char str_a[MAXSTRLEN], str_b[MAXSTRLEN];
421 
422     if (*a != '$' && *b != '$')
423 	return 0;
424     if (*a == '$' && *b != '$')
425 	return -1;
426     if (*a != '$' && *b == '$')
427 	return 1;
428 
429     cp_a = ++a;
430     cp_b = ++b;
431     while ((*cp_a != '\0') && (*cp_a != '='))
432 	if (!isdigit((int)*cp_a))
433 	    break;
434     while ((*cp_b != '\0') && (*cp_b != '='))
435 	if (!isdigit((int)*cp_b))
436 	    break;
437 
438     if (*cp_a != '=' && *cp_b != '=')
439 	return 0;
440     if (*cp_a == '=' && *cp_b != '=')
441 	return -1;
442     if (*cp_a != '=' && *cp_b == '=')
443 	return 1;
444 
445     /* flawfinder: fix (strncpy) */
446     strlcpy(str_a, a, MIN((size_t) (cp_a - a + 1), sizeof(str_a)));
447     /* flawfinder: fix (atoi -> strtolong) */
448     val_a = strtolong(str_a, 10);
449     /* flawfinder: fix (strncpy) */
450     strlcpy(str_b, b, MIN((size_t) (cp_b - a + 1), sizeof(str_b)));
451     /* Flawfinder: fix (atoi -> strtolong) */
452     val_b = strtolong(str_b, 10);
453 
454     if (val_a < val_b)
455 	return -1;
456     if (val_a > val_b)
457 	return 1;
458     return 0;
459 }
460 
461 static void
462 sortopts(cmd_t * cmd)
463 {
464     qsort(cmd->opts, cmd->nopts, sizeof(char *),
465 	  (int(*)(const void *, const void *))cmpopts);
466 }
467 #endif
468 
469 /* Build a new command but don't merge it into the global list */
470 cmd_t *
471 BuildSingle(cmd_t * def, cmd_t * cmd)
472 {
473     cmd_t *new = alloccmd(cmd->name ? cmd->name : "");
474     /* Flawfinder: ignore (char) */
475     char defname[MAXSTRLEN], optname[MAXSTRLEN], *cp;
476     size_t i, j;
477 
478     /* cppcheck-suppress nullPointer */
479     if (cmd == NULL) {
480 	freecmd(new);
481 	return def;
482     }
483     if (def == NULL) {
484 	freecmd(new);
485 	return cmd;
486     }
487 
488     for (i = 0; i < cmd->nargs; i++)
489 	addarg(0, new, cmd->args[i]);
490 
491     for (i = 0; i < def->nopts; i++) {
492 	int skipped = 0;
493 
494 	if ((cp = strchr(def->opts[i], '=')) == NULL)
495 	    /* Flawfinder: fix (strcpy) */
496 	    strlcpy(defname, def->opts[i], sizeof(defname));
497 	else {
498 	    size_t l = (size_t) (cp - def->opts[i]);
499 	    /* Flawfinder: fix (strncpy) */
500 	    strlcpy(defname, def->opts[i], MIN(l + 1, sizeof(defname)));
501 	}
502 	for (j = 0; j < cmd->nopts; j++) {
503 	    if ((cp = strchr(cmd->opts[j], '=')) == NULL)
504 		/* Flawfinder: fix (strcpy) */
505 		strlcpy(optname, cmd->opts[j], sizeof(optname));
506 	    else {
507 		size_t l = (size_t) (cp - cmd->opts[j]);
508 		/* Flawfinder: fix (strncpy) */
509 		strlcpy(optname, cmd->opts[j], MIN(l + 1, sizeof(optname)));
510 	    }
511 	    if (strcmp(defname, optname) == 0) {
512 		skipped = 1;
513 		break;
514 	    }
515 	}
516 	if (skipped)
517 	    continue;
518 	if (def->opts[i][0] != '\0')
519 	    addarg(1, new, def->opts[i]);
520     }
521     for (j = 0; j < cmd->nopts; j++)
522 	addarg(1, new, cmd->opts[j]);
523 
524     /* sortopts(new); */
525 
526     return new;
527 }
528 
529 /* Build a new command *and* merge it with the global command list */
530 cmd_t *
531 Build(cmd_t * def, cmd_t * cmd)
532 {
533     cmd_t *new = BuildSingle(def, cmd);
534 
535     new->next = First;
536     First = new;
537 
538     return new;
539 }
540