| File: | src/usr.bin/lex/filter.c |
| Warning: | line 368, column 4 Value stored to 'num' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: filter.c,v 1.9 2017/08/30 02:54:07 lteo Exp $ */ |
| 2 | |
| 3 | /* filter - postprocessing of flex output through filters */ |
| 4 | |
| 5 | /* This file is part of flex. */ |
| 6 | |
| 7 | /* Redistribution and use in source and binary forms, with or without */ |
| 8 | /* modification, are permitted provided that the following conditions */ |
| 9 | /* are met: */ |
| 10 | |
| 11 | /* 1. Redistributions of source code must retain the above copyright */ |
| 12 | /* notice, this list of conditions and the following disclaimer. */ |
| 13 | /* 2. Redistributions in binary form must reproduce the above copyright */ |
| 14 | /* notice, this list of conditions and the following disclaimer in the */ |
| 15 | /* documentation and/or other materials provided with the distribution. */ |
| 16 | |
| 17 | /* Neither the name of the University nor the names of its contributors */ |
| 18 | /* may be used to endorse or promote products derived from this software */ |
| 19 | /* without specific prior written permission. */ |
| 20 | |
| 21 | /* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ |
| 22 | /* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ |
| 23 | /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ |
| 24 | /* PURPOSE. */ |
| 25 | |
| 26 | #include "flexdef.h" |
| 27 | static const char *check_4_gnu_m4 = |
| 28 | "m4_dnl ifdef(`__gnu__', ," |
| 29 | "`errprint(Flex requires GNU M4. Set the PATH or set the M4 environment variable to its path name.)" |
| 30 | " m4exit(2)')\n"; |
| 31 | |
| 32 | |
| 33 | /** global chain. */ |
| 34 | struct filter *output_chain = NULL((void *)0); |
| 35 | |
| 36 | /* Allocate and initialize an external filter. |
| 37 | * @param chain the current chain or NULL for new chain |
| 38 | * @param cmd the command to execute. |
| 39 | * @param ... a NULL terminated list of (const char*) arguments to command, |
| 40 | * not including argv[0]. |
| 41 | * @return newest filter in chain |
| 42 | */ |
| 43 | struct filter * |
| 44 | filter_create_ext(struct filter * chain, const char *cmd, |
| 45 | ...) |
| 46 | { |
| 47 | struct filter *f; |
| 48 | int max_args; |
| 49 | const char *s; |
| 50 | va_list ap; |
| 51 | |
| 52 | /* allocate and initialize new filter */ |
| 53 | f = calloc(sizeof(struct filter), 1); |
| 54 | if (!f) |
| 55 | flexerror(_("calloc failed (f) in filter_create_ext")"calloc failed (f) in filter_create_ext"); |
| 56 | f->filter_func = NULL((void *)0); |
| 57 | f->extra = NULL((void *)0); |
| 58 | f->next = NULL((void *)0); |
| 59 | f->argc = 0; |
| 60 | |
| 61 | if (chain != NULL((void *)0)) { |
| 62 | /* append f to end of chain */ |
| 63 | while (chain->next) |
| 64 | chain = chain->next; |
| 65 | chain->next = f; |
| 66 | } |
| 67 | /* allocate argv, and populate it with the argument list. */ |
| 68 | max_args = 8; |
| 69 | f->argv = malloc(sizeof(char *) * (max_args + 1)); |
| 70 | if (!f->argv) |
| 71 | flexerror(_("malloc failed (f->argv) in filter_create_ext")"malloc failed (f->argv) in filter_create_ext"); |
| 72 | f->argv[f->argc++] = cmd; |
| 73 | |
| 74 | va_start(ap, cmd)__builtin_va_start(ap, cmd); |
| 75 | while ((s = va_arg(ap, const char *)__builtin_va_arg(ap, const char *)) != NULL((void *)0)) { |
| 76 | if (f->argc >= max_args) { |
| 77 | max_args += 8; |
| 78 | f->argv = realloc(f->argv, |
| 79 | sizeof(char *) * (max_args + 1)); |
| 80 | } |
| 81 | f->argv[f->argc++] = s; |
| 82 | } |
| 83 | f->argv[f->argc] = NULL((void *)0); |
| 84 | |
| 85 | va_end(ap)__builtin_va_end(ap); |
| 86 | return f; |
| 87 | } |
| 88 | |
| 89 | /* Allocate and initialize an internal filter. |
| 90 | * @param chain the current chain or NULL for new chain |
| 91 | * @param filter_func The function that will perform the filtering. |
| 92 | * filter_func should return 0 if successful, and -1 |
| 93 | * if an error occurs -- or it can simply exit(). |
| 94 | * @param extra optional user-defined data to pass to the filter. |
| 95 | * @return newest filter in chain |
| 96 | */ |
| 97 | struct filter * |
| 98 | filter_create_int(struct filter * chain, |
| 99 | int (*filter_func) (struct filter *), |
| 100 | void *extra) |
| 101 | { |
| 102 | struct filter *f; |
| 103 | |
| 104 | /* allocate and initialize new filter */ |
| 105 | f = calloc(sizeof(struct filter), 1); |
| 106 | if (!f) |
| 107 | flexerror(_("calloc failed in filter_create_int")"calloc failed in filter_create_int"); |
| 108 | f->next = NULL((void *)0); |
| 109 | f->argc = 0; |
| 110 | f->argv = NULL((void *)0); |
| 111 | |
| 112 | f->filter_func = filter_func; |
| 113 | f->extra = extra; |
| 114 | |
| 115 | if (chain != NULL((void *)0)) { |
| 116 | /* append f to end of chain */ |
| 117 | while (chain->next) |
| 118 | chain = chain->next; |
| 119 | chain->next = f; |
| 120 | } |
| 121 | return f; |
| 122 | } |
| 123 | |
| 124 | /** Fork and exec entire filter chain. |
| 125 | * @param chain The head of the chain. |
| 126 | * @return true on success. |
| 127 | */ |
| 128 | bool_Bool |
| 129 | filter_apply_chain(struct filter * chain) |
| 130 | { |
| 131 | int pid, pipes[2]; |
| 132 | |
| 133 | /* |
| 134 | * Tricky recursion, since we want to begin the chain at the END. |
| 135 | * Why? Because we need all the forked processes to be children of |
| 136 | * the main flex process. |
| 137 | */ |
| 138 | if (chain) |
| 139 | filter_apply_chain(chain->next); |
| 140 | else |
| 141 | return true1; |
| 142 | |
| 143 | /* |
| 144 | * Now we are the right-most unprocessed link in the chain. |
| 145 | */ |
| 146 | |
| 147 | fflush(stdout(&__sF[1])); |
| 148 | fflush(stderr(&__sF[2])); |
| 149 | |
| 150 | |
| 151 | if (pipe(pipes) == -1) |
| 152 | flexerror(_("pipe failed")"pipe failed"); |
| 153 | |
| 154 | if ((pid = fork()) == -1) |
| 155 | flexerror(_("fork failed")"fork failed"); |
| 156 | |
| 157 | if (pid == 0) { |
| 158 | /* child */ |
| 159 | |
| 160 | /* |
| 161 | * We need stdin (the FILE* stdin) to connect to this new |
| 162 | * pipe. There is no portable way to set stdin to a new file |
| 163 | * descriptor, as stdin is not an lvalue on some systems |
| 164 | * (BSD). So we dup the new pipe onto the stdin descriptor |
| 165 | * and use a no-op fseek to sync the stream. This is a Hail |
| 166 | * Mary situation. It seems to work. |
| 167 | */ |
| 168 | close(pipes[1]); |
| 169 | clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~ (0x0040|0x0020))) : (clearerr)((&__sF[0]))); |
| 170 | if (dup2(pipes[0], fileno(stdin)(!__isthreaded ? (((&__sF[0]))->_file) : (fileno)((& __sF[0])))) == -1) |
| 171 | flexfatal(_("dup2(pipes[0],0)")"dup2(pipes[0],0)"); |
| 172 | close(pipes[0]); |
| 173 | fseek(stdin(&__sF[0]), 0, SEEK_CUR1); |
| 174 | |
| 175 | /* run as a filter, either internally or by exec */ |
| 176 | if (chain->filter_func) { |
| 177 | if (chain->filter_func(chain) == -1) |
| 178 | flexfatal(_("filter_func failed")"filter_func failed"); |
| 179 | exit(0); |
| 180 | } else { |
| 181 | execvp(chain->argv[0], |
| 182 | (char **const) (chain->argv)); |
| 183 | lerrsf_fatal(_("exec of %s failed")"exec of %s failed", |
| 184 | chain->argv[0]); |
| 185 | } |
| 186 | |
| 187 | exit(1); |
| 188 | } |
| 189 | /* Parent */ |
| 190 | close(pipes[0]); |
| 191 | if (dup2(pipes[1], fileno(stdout)(!__isthreaded ? (((&__sF[1]))->_file) : (fileno)((& __sF[1])))) == -1) |
| 192 | flexfatal(_("dup2(pipes[1],1)")"dup2(pipes[1],1)"); |
| 193 | close(pipes[1]); |
| 194 | fseek(stdout(&__sF[1]), 0, SEEK_CUR1); |
| 195 | |
| 196 | return true1; |
| 197 | } |
| 198 | |
| 199 | /** Truncate the chain to max_len number of filters. |
| 200 | * @param chain the current chain. |
| 201 | * @param max_len the maximum length of the chain. |
| 202 | * @return the resulting length of the chain. |
| 203 | */ |
| 204 | int |
| 205 | filter_truncate(struct filter * chain, int max_len) |
| 206 | { |
| 207 | int len = 1; |
| 208 | |
| 209 | if (!chain) |
| 210 | return 0; |
| 211 | |
| 212 | while (chain->next && len < max_len) { |
| 213 | chain = chain->next; |
| 214 | ++len; |
| 215 | } |
| 216 | |
| 217 | chain->next = NULL((void *)0); |
| 218 | return len; |
| 219 | } |
| 220 | |
| 221 | /** Splits the chain in order to write to a header file. |
| 222 | * Similar in spirit to the 'tee' program. |
| 223 | * The header file name is in extra. |
| 224 | * @return 0 (zero) on success, and -1 on failure. |
| 225 | */ |
| 226 | int |
| 227 | filter_tee_header(struct filter * chain) |
| 228 | { |
| 229 | /* |
| 230 | * This function reads from stdin and writes to both the C file and |
| 231 | * the header file at the same time. |
| 232 | */ |
| 233 | |
| 234 | const int readsz = 512; |
| 235 | char *buf; |
| 236 | int to_cfd = -1; |
| 237 | FILE *to_c = NULL((void *)0), *to_h = NULL((void *)0); |
| 238 | bool_Bool write_header; |
| 239 | |
| 240 | write_header = (chain->extra != NULL((void *)0)); |
| 241 | |
| 242 | /* |
| 243 | * Store a copy of the stdout pipe, which is already piped to C file |
| 244 | * through the running chain. Then create a new pipe to the H file as |
| 245 | * stdout, and fork the rest of the chain again. |
| 246 | */ |
| 247 | |
| 248 | if ((to_cfd = dup(1)) == -1) |
| 249 | flexfatal(_("dup(1) failed")"dup(1) failed"); |
| 250 | to_c = fdopen(to_cfd, "w"); |
| 251 | |
| 252 | if (write_header) { |
| 253 | if (freopen((char *) chain->extra, "w", stdout(&__sF[1])) == NULL((void *)0)) |
| 254 | flexfatal(_("freopen(headerfilename) failed")"freopen(headerfilename) failed"); |
| 255 | |
| 256 | filter_apply_chain(chain->next); |
| 257 | to_h = stdout(&__sF[1]); |
| 258 | } |
| 259 | /* |
| 260 | * Now to_c is a pipe to the C branch, and to_h is a pipe to the H |
| 261 | * branch. |
| 262 | */ |
| 263 | |
| 264 | if (write_header) { |
| 265 | fputs(check_4_gnu_m4, to_h); |
| 266 | fputs("m4_changecom`'m4_dnl\n", to_h); |
| 267 | fputs("m4_changequote`'m4_dnl\n", to_h); |
| 268 | fputs("m4_changequote([[,]])[[]]m4_dnl\n", to_h); |
| 269 | fputs("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_h); |
| 270 | fputs("m4_define( [[M4_YY_IN_HEADER]],[[]])m4_dnl\n", |
| 271 | to_h); |
| 272 | fprintf(to_h, "#ifndef %sHEADER_H\n", prefix); |
| 273 | fprintf(to_h, "#define %sHEADER_H 1\n", prefix); |
| 274 | fprintf(to_h, "#define %sIN_HEADER 1\n\n", prefix); |
| 275 | fprintf(to_h, |
| 276 | "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", |
| 277 | headerfilename ? headerfilename : "<stdout>"); |
| 278 | |
| 279 | } |
| 280 | fputs(check_4_gnu_m4, to_c); |
| 281 | fputs("m4_changecom`'m4_dnl\n", to_c); |
| 282 | fputs("m4_changequote`'m4_dnl\n", to_c); |
| 283 | fputs("m4_changequote([[,]])[[]]m4_dnl\n", to_c); |
| 284 | fputs("m4_define([[M4_YY_NOOP]])[[]]m4_dnl\n", to_c); |
| 285 | fprintf(to_c, "m4_define( [[M4_YY_OUTFILE_NAME]],[[%s]])m4_dnl\n", |
| 286 | outfilename ? outfilename : "<stdout>"); |
| 287 | |
| 288 | buf = malloc(readsz); |
| 289 | if (!buf) |
| 290 | flexerror(_("malloc failed in filter_tee_header")"malloc failed in filter_tee_header"); |
| 291 | while (fgets(buf, readsz, stdin(&__sF[0]))) { |
| 292 | fputs(buf, to_c); |
| 293 | if (write_header) |
| 294 | fputs(buf, to_h); |
| 295 | } |
| 296 | |
| 297 | if (write_header) { |
| 298 | fprintf(to_h, "\n"); |
| 299 | |
| 300 | /* |
| 301 | * write a fake line number. It will get fixed by the linedir |
| 302 | * filter. |
| 303 | */ |
| 304 | fprintf(to_h, "#line 4000 \"M4_YY_OUTFILE_NAME\"\n"); |
| 305 | |
| 306 | fprintf(to_h, "#undef %sIN_HEADER\n", prefix); |
| 307 | fprintf(to_h, "#endif /* %sHEADER_H */\n", prefix); |
| 308 | fputs("m4_undefine( [[M4_YY_IN_HEADER]])m4_dnl\n", to_h); |
| 309 | |
| 310 | fflush(to_h); |
| 311 | if (ferror(to_h)(!__isthreaded ? (((to_h)->_flags & 0x0040) != 0) : (ferror )(to_h))) |
| 312 | lerrsf(_("error writing output file %s")"error writing output file %s", |
| 313 | (char *) chain->extra); |
| 314 | |
| 315 | else if (fclose(to_h)) |
| 316 | lerrsf(_("error closing output file %s")"error closing output file %s", |
| 317 | (char *) chain->extra); |
| 318 | } |
| 319 | fflush(to_c); |
| 320 | if (ferror(to_c)(!__isthreaded ? (((to_c)->_flags & 0x0040) != 0) : (ferror )(to_c))) |
| 321 | lerrsf(_("error writing output file %s")"error writing output file %s", |
| 322 | outfilename ? outfilename : "<stdout>"); |
| 323 | |
| 324 | else if (fclose(to_c)) |
| 325 | lerrsf(_("error closing output file %s")"error closing output file %s", |
| 326 | outfilename ? outfilename : "<stdout>"); |
| 327 | |
| 328 | while (wait(0) > 0); |
| 329 | |
| 330 | exit(0); |
| 331 | return 0; |
| 332 | } |
| 333 | |
| 334 | /** Adjust the line numbers in the #line directives of the generated scanner. |
| 335 | * After the m4 expansion, the line numbers are incorrect since the m4 macros |
| 336 | * can add or remove lines. This only adjusts line numbers for generated code, |
| 337 | * not user code. This also happens to be a good place to squeeze multiple |
| 338 | * blank lines into a single blank line. |
| 339 | */ |
| 340 | int |
| 341 | filter_fix_linedirs(struct filter * chain) |
| 342 | { |
| 343 | char *buf; |
| 344 | const int readsz = 512; |
| 345 | int lineno = 1; |
| 346 | bool_Bool in_gen = true1; /* in generated code */ |
| 347 | bool_Bool last_was_blank = false0; |
| 348 | |
| 349 | if (!chain) |
| 350 | return 0; |
| 351 | |
| 352 | buf = malloc(readsz); |
| 353 | if (!buf) |
| 354 | flexerror(_("malloc failed in filter_fix_linedirs")"malloc failed in filter_fix_linedirs"); |
| 355 | |
| 356 | while (fgets(buf, readsz, stdin(&__sF[0]))) { |
| 357 | |
| 358 | regmatch_t m[10]; |
| 359 | |
| 360 | /* Check for #line directive. */ |
| 361 | if (buf[0] == '#' |
| 362 | && regexec(®ex_linedir, buf, 3, m, 0) == 0) { |
| 363 | |
| 364 | int num; |
| 365 | char *fname; |
| 366 | |
| 367 | /* extract the line number and filename */ |
| 368 | num = regmatch_strtol(&m[1], buf, NULL((void *)0), 0); |
Value stored to 'num' is never read | |
| 369 | fname = regmatch_dup(&m[2], buf); |
| 370 | |
| 371 | if (strcmp(fname, |
| 372 | outfilename ? outfilename : "<stdout>") == 0 || |
| 373 | strcmp(fname, headerfilename ? headerfilename : |
| 374 | "<stdout>") == 0) { |
| 375 | |
| 376 | char *s1, *s2; |
| 377 | char filename[MAXLINE2048]; |
| 378 | |
| 379 | s1 = fname; |
| 380 | s2 = filename; |
| 381 | |
| 382 | while ((s2 - filename) < (MAXLINE2048 - 1) && *s1) { |
| 383 | /* Escape the backslash */ |
| 384 | if (*s1 == '\\') |
| 385 | *s2++ = '\\'; |
| 386 | /* Escape the double quote */ |
| 387 | if (*s1 == '\"') |
| 388 | *s2++ = '\\'; |
| 389 | /* Copy the character as usual */ |
| 390 | *s2++ = *s1++; |
| 391 | } |
| 392 | |
| 393 | *s2 = '\0'; |
| 394 | |
| 395 | /* Adjust the line directives. */ |
| 396 | in_gen = true1; |
| 397 | snprintf(buf, readsz, "#line %d \"%s\"\n", |
| 398 | lineno + 1, filename); |
| 399 | } else { |
| 400 | /* |
| 401 | * it's a #line directive for code we didn't |
| 402 | * write |
| 403 | */ |
| 404 | in_gen = false0; |
| 405 | } |
| 406 | |
| 407 | free(fname); |
| 408 | last_was_blank = false0; |
| 409 | } |
| 410 | /* squeeze blank lines from generated code */ |
| 411 | else if (in_gen && |
| 412 | regexec(®ex_blank_line, buf, 0, NULL((void *)0), 0) == 0) { |
| 413 | if (last_was_blank) |
| 414 | continue; |
| 415 | else |
| 416 | last_was_blank = true1; |
| 417 | } else { |
| 418 | /* it's a line of normal, non-empty code. */ |
| 419 | last_was_blank = false0; |
| 420 | } |
| 421 | |
| 422 | fputs(buf, stdout(&__sF[1])); |
| 423 | lineno++; |
| 424 | } |
| 425 | fflush(stdout(&__sF[1])); |
| 426 | if (ferror(stdout)(!__isthreaded ? ((((&__sF[1]))->_flags & 0x0040) != 0) : (ferror)((&__sF[1])))) |
| 427 | lerrsf(_("error writing output file %s")"error writing output file %s", |
| 428 | outfilename ? outfilename : "<stdout>"); |
| 429 | |
| 430 | else if (fclose(stdout(&__sF[1]))) |
| 431 | lerrsf(_("error closing output file %s")"error closing output file %s", |
| 432 | outfilename ? outfilename : "<stdout>"); |
| 433 | |
| 434 | return 0; |
| 435 | } |