/* * Copyright (C) 2016 Andre Noll * * Licensed under the GPL v3, see https://www.gnu.org/licenses/gpl-3.0.html */ #include #include #include #include #include #include #include #include "lsg.h" #include "lopsub-internal.h" #include "lopsub.h" /* * To stringify the result of the *expansion* of a macro argument, we have to * use two levels of macros. */ #define LLS_STRINGIFY_EXPAND(_arg) LLS_STRINGIFY(_arg) #define LLS_STRINGIFY(_arg) #_arg #define LLS_ABI_VERSION_VAR_STRING LLS_STRINGIFY_EXPAND(LLS_ABI_VERSION_VAR) int yylex(void); static void gen_license_header(FILE *out, const char *cmdline, const char *pfx, const char *sfx) { fprintf(out, "%sautogenerated by lopsubgen version %s%s\n", pfx, lls_version(), sfx); if (cmdline) { fprintf(out, "%scommand line:", pfx); fprintf(out, " %s", cmdline); fprintf(out, "%s\n", sfx); } fprintf(out, "%sPublic domain, no copyright claims.%s\n", pfx, sfx); } static void inplace_sanitize(char *src) { for (; *src; src++) if (*src == '-') *src = '_'; } static void inplace_toupper(char *src) { for (; *src; src++) *src = toupper((int)*src); } static void lsg_init_name(struct lsg_name *name) { assert(name->orig); name->sanitized = strdup(name->orig); inplace_sanitize(name->sanitized); name->capitalized = strdup(name->sanitized); inplace_toupper(name->capitalized); } static void print_tabs(int num, FILE *out) { while (--num >= 0) fprintf(out, "\t"); } static void format_c_text(const char *text, const char *member, int indent, FILE *out) { int i; if (!text) return; print_tabs(indent, out); fprintf(out, ".%s = (char []) {", member); for (i = 0; text[i]; i++) { if ((i % 8) == 0) { fprintf(out, "\n"); print_tabs(indent + 1, out); } fprintf(out, "0x%02x,", (unsigned char)text[i]); } fprintf(out, "0x00\n"); print_tabs(indent, out); fprintf(out, "},\n"); } static void format_flags(struct lsg_option *opt, FILE *out) { fprintf(out, "\t\t\t\t\t.flags = 0"); if (opt->multiple) fprintf(out, " | LLS_MULTIPLE"); if (opt->required) fprintf(out, " | LLS_REQUIRED"); if (opt->ignored) fprintf(out, " | LLS_IGNORED"); if (opt->default_val) fprintf(out, " | LLS_HAS_DEFAULT"); fprintf(out, ",\n"); } static int string_literal(const char *src, FILE *out) { int len = 0; len += fprintf(out, "\""); while (src && *src) { if (*src == '"' || *src == '\\') len += fprintf(out, "\\"); len += fprintf(out, "%c", *src); src++; } len += fprintf(out, "\""); return len; } static void format_man_text(FILE *out, bool bol, const char *text) { int i; if (!text) return; for (i = 0; text[i]; i++) { switch (text[i]) { case '\n': fprintf(out, "\n"); bol = true; continue; case '.': case '\'': if (bol) fprintf(out, "\\&"); break; case '\\': fprintf(out, "\\"); break; case ' ': case '\t': if (bol) continue; } bol = false; fprintf(out, "%c", text[i]); } } static int format_option_arg(const struct lsg_option *opt, FILE *out, bool man_format) { char *typestr = opt->typestr? opt->typestr : "val"; int arg_info, len = 0; if (opt->ignored) return 0; if (!opt->arg_info) arg_info = 0; else if (strcmp(opt->arg_info, "LLS_NO_ARGUMENT") == 0) arg_info = 0; else if (strcmp(opt->arg_info, "LLS_OPTIONAL_ARGUMENT") == 0) arg_info = -1; else if (strcmp(opt->arg_info, "LLS_REQUIRED_ARGUMENT") == 0) arg_info = 1; else assert(0); if (arg_info == -1) len += fprintf(out, "["); if (arg_info != 0) { len += fprintf(out, "=<"); if (man_format) len += fprintf(out, "%s", typestr); else { len += fprintf(out, "\""); len += string_literal(typestr, out); len += fprintf(out, "\""); } fprintf(out, ">"); } if (arg_info == -1) len += fprintf(out, "]"); return len; } static void format_synopsis(const struct lsg_command *cmd, FILE *out, bool man_format) { int j, len; if (man_format) fprintf(out, "\\&"); if (cmd->synopsis) { if (man_format) format_man_text(out, true, cmd->synopsis); else { string_literal(cmd->synopsis, out); fprintf(out, ",\n"); } return; } if (!man_format) fprintf(out, "\""); len = strlen(cmd->name.orig) + 8; for (j = 0; j < cmd->num_options; j++) { struct lsg_option *opt = cmd->options + j; if (opt->ignored) continue; if (j > 0) { if (!man_format && len > 70) { fprintf(out, "\\n\"\n\t\t\t\t\" "); len = 8; } else len += fprintf(out, " "); } if (!opt->required) len += fprintf(out, "["); len += fprintf(out, "--%s", opt->name.orig); len += format_option_arg(opt, out, man_format); if (!opt->required) len += fprintf(out, "]"); } if (cmd->non_opts_name) { len += strlen(cmd->non_opts_name); if (!man_format && len > 70) fprintf(out, "\\n\"\n\t\t\t\t\" "); if (cmd->num_options > 0) fprintf(out, " [--]"); fprintf(out, " %s", cmd->non_opts_name); } if (!man_format) fprintf(out, "\","); } static void gen_c_options(const struct lsg_command *cmd, FILE *out) { int i, j; if (cmd->num_options == 0) return; fprintf(out, "\t\t.options = (struct lls_option[]) {\n"); for (j = 0; j < cmd->num_options; j++) { struct lsg_option *opt = cmd->options + j; fprintf(out, "\t\t\t{\n"); fprintf(out, "\t\t\t\t.name = \"%s\",\n", opt->name.orig); if (opt->short_opt) fprintf(out, "\t\t\t\t.short_opt = '%c',\n", opt->short_opt); if (opt->summary) { fprintf(out, "\t\t\t\t.summary = "); string_literal(opt->summary, out); fprintf(out, ",\n"); } if (opt->arg_info) fprintf(out, "\t\t\t\t.arg_info = %s,\n", opt->arg_info); if (opt->arg_type) fprintf(out, "\t\t\t\t.arg_type = %s,\n", opt->arg_type); if (opt->typestr) { fprintf(out, "\t\t\t\t.typestr = "); string_literal(opt->typestr, out); fprintf(out, ",\n"); } fprintf(out, "\t\t\t\t.values = "); if (opt->values) { fprintf(out, "(union lls_val *)(union lls_val[]) {\n"); for (i = 0; i < opt->num_values; i++) fprintf(out, "\t\t\t\t\t{.string_val = %s},\n", opt->value_literals[i]); fprintf(out, "\t\t\t\t\t{.string_val = NULL}\n"); fprintf(out, "\t\t\t\t},\n"); } else fprintf(out, "NULL,\n"); format_flags(opt, out); if (opt->default_val) { bool string = strcmp(opt->arg_type, "LLS_STRING") == 0; fprintf(out, "\t\t\t\t.default_val = {"); if (string) { if (opt->values) fprintf(out, ".uint32_val = "); else { fprintf(out, ".string_val = "); string_literal(opt->default_val, out); } } else if (strcmp(opt->arg_type, "LLS_INT32") == 0) fprintf(out, ".int32_val = "); else if (strcmp(opt->arg_type, "LLS_UINT32") == 0) fprintf(out, ".uint32_val = "); else if (strcmp(opt->arg_type, "LLS_INT64") == 0) fprintf(out, ".int64_val = "); else if (strcmp(opt->arg_type, "LLS_UINT64") == 0) fprintf(out, ".uint64_val = "); if (!string || opt->values) fprintf(out, "%s", opt->default_val); fprintf(out, "},\n"); } format_c_text(opt->help, "help", 5, out); fprintf(out, "\t\t\t},\n"); } fprintf(out, "\t\t\t{\n\t\t\t\t\t.name = NULL\n\t\t\t}\n"); fprintf(out, "\t\t}\n"); } static void format_command(const struct lsg_command *cmd, FILE *out) { if (!cmd->name.orig) { fprintf(out, "{.name = NULL}\n"); return; } fprintf(out, "{\n\t\t.name = \"%s\",\n", cmd->name.orig); fprintf(out, "\t\t.purpose = "); string_literal(cmd->purpose, out); fprintf(out, ",\n"); format_c_text(cmd->description, "description", 3, out); if (cmd->non_opts_name || cmd->synopsis) fprintf(out, "\t\t.non_opts_name = \"%s\",\n", cmd->non_opts_name? cmd->non_opts_name : ""); fprintf(out, "\t\t.synopsis = "); format_synopsis(cmd, out, false); format_c_text(cmd->closing, "closing", 3, out); fprintf(out, "\n\t\t.user_data = &lsg_%s_com_%s_user_data,\n", suite.name.sanitized, cmd->name.sanitized); fprintf(out, "\t\t.num_options = %d,\n", cmd->num_options); gen_c_options(cmd, out); fprintf(out, "\t}"); } static void format_user_data(const struct lsg_command *cmd, FILE *out) { if (!cmd->name.orig) return; fprintf(out, "extern const void *lsg_%s_com_%s_user_data " "__attribute__ ((weak));\n", suite.name.sanitized, cmd->name.sanitized ); } static void gen_c(const char *outpath, const char *cmdline) { int i; FILE *out = fopen(outpath, "w"); if (!out) { perror("fopen"); exit(EXIT_FAILURE); } gen_license_header(out, cmdline, "/* ", " */"); fprintf(out, "#include \n"); fprintf(out, "#include \n"); fprintf(out, "#include \n"); fprintf(out, "#include \n"); fprintf(out, "__attribute__ ((unused)) " "static const unsigned *abi_version = &%s;\n", LLS_ABI_VERSION_VAR_STRING); fprintf(out, "#if LLS_ABI_VERSION - %d\n", LLS_ABI_VERSION); fprintf(out, "#error: \"ABI version mismatch: header version " "differs from lopsubgen version\"\n"); fprintf(out, "#endif\n"); for (i = 0; i <= suite.num_subcommands; i++) format_user_data(suite.commands + i, out); fprintf(out, "static const struct lls_suite the_%s_suite = {\n", suite.name.sanitized); fprintf(out, "\t.name = \"%s\",\n", suite.name.orig); if (suite.caption) fprintf(out, "\t.caption = \"%s\",\n", suite.caption); fprintf(out, "\t.num_subcommands = %d,\n", suite.num_subcommands); fprintf(out, "\t.commands = (struct lls_command[]) {\n"); for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; fprintf(out, "%s", i? ", " : "\t\t"); format_command(cmd, out); } fprintf(out, ", {\n\t\t\t.name = NULL\n\t\t}\n"); fprintf(out, "\t}\n"); fprintf(out, "};\n"); fprintf(out, "const struct lls_suite *%s_suite = &the_%s_suite;\n", suite.name.sanitized, suite.name.sanitized); fclose(out); } static inline bool has_arg(struct lsg_option *opt) { if (!opt->arg_info) return false; return strcmp(opt->arg_info, "LLS_NO_ARGUMENT"); } static void gen_header(const char *outpath, const char *cmdline) { int i, j, k; FILE *out = fopen(outpath, "w"); char *name; if (!out) { perror("fopen"); exit(EXIT_FAILURE); } gen_license_header(out, cmdline, "/* ", " */"); /* generate command enum */ fprintf(out, "extern const struct lls_suite *%s_suite;\n", suite.name.sanitized); fprintf(out, "#define LSG_%s_SUBCOMMANDS \\\n", suite.name.capitalized); for (i = 1; i <= suite.num_subcommands; i++) fprintf(out, "\tLSG_%s_CMD(%s), \\\n", suite.name.capitalized, suite.commands[i].name.sanitized); fprintf(out, "\n"); fprintf(out, "#define LSG_%s_COMMANDS \\\n", suite.name.capitalized); name = suite.commands[0].name.sanitized; fprintf(out, "\tLSG_%s_CMD(%s), \\\n", suite.name.capitalized, name? name : "SUPERCOMMAND_UNAVAILABLE"); fprintf(out, "\tLSG_%s_SUBCOMMANDS\n", suite.name.capitalized); fprintf(out, "enum lsg_%s_command {\n", suite.name.sanitized); for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; char *name = cmd->name.capitalized; if (!name) name = "SUPERCOMMAND_UNAVAILABLE"; fprintf(out, "\tLSG_%s_CMD_%s,\n", suite.name.capitalized, name); } fprintf(out, "};\n#define LSG_NUM_%s_SUBCOMMANDS %u\n", suite.name.capitalized, suite.num_subcommands); fprintf(out, "#define LSG_%s_AUX_INFOS \\\n", suite.name.capitalized); for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; char *ai = cmd->aux_info; if (!ai) { ai = suite.aux_info_default; if (!ai) ai = "0"; } fprintf(out, "\t%s_AUX_INFO(%s) /* %s */ \\\n", suite.name.capitalized, ai, cmd->name.orig? cmd->name.orig : "NO_SUPERCOMMAND"); } fprintf(out, "\n"); /* generate one option enum per command */ for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; if (!cmd->name.orig) continue; fprintf(out, "enum lsg_%s_%s_option {\n", suite.name.sanitized, cmd->name.sanitized); for (j = 0; j < cmd->num_options; j++) { struct lsg_option *opt = cmd->options + j; char suffix[20] = ""; if (opt->ignored) sprintf(suffix, "%d", j); fprintf(out, "\tLSG_%s_%s_OPT_%s%s,\n", suite.name.capitalized, cmd->name.capitalized, opt->name.capitalized, suffix ); } fprintf(out, "\tLSG_NUM_%s_%s_OPTIONS\n};\n", suite.name.capitalized, cmd->name.capitalized); } /* generate enumeration for options of type enum */ for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; if (!cmd->name.orig) continue; for (j = 0; j < cmd->num_options; j++) { struct lsg_option *opt = cmd->options + j; if (!opt->values) continue; fprintf(out, "/* cmd %s, opt %s */\n", cmd->name.orig, opt->name.orig); fprintf(out, "enum {"); for (k = 0; k < opt->num_values; k++) fprintf(out, "%s, ", opt->value_ids[k]); fprintf(out, "LSG_NUM_%s_%s_%s_VALUES};\n", suite.name.capitalized, cmd->name.capitalized, opt->name.capitalized); } } for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; if (!cmd->name.orig) continue; fprintf(out, "#define LSG_%s_%s_SHORT_OPTS ", suite.name.capitalized, cmd->name.capitalized); for (j = 0; j < cmd->num_options; j++) { struct lsg_option *opt = cmd->options + j; if (!opt->short_opt) continue; fprintf(out, "\"-%c%s\"%s", opt->short_opt, has_arg(opt)? "=" : "", j == cmd->num_options - 1? "" : ", "); } fprintf(out, "\n"); fprintf(out, "#define LSG_%s_%s_LONG_OPTS ", suite.name.capitalized, cmd->name.capitalized); for (j = 0; j < cmd->num_options; j++) { struct lsg_option *opt = cmd->options + j; fprintf(out, "\"--%s%s\"%s", opt->name.orig, has_arg(opt)? "=" : "", j == cmd->num_options - 1? "" : ", "); } fprintf(out, "\n"); fprintf(out, "#define LSG_%s_%s_OPTS " "LSG_%s_%s_SHORT_OPTS, LSG_%s_%s_LONG_OPTS\n", suite.name.capitalized, cmd->name.capitalized, suite.name.capitalized, cmd->name.capitalized, suite.name.capitalized, cmd->name.capitalized ); } fclose(out); } static void check_option(struct lsg_option *opt) { if (!opt->arg_info || !strcmp(opt->arg_info, "no_arg")) return; if (opt->arg_type && strcmp(opt->arg_type, "none")) return; fprintf(stderr, "option '%s': inconsistent arg_type/arg_info\n", opt->name.orig); exit(EXIT_FAILURE); } static void sanity_check(void) { int i, j; if (suite.num_subcommands == 0 && !suite.commands) { fprintf(stderr, "no (sub)commands defined\n"); exit(EXIT_FAILURE); } for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; if (!cmd->name.orig) continue; for (j = 0; j < cmd->num_options; j++) check_option(cmd->options + j); } } static void run_yylex(void) { int i, j; yylex(); if (!suite.name.orig) suite.name.orig = strdup("lopsubgen"); sanity_check(); lsg_init_name(&suite.name); for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; if (!cmd->name.orig) continue; lsg_init_name(&cmd->name); for (j = 0; j < cmd->num_options; j++) lsg_init_name(&cmd->options[j].name); } for (i = 0; i < suite.num_sections; i++) lsg_init_name(&suite.sections[i].name); } #ifdef STAGE1 int main(int argc, char **argv) { run_yylex(); gen_c("lopsubgen.lsg.c", NULL); gen_header("lopsubgen.lsg.h", NULL); return EXIT_SUCCESS; } #else /* STAGE1 */ #include "lopsubgen.lsg.h" static char *get_output_path(const char *suffix, const char *arg, const struct lls_parse_result *lpr) { size_t len; char *result, *output_dir; output_dir = OPT_STRING_VAL(OUTPUT_DIR, lpr); if (arg && arg[0] == '/') { result = strdup(arg); assert(result); return result; } if (arg) { /* relative path */ len = strlen(output_dir) + strlen(arg) + 1; result = malloc(len + 1); assert(result); sprintf(result, "%s/%s", output_dir, arg); return result; } /* default: suite name plus suffix */ len = strlen(output_dir) + 1 /* slash */ + strlen(suite.name.orig) + 1 /* dot */ + 3 /* "lsg" */ + 1 /* dot */ + strlen(suffix); result = malloc(len + 1); assert(result); sprintf(result, "%s/%s.lsg.%s", output_dir, suite.name.orig, suffix); return result; } static void gen_man(struct lls_parse_result *lpr, const char *cmdline) { int i; time_t t = 0; struct tm *tmp; FILE *out; char *outpath = get_output_path("man", OPT_STRING_VAL(GEN_MAN, lpr), lpr); out = fopen(outpath, "w"); free(outpath); if (!out) { perror("fopen"); exit(EXIT_FAILURE); } gen_license_header(out, cmdline, ".\\\" ", ""); if (suite.commands[0].name.orig) { char date[200]; const char *version_string; if (!suite.date) { /* * If the SOURCE_DATE_EPOCH environment variable * contains a positive integer in the time_t range, use * that instead of the current time. See: * * for more information. */ char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); if (source_date_epoch != NULL) t = strtoll(source_date_epoch, NULL, 10); if (t <= 0) t = time(NULL); tmp = gmtime(&t); if (tmp == NULL) { perror("gmtime"); exit(EXIT_FAILURE); } if (strftime(date, sizeof(date), "%B %Y", tmp) == 0) { fprintf(stderr, "strftime returned 0\n"); exit(EXIT_FAILURE); } } if (OPT_GIVEN(VERSION_STRING, lpr)) version_string = OPT_STRING_VAL(VERSION_STRING, lpr); else version_string = suite.version_string? suite.version_string : ""; fprintf(out, ".TH %s \"%s\" \"%s\" \"%s\" \"%s\"\n", suite.title? suite.title : suite.commands[0].name.orig, suite.mansect? suite.mansect : "1", suite.date? suite.date : date, version_string, suite.manual_title? suite.manual_title : "User commands" ); } for (i = 0; i <= suite.num_subcommands; i++) { struct lsg_command *cmd = suite.commands + i; int opt_num; if (!cmd->name.orig) continue; if (i == 0) { fprintf(out, ".SH NAME\n"); fprintf(out, ".B\n%s \\- ", cmd->name.orig); format_man_text(out, false, cmd->purpose); } else { if (i == 1 && suite.caption) { char *caption = strdup(suite.caption); inplace_toupper(caption); fprintf(out, ".SH %s\n.P\n", caption); free(caption); } if (i == 1 && suite.introduction) format_man_text(out, true, suite.introduction); fprintf(out, ".SS \n%s \\- ", cmd->name.orig); format_man_text(out, false, cmd->purpose); } fprintf(out, "\n.P\n"); if (i == 0) fprintf(out, ".SH SYNOPSIS\n"); else fprintf(out, "Usage: \n"); fprintf(out, ".B %s\n", cmd->name.orig); format_synopsis(cmd, out, true); fprintf(out, "\n.P\n"); if (cmd->description) { if (i == 0) fprintf(out, ".SH DESCRIPTION\n"); format_man_text(out, true, cmd->description); } if (cmd->num_options > 0) if (i == 0) fprintf(out, ".SH OPTIONS\n"); for (opt_num = 0; opt_num < cmd->num_options; opt_num++) { struct lsg_option *opt = cmd->options + opt_num; if (opt->ignored) { fprintf(out, ".SS "); format_man_text(out, false, opt->summary); fprintf(out, "\n"); if (opt->help) format_man_text(out, true, opt->help); continue; } fprintf(out, ".TP\n"); if (opt->short_opt != '\0') fprintf(out, "\\fB\\-%c\\fR, ", opt->short_opt); fprintf(out, "\\fB\\-\\-%s\\fR", opt->name.orig); format_option_arg(opt, out, true /* man_format */); fprintf(out, "\n"); format_man_text(out, true, opt->summary); fprintf(out, "\n"); if (opt->values) { unsigned n, dflt; fprintf(out, ".IP\n"); fprintf(out, "values:\n"); dflt = opt->default_val? atoi(opt->default_val) : 0; for (n = 0; n < opt->num_values; n++) { if (n == dflt) fprintf(out, ".B "); fprintf(out, "%s%s\n", opt->values[n], n == opt->num_values - 1? "" : ","); } } else if (opt->default_val) { fprintf(out, ".IP\n"); fprintf(out, "default: "); format_man_text(out, false, opt->default_val); fprintf(out, "\n.P\n"); } if (opt->help) { fprintf(out, ".IP\n"); format_man_text(out, true, opt->help); } } fprintf(out, ".PP\n"); if (cmd->aux_info) { char *pfx = suite.aux_info_prefix; fprintf(out, ".RS\n"); if (pfx) fprintf(out, "%s ", pfx); fprintf(out, "%s\n.PP\n", cmd->aux_info); fprintf(out, ".RE\n"); } if (cmd->closing) format_man_text(out, true, cmd->closing); } if (suite.conclusion) format_man_text(out, true, suite.conclusion); for (i = 0; i < suite.num_sections; i++) { struct lsg_section *sec = suite.sections + i; fprintf(out, "\n.P\n.SH \"%s\"\n", sec->name.capitalized); fprintf(out, "%s\n.P\n", sec->text); } fclose(out); } int main(int argc, char **argv) { const struct lls_command *cmd = lls_cmd(0, lopsubgen_suite); char *errctx, *outpath, *cmdline; struct lls_parse_result *lpr; int i, ret; /* Make a copy of the command line because lls_parse() permutes argv[] */ for (i = 0, ret = 0; argv[i]; i++) ret += strlen(argv[i]) + 1; cmdline = malloc(ret + 1); assert(cmdline); for (i = 0, ret = 0; argv[i]; i++) ret += sprintf(cmdline + ret, "%s ", argv[i]); cmdline[ret - 1] = '\0'; ret = lls_parse(argc, argv, cmd, &lpr, &errctx); if (ret < 0) { if (errctx) fprintf(stderr, "%s\n", errctx); fprintf(stderr, "%s\n", lls_strerror(-ret)); return EXIT_FAILURE; } if (OPT_GIVEN(VERSION, lpr)) { printf("lopsubgen-%s\n", lls_version()); return EXIT_SUCCESS; } if (OPT_GIVEN(HELP, lpr)) { char *help; if (OPT_GIVEN(HELP, lpr) > 1) help = lls_long_help(cmd); else help = lls_short_help(cmd); printf("%s", help); free(help); return EXIT_SUCCESS; } run_yylex(); if (OPT_GIVEN(GEN_C, lpr)) { outpath = get_output_path("c", OPT_STRING_VAL(GEN_C, lpr), lpr); gen_c(outpath, cmdline); free(outpath); } if (OPT_GIVEN(GEN_HEADER, lpr)) { outpath = get_output_path("h", OPT_STRING_VAL(GEN_HEADER, lpr), lpr); gen_header(outpath, cmdline); free(outpath); } if (OPT_GIVEN(GEN_MAN, lpr)) gen_man(lpr, cmdline); return EXIT_SUCCESS; } #endif /* STAGE1 */