/** * Check for a match to the current option. * * This function does some initial parsing, then calls the appropriate * sub-function to look for matches. * * @param opt The argument string. * @return TRUE if a match is found, FALSE otherwise. * * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse(), find_long_option(), * find_short_option() * @ingroup DOXGRP_OPT */ static spif_bool_t is_valid_option(spif_charptr_t opt) { REQUIRE_RVAL(opt != NULL, FALSE); if (*opt != '-') { return FALSE; } opt++; if (*opt == '-') { opt++; if (find_long_option(opt) >= 0) { return TRUE; } } else { if (find_short_option(*opt) >= 0) { return TRUE; } } return FALSE; }
/** * Parse the command line arguments for options. * * This function iterates through the command line arguments looking * for options which have been defined. Each option encountered is * handled according to its type. * * @param argc The number of arguments. * @param argv The array of argument strings. * * @see @link DOXGRP_OPT Command Line Option Parser @endlink * @ingroup DOXGRP_OPT */ void spifopt_parse(int argc, char *argv[]) { spif_int32_t i, j; spif_charptr_t opt; REQUIRE(argc > 1); REQUIRE(argv != NULL); /* Process each command line arg one-by-one. */ for (i = 1, opt = SPIF_CHARPTR(argv[1]); i < argc; ) { spif_charptr_t val_ptr = NULL; spif_char_t islong = 0, hasequal = 0; D_OPTIONS(("argv[%d] == \"%s\", opt == \"%s\"\n", i, argv[i], opt)); if (SPIF_PTR_ISNULL(opt)) { /* NEXT_ARG(); */ break; } else if (opt == SPIF_CHARPTR(argv[i])) { /* If it's not an option, skip it. */ if (*opt != '-') { NEXT_ARG(); } else { opt++; } } /* If the second character is also a hyphen, it's a long option. */ if (*opt == '-') { islong = 1; /* Skip the leading "--" */ opt++; D_OPTIONS(("Long option detected\n")); if ((j = find_long_option(opt)) == -1) { NEXT_ARG(); } } else { if ((j = find_short_option(*opt)) == -1) { NEXT_LETTER(); } } if (!SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE) && SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_REMOVE_ARGS)) { argv[i] = NULL; } /* If a value was passed to this option, set val_ptr to point to it. */ if (islong) { val_ptr = find_value_long(SPIF_CHARPTR(opt), SPIF_CHARPTR(argv[i + 1]), &hasequal); } else { val_ptr = find_value_short(opt, SPIF_CHARPTR(argv[i + 1])); } /* Boolean options may or may not have a value... */ if (val_ptr) { if (SPIFOPT_OPT_IS_BOOLEAN(j) && !is_boolean_value(val_ptr)) { val_ptr = NULL; } else if (SPIFOPT_OPT_IS_ABSTRACT(j) && is_valid_option(val_ptr)) { val_ptr = NULL; } } if (val_ptr) { if (val_ptr == SPIF_CHARPTR(argv[i + 1])) { i++; opt += strlen((char *) opt); } } /* If this option is deprecated, print a warning before continuing. */ if (SPIFOPT_OPT_IS_DEPRECATED(j)) { spif_str_t warn; warn = spif_str_new_from_buff(SPIF_CHARPTR("The "), 128); if (SPIFOPT_OPT_SHORT(j)) { spif_str_append_char(warn, '-'); spif_str_append_char(warn, SPIFOPT_OPT_SHORT(j)); spif_str_append_from_ptr(warn, SPIF_CHARPTR(" / --")); } else { spif_str_append_from_ptr(warn, SPIF_CHARPTR("--")); } spif_str_append_from_ptr(warn, SPIFOPT_OPT_LONG(j)); spif_str_append_from_ptr(warn, SPIF_CHARPTR(" option is deprecated and should not be used.\n")); libast_print_warning((char *) SPIF_STR_STR(warn)); spif_str_del(warn); } /* Make sure that options which require a parameter have them. */ if (SPIFOPT_OPT_NEEDS_VALUE(j)) { if (!val_ptr) { if (islong) { libast_print_error("long option --%s requires a%s value\n", SPIFOPT_OPT_LONG(j), (SPIFOPT_OPT_IS_INTEGER(j) ? ("n integer") : (SPIFOPT_OPT_IS_STRING(j) ? " string" : (SPIFOPT_OPT_IS_ARGLIST(j) ? "n argument list" : "")))); } else { libast_print_error("option -%c requires a%s value\n", SPIFOPT_OPT_SHORT(j), (SPIFOPT_OPT_IS_INTEGER(j) ? ("n integer") : (SPIFOPT_OPT_IS_STRING(j) ? " string" : (SPIFOPT_OPT_IS_ARGLIST(j) ? "n argument list" : "")))); } CHECK_BAD(); continue; } /* Also make sure we know what to do with the value. */ if (!SPIFOPT_OPT_VALUE(j)) { NEXT_LOOP(); } } else if (SPIFOPT_OPT_IS_ABSTRACT(j) && !SPIFOPT_OPT_VALUE(j)) { /* Also make sure that abstract options have a function pointer. */ NEXT_LOOP(); } if (SPIFOPT_OPT_IS_BOOLEAN(j)) { if (!handle_boolean(j, val_ptr, islong)) { i--; } } else if (SPIFOPT_OPT_IS_STRING(j)) { if (SHOULD_PARSE(j)) { handle_string(j, val_ptr); } } else if (SPIFOPT_OPT_IS_INTEGER(j)) { if (SHOULD_PARSE(j)) { handle_integer(j, val_ptr); } } else if (SPIFOPT_OPT_IS_ARGLIST(j)) { if (SHOULD_PARSE(j)) { handle_arglist(j, val_ptr, hasequal, i, argc, argv); } if (!hasequal) { break; } } else if (SPIFOPT_OPT_IS_ABSTRACT(j)) { if (SHOULD_PARSE(j)) { D_OPTIONS(("Abstract option detected\n")); ((spifopt_abstract_handler_t) SPIFOPT_OPT_VALUE(j))(val_ptr); } } if (!SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE) && SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_REMOVE_ARGS)) { argv[i] = NULL; } NEXT_LOOP(); } if (SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE)) { SPIFOPT_FLAGS_CLEAR(SPIFOPT_SETTING_PREPARSE); } else if (SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_REMOVE_ARGS)) { for (i = 1, j = 1; i < argc; i++) { if (argv[i]) { argv[j] = argv[i]; j++; } } if (j > 1) { argv[j] = NULL; } } }
/* Parse the next argument in PARSER (as indicated by PARSER->state.next). Any error from the parsers is returned, and *ARGP_EBADKEY indicates whether a value of EBADKEY is due to an unrecognized argument (which is generally not fatal). */ static error_t parser_parse_next (struct parser *parser, int *arg_ebadkey) { if (parser->state.quoted && parser->state.next < parser->state.quoted) /* The next argument pointer has been moved to before the quoted region, so pretend we never saw the quoting `--', and start looking for options again. If the `--' is still there we'll just process it one more time. */ parser->state.quoted = parser->args_only = 0; /* Give FIRST_NONOPT & LAST_NONOPT rational values if NEXT has been moved back by the user (who may also have changed the arguments). */ if (parser->last_nonopt > parser->state.next) parser->last_nonopt = parser->state.next; if (parser->first_nonopt > parser->state.next) parser->first_nonopt = parser->state.next; if (parser->nextchar) /* Deal with short options. */ { struct group *group; char c; const struct argp_option *option; char *value = NULL;; assert(!parser->args_only); c = *parser->nextchar++; option = find_short_option(parser, c, &group); if (!option) { if (parser->posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: illegal option -- %c\n"), parser->state.name, c); else fprintf (parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: invalid option -- %c\n"), parser->state.name, c); *arg_ebadkey = 0; return EBADKEY; } if (!*parser->nextchar) parser->nextchar = NULL; if (option->arg) { value = parser->nextchar; parser->nextchar = NULL; if (!value && !(option->flags & OPTION_ARG_OPTIONAL)) /* We need an mandatory argument. */ { if (parser->state.next == parser->state.argc) /* Missing argument */ { /* 1003.2 specifies the format of this message. */ fprintf (parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: option requires an argument -- %c\n"), parser->state.name, c); *arg_ebadkey = 0; return EBADKEY; } value = parser->state.argv[parser->state.next++]; } } return group_parse(group, &parser->state, option->key, value); } else /* Advance to the next ARGV-element. */ { if (parser->args_only) { *arg_ebadkey = 1; if (parser->state.next >= parser->state.argc) /* We are done: */ return EBADKEY; else return parser_parse_arg(parser, parser->state.argv[parser->state.next]); } if (parser->state.next >= parser->state.argc) /* Almost done. If there are non-options that we skipped previously, we should process them now. */ { *arg_ebadkey = 1; if (parser->first_nonopt != parser->last_nonopt) { exchange(parser); /* Start processing the arguments we skipped previously: */ parser->state.next = parser->first_nonopt; parser->first_nonopt = parser->last_nonopt = 0; parser->args_only = 1; return 0; } else /* Indicate that we are really done. */ return EBADKEY; } else /* Look for options. */ { char *arg = parser->state.argv[parser->state.next]; char *optstart; enum arg_type token = classify_arg(parser, arg, &optstart); switch (token) { case ARG_ARG: switch (parser->ordering) { case PERMUTE: if (parser->first_nonopt == parser->last_nonopt) { /* Skipped sequence is empty; start a new one: */ parser->first_nonopt = parser->last_nonopt = parser->state.next; } else if (parser->last_nonopt != parser->state.next) { /* We have a non-empty skipped sequence, and we are not * at the end-point, so move it. */ exchange(parser); } assert(parser->last_nonopt == parser->state.next); /* Skip this argument for now: */ parser->state.next++; parser->last_nonopt = parser->state.next; return 0; case REQUIRE_ORDER: /* Implicit quote before the first argument. */ parser->args_only = 1; return 0; case RETURN_IN_ORDER: *arg_ebadkey = 1; return parser_parse_arg(parser, arg); default: abort(); } case ARG_QUOTE: /* Skip it, then exchange with any previous non-options: */ parser->state.next++; assert(parser->last_nonopt != parser->state.next); if (parser->first_nonopt != parser->last_nonopt) { exchange(parser); /* Start processing the skipped and the quoted arguments. */ parser->state.quoted = parser->state.next = parser->first_nonopt; /* Also empty the skipped-list, to avoid confusion if the user resets the next pointer. */ parser->first_nonopt = parser->last_nonopt = 0; } else parser->state.quoted = parser->state.next; parser->args_only = 1; return 0; case ARG_LONG_ONLY_OPTION: case ARG_LONG_OPTION: { struct group *group; const struct argp_option *option; char *value; parser->state.next++; option = find_long_option(parser, optstart, &group); if (!option) { /* NOTE: This includes any "=something" in the output: */ fprintf(parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: unrecognized option `%s'\n"), parser->state.name, arg); *arg_ebadkey = 0; return EBADKEY; } value = strchr(optstart, '='); if (value) value++; if (value && !option->arg) /* Unexpected argument. */ { if (token == ARG_LONG_OPTION) /* --option */ fprintf(parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: option `--%s' does NOT allow an argument\n"), parser->state.name, option->name); else /* +option or -option */ fprintf(parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: option `%c%s' does NOT allow an argument\n"), parser->state.name, arg[0], option->name); *arg_ebadkey = 0; return EBADKEY; } if (option->arg && !value && !(option->flags & OPTION_ARG_OPTIONAL)) /* We need an mandatory argument. */ { if (parser->state.next == parser->state.argc) /* Missing argument */ { if (token == ARG_LONG_OPTION) /* --option */ fprintf (parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: option `--%s' requires an argument\n"), parser->state.name, option->name); else /* +option or -option */ fprintf (parser->state.err_stream, dgettext(parser->state.root_argp->argp_domain, "%s: option `%c%s' requires an argument\n"), parser->state.name, arg[0], option->name); *arg_ebadkey = 0; return EBADKEY; } value = parser->state.argv[parser->state.next++]; } *arg_ebadkey = 0; return group_parse(group, &parser->state, option->key, value); } case ARG_SHORT_OPTION: parser->state.next++; parser->nextchar = optstart; return 0; default: abort(); } } } }
/* * args_ok * * prepare args, check & parse user args, display error and * help message if neccessary * * result: TRUE if all args ok */ BOOL args_ok(HSCPRC * hp, int argc, char *argv[]) { static const struct hscoption opts[] = { {"from", "Input file(s)", "FILE", NULL,ARG_OBL, OPT_TXT, 'f'}, {"to", "Output file", "FILE", NULL,ARG_OBL, OPT_TXT, 'o'}, {"prj", "Project file", "FILE", NULL,ARG_OBL, OPT_TXT, 'p'}, {"syntax", "Syntax definition file (default: hsc.prefs)", "FILE", NULL,ARG_OBL, OPT_TXT, 's'}, {"msgfile", "Message file for output (default: stderr)", "FILE", NULL,ARG_OBL, OPT_TXT, 0}, {"msgfmt", "printf-style format for messages", "FORMAT",NULL,ARG_OBL, OPT_TXT, 0}, {"browser", "Message browser program to use", "PROG", NULL,ARG_OBL, OPT_TXT, 0}, {"maxerr", "Maximum numer of errors (default: " DEFAULT_MAXERR ")", "NUM", NULL,ARG_OBL, OPT_DEC, 0}, {"maxmsg", "Maximum numer of messages (default: " DEFAULT_MAXMSG ")", "NUM", NULL,ARG_OBL, OPT_DEC, 0}, {"ext", "Output file extension (default: " DEFAULT_EXTENSION ")", "WORD", NULL,ARG_OBL, OPT_TXT, 0}, {"define", "Define global attribute", "WORD", NULL,ARG_OBL, OPT_TXT, 'D'}, {"ignore", "Ignore message number or class", "WORD", NULL,ARG_OBL, OPT_TXT, 'i'}, {"enable", "Enable message number or class", "WORD", NULL,ARG_OBL, OPT_TXT, 'e'}, {"msgmode", "Syntax checking mode (" MODE_ENUMSTR ")", "WORD", NULL,ARG_OBL, OPT_TXT, 'm'}, {"qstyle", "Quote style (" QMODE_ENUMSTR ")", "WORD", NULL,ARG_OBL, OPT_TXT, 0}, {"estyle", "Entity style (" EMODE_ENUMSTR ")", "WORD", NULL,ARG_OBL, OPT_TXT, 0}, {"incdir", "Add include directory", "DIR", NULL,ARG_OBL, OPT_TXT, 'I'}, {"compact", "Strip superfluous whitespace", NULL, NULL,ARG_NONE,OPT_NONE,'c'}, {"getsize", "Set width and height attributes for images", NULL, NULL,ARG_NONE,OPT_NONE,'g'}, {"rplcent", "Replace non-ASCII characters with entities (cf. --estyle)", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"rplcquote", "Replace double quotes with `"'", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"stripbadws", "Strip bad whitespace", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"stripcomment","Strip SGML comments", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"stripext", "Strip tags with external URIs", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"striptags", "Tags to be stripped", "LIST", NULL,ARG_OBL, ARG_TEXT, 0}, {"iconbase", "Base URI for icon entities", "URI", NULL,ARG_OBL, ARG_TEXT, 0}, {"serverdir", "Base directory for server relative URIs", "DIR", NULL,ARG_OBL, ARG_TEXT, 0}, {"status", "Status message verbosity (" STATUS_ENUM_STR ")", "LIST", NULL,ARG_OBL, ARG_TEXT, 0}, {"quiet", "Be quiet. Equivalent to --status=QUIET", NULL, NULL,ARG_NONE,OPT_NONE,'q'}, {"nonesterr", "Don't show \"previous call\" tracebacks on error", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"lowercase", "Force all tags and attributes to lowercase", NULL, NULL,ARG_NONE,OPT_NONE,'l'}, {"xhtml", "Use XHTML mode (implies -l --qstyle=DOUBLE)", NULL, NULL,ARG_NONE,OPT_NONE,'x'}, {"nocss", "Don't validate CSS in STYLE attributes", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"ckxuri", "Check external links", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"debug", "Enable debugging output if enabled at compile time", NULL, NULL,ARG_NONE,OPT_NONE,'d'}, {"license", "Display the license", NULL, NULL,ARG_NONE,OPT_NONE,0}, {"help", "Show this help", NULL, NULL,ARG_NONE,OPT_NONE,'h'}, }; int c; int nopts=sizeof(opts)/sizeof(opts[0]); struct option *lop = hscopts_to_getopt_long(opts,nopts); char *sop = hscopts_to_getopt_short(opts,nopts); EXPSTR *destdir = init_estr(32); /* destination dir */ EXPSTR *rel_destdir = init_estr(32); /* relative destination dir */ EXPSTR *kack_name = init_estr(0); /* temp. str for outfilename */ LONG maximum_number_of_errors = strtol(DEFAULT_MAXERR, (char **) NULL, 10); LONG maximum_number_of_messages = strtol(DEFAULT_MAXMSG, (char **) NULL, 10); arg_hp = hp; arg_mode_CB(DEFAULT_MODE_STR); while(1) { int opt_index; c = getopt_long(argc, argv, sop, lop, &opt_index); if(-1 == c) break; if(c) { if(!verify_option(hp, find_short_option(hp,opts,nopts,c), optarg)) return FALSE; process_short_option(hp, c, optarg); } else { if(!verify_option(hp, find_long_option(hp,opts,nopts,lop[opt_index].name), optarg)) return FALSE; process_long_option(hp, lop[opt_index].name, optarg); break; } } ufree(lop); ufree(sop); return TRUE; }