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