/** * Copyright (C) 1999-2010 Free Software Foundation, Inc. * * This file is part of GNU gengetopt * * GNU gengetopt is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3, or (at your option) * any later version. * * GNU gengetopt is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along * with gengetopt; see the file COPYING. If not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include // for pair #include extern "C" { #include "argsdef.h" #include "global_opts.h" }; #include "ggo_options.h" #include "gm.h" #include "my_sstream.h" #include "groups.h" #include "skels/option_arg.h" #include "skels/required_option.h" #include "skels/dependant_option.h" #include "skels/generic_option.h" #include "skels/group_option.h" #include "skels/group_counter.h" #include "skels/handle_help.h" #include "skels/handle_version.h" #include "skels/print_help_string.h" #include "skels/multiple_opt_list.h" #include "skels/multiple_fill_array.h" #include "skels/free_string.h" #include "skels/free_multiple.h" #include "skels/reset_group.h" #include "skels/exit_failure.h" #include "skels/update_given.h" #include "skels/given_field.h" #include "skels/clear_given.h" #include "skels/clear_arg.h" #include "skels/free_list.h" #include "skels/file_save.h" #include "skels/file_save_multiple.h" #include "skels/init_args_info.h" #include "skels/custom_getopt_gen.h" #include "skels/check_modes.h" #include "skels/enum_decl.h" #include "gm_utils.h" #include "fileutils.h" #ifndef FIX_UNUSED #define FIX_UNUSED(X) (void) (X) #endif // FIX_UNUSED #define MAX_STARTING_COLUMN 32 #define EXE_NAME "argv[0]" #define PARSER_NAME_PREFIX (c_source_gen_class::parser_name + "_") #define OPTION_VALUES_NAME(n) (PARSER_NAME_PREFIX + n + "_values") using std::endl; using std::set; extern char * gengetopt_package; extern char * gengetopt_version; extern char * gengetopt_purpose; extern char * gengetopt_description; extern char * gengetopt_usage; extern char * gengetopt_input_filename; extern groups_collection_t gengetopt_groups; extern modes_collection_t gengetopt_modes; // a map where for each mode we store the corresponding given field names // and the options typedef std::pair OptionValueElem; typedef std::list ModeOptions; typedef std::map ModeOptionMap; static ModeOptionMap modeOptionMap; static const ModeOptionMap &getModeOptionMap() { if (modeOptionMap.size() == 0) { // it's the first time, so we build it struct gengetopt_option * opt; foropt { if (opt->mode_value) { modeOptionMap[opt->mode_value].push_back (std::make_pair("args_info->" + string(opt->var_arg) + "_given", string("\"--") + opt->long_opt + "\"")); } } } return modeOptionMap; } // a map associating to a mode the list of gengetopt_options typedef std::map ModeOptMap; static ModeOptMap modeOptMap; static const ModeOptMap &getModeOptMap() { if (modeOptMap.size() == 0) { // it's the first time, so we build it struct gengetopt_option * opt; foropt { if (opt->mode_value) { modeOptMap[opt->mode_value].push_back(opt); } } } return modeOptMap; } static void _generate_option_arg(ostream &stream, unsigned int indent, struct gengetopt_option * opt); static void generate_help_desc_print(ostream &stream, unsigned int desc_column, const char *descript, const char *defval, const string &values, const string &show_required_string); CmdlineParserCreator::CmdlineParserCreator (char *function_name, char *struct_name, char *unamed_options_, char *filename_, char *header_ext, char *c_ext, bool long_help_, bool no_handle_help_, bool no_help_, bool no_handle_version_, bool no_version_, bool no_handle_error_, bool conf_parser_, bool string_parser_, bool gen_version, bool gen_getopt, bool no_options_, const string &comment_, const string &outdir, const string &header_outdir, const string &src_outdir, const string &show_required) : filename (filename_), args_info_name (struct_name), output_dir (outdir), header_output_dir (header_outdir), src_output_dir (src_outdir), comment (comment_), unamed_options (unamed_options_), show_required_string (show_required), long_help (long_help_), no_handle_help (no_handle_help_), no_help (no_help_), no_handle_version (no_handle_version_), no_version (no_version_), no_handle_error (no_handle_error_), conf_parser (conf_parser_), string_parser (string_parser_), gen_gengetopt_version (gen_version), tab_indentation (0) { parser_function_name = canonize_names (function_name); c_filename = create_filename (filename, c_ext); header_filename = create_filename (filename, header_ext); // header_gen_class const string stripped_header_file_name = strip_path (filename); set_header_file_name (stripped_header_file_name); header_gen_class::set_header_file_ext (header_ext); c_source_gen_class::set_header_file_ext (header_ext); if (gen_gengetopt_version) header_gen_class::set_generator_version ("version " VERSION); const string my_ifndefname = to_upper (strip_path (stripped_header_file_name)); set_ifndefname (canonize_names (my_ifndefname.c_str ())); header_gen_class::set_parser_name (parser_function_name); const string my_package_var_name = to_upper (parser_function_name) + "_PACKAGE"; const string my_version_var_name = to_upper (parser_function_name) + "_VERSION"; header_gen_class::set_package_var_name (my_package_var_name); c_source_gen_class::set_package_var_name (my_package_var_name); header_gen_class::set_version_var_name (my_version_var_name); c_source_gen_class::set_version_var_name (my_version_var_name); header_gen_class::set_args_info (args_info_name); c_source_gen_class::set_args_info (args_info_name); const string uppersand = "\""; // if no_options then we don't need to generate update_arg, // but if we need to handle help or version we still need to generate it set_no_options (no_options_ && !no_handle_help && !no_handle_version); if (gengetopt_package) set_package_var_val (uppersand + gengetopt_package + uppersand); else set_package_var_val ("PACKAGE"); if (gengetopt_version) set_version_var_val (uppersand + gengetopt_version + uppersand); else set_version_var_val ("VERSION"); header_gen_class::set_generate_config_parser (conf_parser); header_gen_class::set_generate_string_parser (string_parser); c_source_gen_class::set_generate_string_parser (string_parser); // c_source_gen_class set_command_line (comment); if (gen_gengetopt_version) c_source_gen_class::set_generator_version ("version " VERSION); c_source_gen_class::set_parser_name (parser_function_name); set_source_name (filename); ostringstream exit_failure_str; exit_failure_gen_class exit_gen; exit_gen.set_parser_name (c_source_gen_class::parser_name); exit_gen.set_handle_error (! no_handle_error); exit_gen.generate_exit_failure (exit_failure_str); set_final_exit (exit_failure_str.str ()); set_conf_parser (conf_parser); set_cmd_list (conf_parser || string_parser); set_include_getopt (gen_getopt); struct gengetopt_option * opt; gen_strdup = (unamed_options != 0 || conf_parser || string_parser); if (! gen_strdup) { foropt if (opt->type != ARG_FLAG || opt->type != ARG_NO) { gen_strdup = true; break; } } set_do_generate_strdup(gen_strdup); set_check_possible_values(has_values()); set_multiple_token_functions(has_multiple_options_with_type()); set_multiple_options_with_default(has_multiple_options_with_default()); set_multiple_options(has_multiple_options()); set_multiple_options_string(has_multiple_options_string()); set_multiple_options_all_string(has_multiple_options_all_string()); set_has_typed_options(has_options_with_type()); set_has_modes(has_options_with_mode()); set_handle_unamed(unamed_options); set_check_required_options(has_required() || has_dependencies() || has_multiple_options()); set_purpose(generate_purpose()); set_description(generate_description()); set_no_package((gengetopt_package == 0)); c_source_gen_class::set_has_hidden(has_hidden_options()); header_gen_class::set_has_hidden(c_source_gen_class::has_hidden); c_source_gen_class::set_has_details(has_options_with_details()); header_gen_class::set_has_details(c_source_gen_class::has_details); set_has_arg_types(); } void CmdlineParserCreator::set_has_arg_types() { struct gengetopt_option * opt; set_has_arg_flag(false); set_has_arg_string(false); set_has_arg_int(false); set_has_arg_short(false); set_has_arg_long(false); set_has_arg_float(false); set_has_arg_double(false); set_has_arg_longdouble(false); set_has_arg_longlong(false); foropt { switch (opt->type) { case ARG_NO: break; case ARG_FLAG: set_has_arg_flag(true); break; case ARG_STRING: set_has_arg_string(true); break; case ARG_INT: set_has_arg_int(true); break; case ARG_SHORT: set_has_arg_short(true); break; case ARG_LONG: set_has_arg_long(true); break; case ARG_FLOAT: set_has_arg_float(true); break; case ARG_DOUBLE: set_has_arg_double(true); break; case ARG_LONGDOUBLE: set_has_arg_longdouble(true); break; case ARG_LONGLONG: set_has_arg_longlong(true); break; case ARG_ENUM: set_has_arg_enum(true); break; default: fprintf (stderr, "gengetopt: bug found in %s:%d!!\n", __FILE__, __LINE__); abort (); } } } void CmdlineParserCreator::generateBreak(ostream &stream, unsigned int indent) { string indent_str (indent, ' '); stream << endl; stream << indent_str; stream << "break;"; } int CmdlineParserCreator::generate () { int head_result; head_result = generate_header_file (); if (head_result) return head_result; return generate_source (); } int CmdlineParserCreator::generate_header_file () { if (! gengetopt_options.size()) { fprintf (stderr, "gengetopt: none option given\n"); return 1; } /* ****************************************************** */ /* HEADER FILE******************************************* */ /* ****************************************************** */ string header_file = header_filename; if (header_output_dir.size()) header_file = header_output_dir + "/" + header_file; else if (output_dir.size()) header_file = output_dir + "/" + header_file; ofstream *output_file = open_fstream (header_file.c_str()); generate_header (*output_file); output_file->close (); delete output_file; return 0; } /** * generate the enum value from a given option * @param name the (canonized) name of the option * @param val the value of the option * @return the enum value string */ static const string from_value_to_enum(const string &name, const string &val) { return name + "_arg_" + canonize_enum(val); } void CmdlineParserCreator::generate_enum_types(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; FIX_UNUSED (indent); if (has_arg_enum) stream << endl; foropt { // if type is enum then it should also have values (checked during parsing) // but it's better to check it if (opt->type == ARG_ENUM) { if (! (opt->acceptedvalues)) { fprintf (stderr, "gengetopt: bug found in %s:%d!!\n", __FILE__, __LINE__); abort (); } ostringstream enum_values; enum_decl_gen_class enum_gen; enum_gen.set_var_arg(opt->var_arg); for (AcceptedValues::const_iterator it = opt->acceptedvalues->begin(); it != opt->acceptedvalues->end(); ++it) { enum_values << ", "; // the first enum element is set to 0 enum_values << from_value_to_enum(opt->var_arg, *it); if (it == opt->acceptedvalues->begin()) enum_values << " = 0"; } enum_gen.set_enum_values(enum_values.str()); enum_gen.generate_enum_decl(stream); } } } void CmdlineParserCreator::generate_option_arg(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; foropt { _generate_option_arg (stream, indent, opt); } } void _generate_option_arg(ostream &stream, unsigned int indent, struct gengetopt_option *opt) { option_arg_gen_class option_arg_gen; string type = ""; if (opt->type) type = arg_types[opt->type]; string origtype = "char *"; if (opt->multiple) { type += "*"; origtype += "*"; option_arg_gen.set_multiple(true); } else { option_arg_gen.set_multiple(false); } option_arg_gen.set_type(type); option_arg_gen.set_origtype(origtype); option_arg_gen.set_flag_arg((opt->type == ARG_FLAG)); option_arg_gen.set_desc(opt->desc); option_arg_gen.set_name(opt->var_arg); option_arg_gen.set_has_arg(opt->type != ARG_NO); option_arg_gen.set_has_enum(opt->type == ARG_ENUM); if (opt->default_given) { option_arg_gen.set_has_default(true); option_arg_gen.set_default_value(opt->default_string); } if (opt->type == ARG_FLAG) { option_arg_gen.set_default_on(opt->flagstat); } if (opt->type == ARG_LONGLONG) { // the fallback type in case longlong is not supported by the compiler string longtype = arg_types[ARG_LONG]; if (opt->multiple) longtype += "*"; option_arg_gen.set_long_long_arg(true); option_arg_gen.set_longtype(longtype); } option_arg_gen.generate_option_arg(stream, indent); } void CmdlineParserCreator::generate_option_given(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; string indent_str (indent, ' '); bool first = true; given_field_gen_class given_gen; foropt { switch (opt->type) { case ARG_NO: case ARG_FLAG: case ARG_STRING: case ARG_INT: case ARG_SHORT: case ARG_LONG: case ARG_FLOAT: case ARG_DOUBLE: case ARG_LONGDOUBLE: case ARG_LONGLONG: case ARG_ENUM: break; default: fprintf (stderr, "gengetopt: bug found in %s:%d!!\n", __FILE__, __LINE__); abort (); } if (! first) stream << indent_str; else first = false; given_gen.set_arg_name (opt->var_arg); given_gen.set_long_opt (opt->long_opt); given_gen.set_group (opt->multiple && opt->group_value); given_gen.generate_given_field (stream); } if (unamed_options) { stream << endl; stream << indent_str; stream << "char **inputs ; /**< @brief unamed options (options without names) */\n" ; stream << indent_str; stream << "unsigned inputs_num ; /**< @brief unamed options number */" ; } } void CmdlineParserCreator::generate_option_values_decl(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; bool first = true; FIX_UNUSED (indent); foropt { if (opt->acceptedvalues) { if (first) { first = false; } stream << "extern const char *" << OPTION_VALUES_NAME(opt->var_arg) << "[]; /**< @brief Possible values for " << opt->long_opt << ". */\n"; } } if (! first) stream << "\n"; } void CmdlineParserCreator::generate_option_values(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; bool first = true; FIX_UNUSED (indent); foropt { if (opt->acceptedvalues) { if (first) { first = false; } stream << "const char *" << OPTION_VALUES_NAME(opt->var_arg) << "[] = {" << opt->acceptedvalues->toString(false) << ", 0}; /*< Possible values for " << opt->long_opt << ". */\n"; } } if (! first) stream << "\n"; } static void generate_option_usage_string(gengetopt_option * opt, ostream &usage) { const char *type_str; usage << " "; if (!opt->required) usage << "["; switch (opt->type) { case ARG_NO: case ARG_FLAG: if (opt->short_opt) usage << "-" << opt->short_opt << "|"; usage << "--" << opt->long_opt; break; case ARG_INT: case ARG_SHORT: case ARG_LONG: case ARG_FLOAT: case ARG_DOUBLE: case ARG_LONGDOUBLE: case ARG_LONGLONG: case ARG_STRING: case ARG_ENUM: if (opt->type_str) type_str = opt->type_str; else type_str = arg_names[opt->type]; if (opt->short_opt) usage << "-" << opt->short_opt << type_str << "|"; usage << "--" << opt->long_opt << "=" << type_str; break; default: fprintf (stderr, "gengetopt: bug found in %s:%d!!\n", __FILE__, __LINE__); abort (); } if (!opt->required) usage << "]"; } const string CmdlineParserCreator::generate_usage_string(bool use_config_package) { FIX_UNUSED (use_config_package); // if specified by the programmer, the usage string has the precedence if (gengetopt_usage) { return gengetopt_usage; } struct gengetopt_option * opt; ostringstream usage; // otherwise the config.h package constant will be used if (gengetopt_package) usage << gengetopt_package; if ( long_help ) { // we first generate usage strings of required options // handle mode options separately foropt if (opt->required && !opt->hidden && !opt->mode_value) { generate_option_usage_string(opt, usage); } foropt if (!opt->required && !opt->hidden && !opt->mode_value) { generate_option_usage_string(opt, usage); } } else { /* if not long help we generate it as GNU standards */ usage << " [OPTIONS]..."; } string wrapped; if ( unamed_options ) usage << " [" << unamed_options << "]..."; wrap_cstr ( wrapped, strlen("Usage: "), 2, usage.str() ); // now deal with modes if (has_modes && long_help) { const ModeOptMap &modeOptMap = getModeOptMap(); for (ModeOptMap::const_iterator map_it = modeOptMap.begin(); map_it != modeOptMap.end(); ++map_it) { string mode_line; // a mode alternative in the usage string gengetopt_option_list::const_iterator opt_it; usage.str(""); // reset the usage string buffer for (opt_it = map_it->second.begin(); opt_it != map_it->second.end(); ++opt_it) { if (((*opt_it)->required) && !((*opt_it)->hidden)) { generate_option_usage_string(*opt_it, usage); } } for (opt_it = map_it->second.begin(); opt_it != map_it->second.end(); ++opt_it) { if (!((*opt_it)->required) && !((*opt_it)->hidden)) { generate_option_usage_string(*opt_it, usage); } } wrap_cstr ( mode_line, strlen(" or : "), 2, gengetopt_package + usage.str() ); wrapped += "\\n or : "; wrapped += mode_line; } } return wrapped; } static void generate_help_desc_print(ostream &stream, unsigned int desc_column, const char *descript, const char *defval, const string &values, const string &show_required_string) { string desc; string desc_with_default = descript; if (defval || values.size()) { desc_with_default += " ("; if (values.size()) { desc_with_default += "possible values="; desc_with_default += values; if (defval) desc_with_default += " "; } if (defval) { desc_with_default += "default="; desc_with_default += defval; } desc_with_default += ")"; } if (show_required_string != "") desc_with_default += " " + show_required_string; wrap_cstr ( desc, desc_column, 2, desc_with_default ); stream << desc; } void CmdlineParserCreator::generate_help_option_print_from_lists(ostream &stream, unsigned int indent, OptionHelpList *full_option_list, OptionHelpList *option_list, const std::string &target_array, const std::string &source_array) { print_help_string_gen_class print_gen; // the index into the help arrays int i = 0, full_i = 0; // num of help strings int help_num = 0; print_gen.set_target(target_array); print_gen.set_from(source_array); print_gen.set_shared(true); print_gen.set_last(false); OptionHelpList::const_iterator it = option_list->begin(); OptionHelpList::const_iterator it2 = full_option_list->begin(); // the second list is surely longer so we scan that one for (; it != option_list->end() && it2 != full_option_list->end(); ++it2) { if (*it == *it2) { // when the two strings are the same it means that's a non-hidden // option, so we share it with the full help array ostringstream converted_int; converted_int << i; // the index into the help array print_gen.set_index(converted_int.str()); converted_int.str(""); converted_int << full_i; // the index into the full help array print_gen.set_full_index(converted_int.str()); print_gen.generate_print_help_string(stream, indent); ++help_num; ++i; ++it; } ++full_i; } ostringstream converted_int; converted_int << help_num; // the final 0 print_gen.set_last(true); print_gen.set_index(converted_int.str()); print_gen.generate_print_help_string(stream, indent); // we increment it to store the final 0 converted_int.str(""); converted_int << ++help_num; set_help_string_num(converted_int.str()); } void CmdlineParserCreator::generate_help_option_print(ostream &stream, unsigned int indent) { OptionHelpList *option_list = generate_help_option_list(); if (!c_source_gen_class::has_hidden && !c_source_gen_class::has_details) { print_help_string_gen_class print_gen; print_gen.set_shared(false); // simple help generation for (OptionHelpList::const_iterator it = option_list->begin(); it != option_list->end(); ++it) { print_gen.set_helpstring(*it); print_gen.generate_print_help_string(stream, indent); } } else { // in order to avoid generating the same help string twice, and thus // to save memory, in case of hidden options (or details), we try to share most // of the strings with the full help array OptionHelpList *full_option_list = generate_help_option_list(true, true); generate_help_option_print_from_lists (stream, indent, full_option_list, option_list, c_source_gen_class::args_info + "_help", (c_source_gen_class::has_details ? c_source_gen_class::args_info + "_detailed_help" : c_source_gen_class::args_info + "_full_help")); delete full_option_list; } delete option_list; } void CmdlineParserCreator::generate_full_help_option_print(ostream &stream, unsigned int indent) { // generate also hidden options OptionHelpList *option_list = generate_help_option_list(true); if (!c_source_gen_class::has_details) { print_help_string_gen_class print_gen; print_gen.set_shared(false); for (OptionHelpList::const_iterator it = option_list->begin(); it != option_list->end(); ++it) { print_gen.set_helpstring(*it); print_gen.generate_print_help_string(stream, indent); } } else { // in order to avoid generating the same help string twice, and thus // to save memory, in case of options with details, we try to share most // of the strings with the full help array OptionHelpList *full_option_list = generate_help_option_list(true, true); generate_help_option_print_from_lists (stream, indent, full_option_list, option_list, c_source_gen_class::args_info + "_full_help", c_source_gen_class::args_info + "_detailed_help"); delete full_option_list; } delete option_list; } void CmdlineParserCreator::generate_detailed_help_option_print(ostream &stream, unsigned int indent) { // generate also hidden options and details OptionHelpList *option_list = generate_help_option_list(true, true); print_help_string_gen_class print_gen; print_gen.set_shared(false); for (OptionHelpList::const_iterator it = option_list->begin(); it != option_list->end(); ++it) { print_gen.set_helpstring(*it); print_gen.generate_print_help_string(stream, indent); } delete option_list; } void CmdlineParserCreator::generate_init_args_info(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; init_args_info_gen_class init_args_info_gen; int i = 0; ostringstream index; string help_string = c_source_gen_class::args_info; if (c_source_gen_class::has_details) { help_string += "_detailed_help"; } else if (c_source_gen_class::has_hidden) { help_string += "_full_help"; } else { help_string += "_help"; } init_args_info_gen.set_help_strings(help_string); const char *current_section = 0, *current_group = 0, *current_mode = 0; // we have to skip section description references (that appear in the help vector) foropt { index.str(""); if (opt->section) { if (!current_section || (strcmp(current_section, opt->section) != 0)) { // a different section reference, skip it current_section = opt->section; ++i; if (opt->section_desc) { // section description takes another line, thus we have to skip this too ++i; } } } // skip group desc if (opt->group_value) { if (!current_group || strcmp(current_group, opt->group_value) != 0) { current_group = opt->group_value; ++i; } } // skip mode desc if (opt->mode_value) { if (!current_mode || strcmp(current_mode, opt->mode_value) != 0) { current_mode = opt->mode_value; ++i; } } // also skip the text before if (opt->text_before) ++i; index << i++; init_args_info_gen.set_var_arg(opt->var_arg); init_args_info_gen.set_num(index.str()); if (opt->multiple) { init_args_info_gen.set_multiple(true); init_args_info_gen.set_min(opt->multiple_min); init_args_info_gen.set_max(opt->multiple_max); } else { init_args_info_gen.set_multiple(false); } init_args_info_gen.generate_init_args_info(stream, indent); // skip the details if (opt->details) ++i; // skip the text after if (opt->text_after) ++i; } } void CmdlineParserCreator::generate_custom_getopt(ostream &stream, unsigned int indent) { custom_getopt_gen_gen_class custom_getopt; custom_getopt.generate_custom_getopt_gen (stream, indent); } const string CmdlineParserCreator::generate_purpose() { string wrapped_purpose; if (gengetopt_purpose != NULL) { wrap_cstr(wrapped_purpose, 0, 0, gengetopt_purpose); } return wrapped_purpose; } const string CmdlineParserCreator::generate_description() { string wrapped_description; if (gengetopt_description != NULL) { wrap_cstr(wrapped_description, 0, 0, gengetopt_description); } return wrapped_description; } OptionHelpList * CmdlineParserCreator::generate_help_option_list(bool generate_hidden, bool generate_details) { OptionHelpList *option_list = new OptionHelpList; unsigned long desc_col; struct gengetopt_option * opt; int type_len; const char *type_str; ostringstream stream; // if we want to generate details then we will also generate hidden options if (generate_details) generate_hidden = true; /* calculate columns */ desc_col = 0; foropt { // if (opt->hidden && !generate_hidden) // continue; // when computing columns, we also consider hidden_options, so that // the --help and --full-help will be aligned just the same // IMPORTANT: this is also crucial due to how the help string array // is built starting from the full-help string array: // we iterate over the two lists of options and check whether the // corresponding strings are the same; thus, the help strings must // have the same space alignments, otherwise they're not equal unsigned int width = 2 + 4 + 2; // ws + "-a, " + ws width += strlen (opt->long_opt) + 2; // "--" if ((opt->type != ARG_FLAG) && (opt->type != ARG_NO)) { if (opt->type_str) type_str = opt->type_str; else type_str = arg_names[opt->type]; type_len = strlen(type_str); width += type_len + 1; // "=" if (opt->arg_is_optional) width += 2; // "[" and "]" } if (width > desc_col) desc_col = width; } if (desc_col > MAX_STARTING_COLUMN) desc_col = MAX_STARTING_COLUMN; /* print justified options */ char *prev_group = 0; char *prev_mode = 0; char *curr_section = 0; bool first_option = true; foropt { // if the option is hidden, avoid to print a section containing only // hidden options if (opt->section && (!curr_section || strcmp (curr_section, opt->section)) && (!opt->hidden || generate_hidden)) { curr_section = opt->section; ostringstream sec_string; if (! first_option) sec_string << "\\n"; sec_string << opt->section << ":" ; string wrapped_def; wrap_cstr(wrapped_def, 0, 0, sec_string.str()); option_list->push_back(wrapped_def); if (opt->section_desc) { string wrapped_desc ( 2, ' '); wrap_cstr ( wrapped_desc, 2, 0, opt->section_desc ); option_list->push_back(wrapped_desc); } } if (opt->group_value && (! prev_group || strcmp (opt->group_value, prev_group) != 0)) { string group_string = "\\n Group: "; string wrapped_desc; if (opt->group_desc && strlen (opt->group_desc)) { wrapped_desc = "\\n "; wrap_cstr (wrapped_desc, 2, 0, opt->group_desc); } group_string += opt->group_value + wrapped_desc; option_list->push_back (group_string); prev_group = opt->group_value; } if (opt->mode_value && (! prev_mode || strcmp (opt->mode_value, prev_mode) != 0)) { string mode_string = "\\n Mode: "; string wrapped_desc; if (opt->mode_desc && strlen (opt->mode_desc)) { wrapped_desc = "\\n "; wrap_cstr (wrapped_desc, 2, 0, opt->mode_desc); } mode_string += opt->mode_value + wrapped_desc; option_list->push_back (mode_string); prev_mode = opt->mode_value; } // a possible description to be printed before this option if (opt->text_before) { string wrapped_desc; wrap_cstr ( wrapped_desc, 0, 0, opt->text_before); option_list->push_back(wrapped_desc); } if (!opt->hidden || generate_hidden) { first_option = false; const char * def_val = NULL; string def_str = "`"; ostringstream option_stream; if (opt->type == ARG_FLAG || opt->type == ARG_NO) { def_val = NULL; if (opt->short_opt) option_stream << " -" << opt->short_opt << ", "; else option_stream << " "; option_stream << "--" << opt->long_opt; if (opt->type == ARG_FLAG) def_val = opt->flagstat ? "on" : "off"; } else { def_val = NULL; if (opt->type_str) type_str = opt->type_str; else type_str = arg_names[opt->type]; type_len = strlen(type_str); if (opt->short_opt) { option_stream << " -" << opt->short_opt << ", "; } else { option_stream << " "; } bool arg_optional = opt->arg_is_optional; option_stream << "--" << opt->long_opt << (arg_optional ? "[" : "") << "=" << type_str << (arg_optional ? "]" : ""); if (opt->default_string) { def_str += opt->default_string; def_str += "'"; def_val = def_str.c_str(); } } const string &option_string = option_stream.str(); stream << option_string; const char *opt_desc = opt->desc; if ((option_string.size() >= MAX_STARTING_COLUMN) || (desc_col <= option_string.size())) { string indent (MAX_STARTING_COLUMN, ' '); stream << "\\n" << indent; } else { string indent (desc_col - option_string.size(), ' '); stream << indent; } generate_help_desc_print(stream, desc_col, opt_desc, def_val, (opt->acceptedvalues ? opt->acceptedvalues->toString() : ""), (opt->required && show_required_string != "" ? show_required_string : "")); option_list->push_back(stream.str()); stream.str(""); } // before the text after we generate details if we need to if (opt->details && generate_details) { string wrapped_desc ( 2, ' '); // details are indented wrap_cstr ( wrapped_desc, 2, 0, opt->details); option_list->push_back(wrapped_desc); } // a possible description to be printed after this option if (opt->text_after) { string wrapped_desc; wrap_cstr ( wrapped_desc, 0, 0, opt->text_after); option_list->push_back(wrapped_desc); } } return option_list; } template void generate_counter_init(const Collection &collection, const string &name, ostream &stream, unsigned int indent) { string indent_str (indent, ' '); typename Collection::const_iterator end = collection.end(); for ( typename Collection::const_iterator idx = collection.begin(); idx != end; ++idx) { stream << indent_str; stream << ARGS_STRUCT << "->" << canonize_name (idx->first) << "_" << name << "_counter = 0 ;"; stream << endl; } } void CmdlineParserCreator::generate_given_init(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; string indent_str (indent, ' '); clear_given_gen_class clear_given; clear_given.set_arg_struct(ARGS_STRUCT); /* now we initialize "given" fields */ foropt { stream << indent_str; clear_given.set_var_arg(opt->var_arg); clear_given.set_group(opt->multiple && opt->group_value); clear_given.generate_clear_given(stream); } // for group counter initialization generate_counter_init(gengetopt_groups, "group", stream, indent); // for mode counter initialization generate_counter_init(gengetopt_modes, "mode", stream, indent); } void CmdlineParserCreator::generate_reset_groups(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; string indent_str (indent, ' '); ostringstream body; reset_group_gen_class reset_group; clear_given_gen_class clear_given; clear_given.set_arg_struct(ARGS_STRUCT); reset_group.set_args_info (c_source_gen_class::args_info); groups_collection_t::const_iterator end = gengetopt_groups.end(); for ( groups_collection_t::const_iterator idx = gengetopt_groups.begin(); idx != end; ++idx) { body.str (""); bool found_option = false; bool multiple_arg = false; foropt { if (opt->group_value && strcmp(opt->group_value, idx->first.c_str()) == 0) { /* now we reset "given" fields */ stream << indent_str; clear_given.set_var_arg(opt->var_arg); if (opt->multiple && opt->group_value) multiple_arg = true; clear_given.set_group(opt->multiple && opt->group_value); clear_given.generate_clear_given(body); free_option (opt, body, indent); found_option = true; } } if (found_option) { reset_group.set_name (canonize_name (idx->first)); reset_group.set_body (body.str ()); reset_group.generate_reset_group (stream); } } } void CmdlineParserCreator::free_option(struct gengetopt_option *opt, ostream &stream, unsigned int indent) { if (opt->type == ARG_NO) return; if (opt->type != ARG_FLAG) { if (opt->multiple) { free_multiple_gen_class free_multiple; free_multiple.set_has_string_type(opt->type == ARG_STRING); free_multiple.set_structure (ARGS_STRUCT); free_multiple.set_opt_var (opt->var_arg); free_multiple.generate_free_multiple (stream, indent); } else { free_string_gen_class free_string; free_string.set_has_string_type(opt->type == ARG_STRING); free_string.set_structure (ARGS_STRUCT); free_string.set_opt_var (opt->var_arg); free_string.generate_free_string (stream, indent); } } } void CmdlineParserCreator::generate_list_def(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; string indent_str (indent, ' '); multiple_opt_list_gen_class multiple_opt_list; /* define linked-list structs for multiple options */ foropt { if (opt->multiple) { if (opt->type) { stream << indent_str; multiple_opt_list.set_arg_name (opt->var_arg); multiple_opt_list.generate_multiple_opt_list (stream, indent); stream << endl; } } } } void CmdlineParserCreator::generate_multiple_fill_array(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; string indent_str (indent, ' '); multiple_fill_array_gen_class filler; /* copy linked list into the array */ foropt { if (opt->multiple && opt->type) { stream << indent_str; filler.set_option_var_name (opt->var_arg); filler.set_arg_type(arg_type_constants[opt->type]); filler.set_type (arg_types_names[opt->type]); string default_string = "0"; if (opt->default_string) { if (opt->type == ARG_STRING) default_string = string("\"") + opt->default_string + "\""; else if (opt->type == ARG_ENUM) default_string = from_value_to_enum(opt->var_arg, opt->default_string); else default_string = opt->default_string; } filler.set_default_value (default_string); filler.generate_multiple_fill_array (stream, indent); stream << endl; } } } void CmdlineParserCreator::generate_update_multiple_given(ostream &stream, unsigned int indent) { if (! has_multiple_options()) return; string indent_str (indent, ' '); stream << endl; stream << indent_str; update_given_gen_class update_given_gen; struct gengetopt_option * opt; foropt { if (opt->multiple) { update_given_gen.set_option_var_name (opt->var_arg); update_given_gen.generate_update_given (stream, indent); } } } void CmdlineParserCreator::generate_check_modes(ostream &stream, unsigned int indent) { // no need to check for conflict if there's only one mode if (gengetopt_modes.size() < 2) return; string indent_str (indent, ' '); stream << endl; stream << indent_str; const ModeOptionMap &modeOptionMap = getModeOptionMap(); check_modes_gen_class check_modes_gen; // now we check each mode options against every other mode options: // the first one with the other n-1, the second one with the other n-2, etc. ModeOptionMap::const_iterator map_it1, map_it2; for (ModeOptionMap::const_iterator map_it = modeOptionMap.begin(); map_it != modeOptionMap.end(); ++map_it) { map_it1 = map_it; ++map_it; if (map_it == modeOptionMap.end()) break; for (map_it2 = map_it; map_it2 != modeOptionMap.end(); ++map_it2) { const string mode1 = canonize_name(map_it1->first); const string mode2 = canonize_name(map_it2->first); check_modes_gen.set_mode1_name(mode1); check_modes_gen.set_mode2_name(mode2); ostringstream mode1_given, mode2_given, mode1_options, mode2_options; std::for_each(map_it1->second.begin(), map_it1->second.end(), pair_print_f(mode1_given, mode1_options)); std::for_each(map_it2->second.begin(), map_it2->second.end(), pair_print_f(mode2_given, mode2_options)); check_modes_gen.set_mode1_given_fields(mode1_given.str()); check_modes_gen.set_mode1_options(mode1_options.str()); check_modes_gen.set_mode2_given_fields(mode2_given.str()); check_modes_gen.set_mode2_options(mode2_options.str()); check_modes_gen.generate_check_modes(stream, indent); } map_it = map_it1; } } void CmdlineParserCreator::generate_clear_arg(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; clear_arg_gen_class clear_arg; /* now we initialize value fields */ foropt { if (opt->type == ARG_NO) continue; clear_arg.set_name(opt->var_arg); clear_arg.set_suffix("arg"); clear_arg.set_value("NULL"); clear_arg.set_has_orig(opt->type != ARG_FLAG); clear_arg.set_has_arg(false); if (opt->multiple && opt->type) { clear_arg.set_has_arg(true); } else if (opt->type == ARG_STRING) { clear_arg.set_has_arg(true); if (opt->default_given) clear_arg.set_value ("gengetopt_strdup (\"" + string(opt->default_string) + "\")"); } else if (opt->type == ARG_FLAG) { clear_arg.set_has_arg(true); clear_arg.set_suffix("flag"); clear_arg.set_value(opt->flagstat ? "1" : "0"); } else if (opt->type == ARG_ENUM) { // initialize enum arguments to -1 (unless they have a default) clear_arg.set_has_arg(true); if (opt->default_given) clear_arg.set_value(from_value_to_enum(opt->var_arg, opt->default_string)); else clear_arg.set_value(string(opt->var_arg) + "__NULL"); } else if (opt->default_given) { clear_arg.set_has_arg(true); clear_arg.set_value(opt->default_string); } clear_arg.generate_clear_arg(stream, indent); } } void CmdlineParserCreator::generate_long_option_struct(ostream &stream, unsigned int indent) { string indent_str (indent, ' '); struct gengetopt_option * opt; foropt { stream << indent_str; stream << "{ \"" << opt->long_opt << "\",\t" << (opt->type == ARG_NO || opt->type == ARG_FLAG ? 0 : (opt->arg_is_optional ? 2 : 1)) << ", NULL, "; if (opt->short_opt) stream << "\'" << opt->short_opt << "\'"; else stream << "0"; stream << " }," << endl; } } string CmdlineParserCreator::generate_getopt_string() { struct gengetopt_option * opt; ostringstream built_getopt_string; foropt if (opt->short_opt) { built_getopt_string << opt->short_opt << (opt->type == ARG_NO || opt->type == ARG_FLAG ? "" : ":"); built_getopt_string << (opt->arg_is_optional ? ":" : ""); } return built_getopt_string.str (); } void CmdlineParserCreator::generate_handle_no_short_option(ostream &stream, unsigned int indent) { handle_options(stream, indent, false); } void CmdlineParserCreator::generate_handle_option(ostream &stream, unsigned int indent) { handle_options(stream, indent, true); } void CmdlineParserCreator::handle_options(ostream &stream, unsigned int indent, bool has_short) { struct gengetopt_option * opt; generic_option_gen_class option_gen; string indent_str (indent, ' '); bool first = true; option_gen.set_has_short_option (has_short); // by default we handle '?' case in the switch // unless the user defined a short option as ? set_handle_question_mark(true); foropt { if (opt->short_opt == '?') set_handle_question_mark(false); if ((has_short && opt->short_opt) || (!has_short && !opt->short_opt)) { if (has_short || first) stream << indent_str; option_gen.set_option_comment (opt->desc); option_gen.set_long_option (opt->long_opt); option_gen.set_short_option(opt->short_opt ? string (1, opt->short_opt) : "-"); option_gen.set_option_var_name (opt->var_arg); option_gen.set_final_instructions(""); if (!no_help && ((opt->short_opt == HELP_SHORT_OPT && strcmp(opt->long_opt, HELP_LONG_OPT) == 0) || strcmp(opt->long_opt, HELP_LONG_OPT) == 0 || strcmp(opt->long_opt, FULL_HELP_LONG_OPT) == 0 || strcmp(opt->long_opt, DETAILED_HELP_LONG_OPT) == 0)) { bool full_help = (strcmp(opt->long_opt, FULL_HELP_LONG_OPT) == 0); bool detailed_help = (strcmp(opt->long_opt, DETAILED_HELP_LONG_OPT) == 0); if (no_handle_help) { // we use the final_instructions parameter to call the free function // and to return 0 const string final_instructions = parser_function_name + string("_free (&local_args_info);\nreturn 0;"); option_gen.set_final_instructions(final_instructions); if (full_help) { option_gen.set_long_option (FULL_HELP_LONG_OPT); option_gen.set_option_comment (FULL_HELP_OPT_DESCR); } else if (detailed_help) { option_gen.set_long_option (DETAILED_HELP_LONG_OPT); option_gen.set_option_comment (DETAILED_HELP_OPT_DESCR); } else { option_gen.set_long_option (HELP_LONG_OPT); option_gen.set_short_option (HELP_SHORT_OPT_STR); option_gen.set_option_comment (HELP_OPT_DESCR); } //option_gen.set_has_short_option (!full_help); } else { handle_help_gen_class help_gen; help_gen.set_parser_name (parser_function_name); help_gen.set_full_help(full_help); help_gen.set_detailed_help(detailed_help); help_gen.set_short_opt(opt->short_opt == HELP_SHORT_OPT); help_gen.generate_handle_help (stream, indent); stream << endl; stream << endl; continue; } } if (!no_version && ((opt->short_opt == VERSION_SHORT_OPT && strcmp(opt->long_opt, VERSION_LONG_OPT) == 0) || strcmp(opt->long_opt, VERSION_LONG_OPT) == 0)) { if (no_handle_version) { option_gen.set_long_option (VERSION_LONG_OPT); option_gen.set_short_option (VERSION_SHORT_OPT_STR); option_gen.set_option_comment (VERSION_OPT_DESCR); //option_gen.set_has_short_option (true); // we use the final_instrauctions parameter to call the free function // and to return 0 const string final_instructions = parser_function_name + string("_free (&local_args_info);\nreturn 0;"); option_gen.set_final_instructions(final_instructions); } else { handle_version_gen_class version_gen; version_gen.set_parser_name (parser_function_name); version_gen.set_short_opt (opt->short_opt == VERSION_SHORT_OPT); version_gen.generate_handle_version (stream, indent); stream << endl; stream << endl; continue; } } if (opt->acceptedvalues != 0) option_gen.set_possible_values (OPTION_VALUES_NAME(opt->var_arg)); else option_gen.set_possible_values ("0"); string default_string = "0"; if (opt->default_string) default_string = string("\"") + opt->default_string + "\""; option_gen.set_default_value (default_string); option_gen.set_arg_type(arg_type_constants[opt->type]); if (opt->group_value) { option_gen.set_group_var_name (canonize_name (opt->group_value)); option_gen.set_option_has_group(true); } else option_gen.set_option_has_group(false); if (opt->mode_value) { // we reuse the variable group_var_name also for modes option_gen.set_group_var_name (canonize_name (opt->mode_value)); option_gen.set_option_has_mode(true); } else option_gen.set_option_has_mode(false); option_gen.set_option_has_type(opt->type != 0); if (opt->multiple) { option_gen.set_multiple(true); option_gen.set_structure (string (opt->var_arg) + "_list"); } else { option_gen.set_multiple(false); option_gen.set_structure (ARGS_STRUCT); } option_gen.generate_generic_option (stream, indent); if (has_short) { stream << endl; } if (first && !has_short) { first = false; option_gen.set_gen_else ("else "); } } } if (! first && !has_short) // something has been generated { generateBreak(stream, indent); stream << endl; } } #define GROUP_REQUIRED_COMPARISON "!=" #define GROUP_NOT_REQUIRED_COMPARISON ">" #define GROUP_REQUIRED_MESSAGE "One" #define GROUP_NOT_REQUIRED_MESSAGE "At most one" void CmdlineParserCreator::generate_handle_group(ostream &stream, unsigned int indent) { group_option_gen_class opt_gen; string indent_str (indent, ' '); opt_gen.set_package_var_name (EXE_NAME); opt_gen.set_Comparison_rule(GROUP_NOT_REQUIRED_COMPARISON " 1"); groups_collection_t::const_iterator end = gengetopt_groups.end(); for ( groups_collection_t::const_iterator idx = gengetopt_groups.begin(); idx != end; ++idx) { stream << indent_str; opt_gen.set_group_name (idx->first); opt_gen.set_group_var_name (canonize_name (idx->first)); if (idx->second.required) { opt_gen.set_number_required(GROUP_REQUIRED_MESSAGE); } else { opt_gen.set_number_required(GROUP_NOT_REQUIRED_MESSAGE); } opt_gen.generate_group_option (stream, indent); stream << endl; } } void CmdlineParserCreator::generate_handle_required(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; required_option_gen_class opt_gen; opt_gen.set_package_var_name ("prog_name"); /* write test for required options or for multiple options (occurrence number check) */ foropt if ( opt->required || opt->multiple ) { if (opt->mode_value) { opt_gen.set_mode_condition("args_info->" + canonize_name(opt->mode_value) + "_mode_counter && "); } else { opt_gen.set_mode_condition(""); } // build the option command line representation ostringstream req_opt; req_opt << "'--" << opt->long_opt << "'"; if (opt->short_opt) req_opt << " ('-" << opt->short_opt << "')"; opt_gen.set_option_var_name (opt->var_arg); opt_gen.set_option_descr (req_opt.str ()); // if the option is required this is the standard check if (opt->required) { opt_gen.set_checkrange(false); opt_gen.generate_required_option (stream, indent); } // if the option is multiple we generate also the // occurrence range check if (opt->multiple) { opt_gen.set_checkrange(true); opt_gen.generate_required_option (stream, indent); } // notice that the above ifs are not mutual exclusive: // a multiple option can have a range check without being // required. } // now generate the checks for required group options group_option_gen_class group_opt_gen; group_opt_gen.set_package_var_name ("prog_name"); group_opt_gen.set_Comparison_rule("== 0"); group_opt_gen.set_number_required(GROUP_REQUIRED_MESSAGE); groups_collection_t::const_iterator end = gengetopt_groups.end(); for ( groups_collection_t::const_iterator idx = gengetopt_groups.begin(); idx != end; ++idx) { if (idx->second.required) { group_opt_gen.set_group_name (idx->first); group_opt_gen.set_group_var_name (canonize_name (idx->first)); group_opt_gen.generate_group_option (stream, indent); stream << endl; } } } void CmdlineParserCreator::generate_handle_dependencies(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; dependant_option_gen_class opt_gen; opt_gen.set_package_var_name ("prog_name"); string indent_str (indent, ' '); /* write test for required options */ foropt if ( opt->dependon ) { stream << indent_str; ostringstream req_opt; req_opt << "'--" << opt->long_opt << "'"; if (opt->short_opt) req_opt << " ('-" << opt->short_opt << "')"; opt_gen.set_option_var_name (opt->var_arg); opt_gen.set_dep_option (canonize_name(opt->dependon)); opt_gen.set_option_descr (req_opt.str ()); opt_gen.set_dep_option_descr (opt->dependon); opt_gen.generate_dependant_option (stream, indent); stream << endl; } } template void generate_counters(const Collection &collection, const string &name, ostream &stream, unsigned int indent) { group_counter_gen_class counter_gen; string indent_str (indent, ' '); counter_gen.set_name(name); typename Collection::const_iterator end = collection.end(); for ( typename Collection::const_iterator idx = collection.begin(); idx != end; ++idx) { stream << indent_str; counter_gen.set_group_name (canonize_name (idx->first)); counter_gen.generate_group_counter (stream, indent); stream << endl; } } void CmdlineParserCreator::generate_group_counters(ostream &stream, unsigned int indent) { generate_counters(gengetopt_groups, "group", stream, indent); } void CmdlineParserCreator::generate_mode_counters(ostream &stream, unsigned int indent) { // we can reuse group counter gen class also for modes generate_counters(gengetopt_modes, "mode", stream, indent); } int CmdlineParserCreator::generate_source () { /* ****************************************************** */ /* ********************************************** C FILE */ /* ****************************************************** */ set_usage_string (generate_usage_string ()); set_getopt_string (generate_getopt_string ()); string output_source = c_filename; if (src_output_dir.size()) output_source = src_output_dir + "/" + output_source; else if (output_dir.size()) output_source = output_dir + "/" + output_source; ofstream *output_file = open_fstream (output_source.c_str()); generate_c_source (*output_file); output_file->close (); delete output_file; return 0; } void CmdlineParserCreator::generate_free(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; foropt { free_option (opt, stream, indent); } } void CmdlineParserCreator::generate_list_free(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; if (! has_multiple_options()) return; free_list_gen_class free_list; foropt { if (opt->multiple && opt->type) { free_list.set_list_name(opt->var_arg); free_list.set_string_list(opt->type == ARG_STRING); free_list.generate_free_list(stream, indent); } } } void CmdlineParserCreator::generate_file_save_loop(ostream &stream, unsigned int indent) { struct gengetopt_option * opt; file_save_multiple_gen_class file_save_multiple; file_save_gen_class file_save; const string suffix = "_orig"; const string suffix_given = "_given"; foropt { if (opt->multiple) { file_save_multiple.set_has_arg(opt->type != ARG_NO); file_save_multiple.set_opt_var(opt->var_arg); file_save_multiple.set_opt_name(opt->long_opt); file_save_multiple.set_values ((opt->acceptedvalues ? OPTION_VALUES_NAME(opt->var_arg) : "0")); file_save_multiple.generate_file_save_multiple(stream, indent); } else { file_save.set_opt_name(opt->long_opt); file_save.set_given(opt->var_arg + suffix_given); file_save.set_values ((opt->acceptedvalues ? OPTION_VALUES_NAME(opt->var_arg) : "0")); if (opt->type != ARG_NO && opt->type != ARG_FLAG) { file_save.set_arg(opt->var_arg + suffix + (opt->multiple ? " [i]" : "")); } else { file_save.set_arg(""); } file_save.generate_file_save(stream, indent); } } }