+static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) {
+ int chr = 0;
+ struct opt_item * item;
+ while (argv[index][++chr] && (item = opt_find_item_shortopt(argv[index][chr], pre))) {
+ if (item->flags & OPT_NO_VALUE) {
+ opt_parse_value(item, NULL, 0);
+ continue;
+ }
+ if (chr == 1 && (item->flags & OPT_REQUIRED_VALUE)) {
+ if (argv[index][2]) {
+ opt_parse_value(item, argv[index] + 2, 0);
+ return 0;
+ }
+ else {
+ opt_parse_value(item, argv[index+1], 0);
+ return 1;
+ }
+ }
+ else if (chr == 1 && (item->flags & OPT_MAYBE_VALUE)) {
+ if (argv[index][2])
+ opt_parse_value(item, argv[index] + 2, 0);
+ else
+ opt_parse_value(item, NULL, 0);
+ }
+ else if (item->flags & (OPT_REQUIRED_VALUE | OPT_MAYBE_VALUE)) {
+ if (argv[index][chr+1] || (item->flags | OPT_MAYBE_VALUE))
+ opt_failure("Option -%c may or must have a value but found inside a bunch of short opts.", item->letter);
+ else {
+ opt_parse_value(item, argv[index+1], 0);
+ return 1;
+ }
+ }
+ }
+
+ if (argv[index][chr])
+ opt_failure("Unknown option -%c.", item->letter);
+
+ return 0;
+}
+
+#define OPT_TRAVERSE_SECTIONS \
+ do { \
+ while (item->cls == OPT_CL_SECTION) { \
+ if (stk->next) \
+ stk = stk->next; \
+ else { \
+ struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
+ new_stk->prev = stk; \
+ stk->next = new_stk; \
+ stk = new_stk; \
+ } \
+ stk->this = item; \
+ item = item->u.section->opt; \
+ } \
+ if (item->cls == OPT_CL_END) { \
+ if (!stk) break; \
+ item = stk->this; \
+ stk = stk->prev; \
+ continue; \
+ } \
+ } while (0)
+
+void opt_parse(const struct opt_section * options, char ** argv, opt_positional * callback) {
+ struct opt_stack {
+ struct opt_item * this;
+ struct opt_stack * prev;
+ struct opt_stack * next;
+ } * stk = alloca(sizeof(*stk));
+ stk->this = NULL;
+ stk->prev = NULL;
+ stk->next = NULL;
+
+ struct opt_precomputed * pre = alloca(sizeof(*pre));
+
+ int count;
+
+ for (struct opt_item * item = options->opt; ; item++) {
+ OPT_TRAVERSE_SECTIONS;
+ if (item->letter || item->name)
+ count++;
+ }
+
+ pre->opts = xmalloc(sizeof(*pre->opts) * count);
+ pre->opt_count = 0;
+
+ for (struct opt_item * item = options->opt; ; item++) {
+ OPT_TRAVERSE_SECTIONS;
+ if (item->letter || item->name) {
+ struct opt_precomputed_option * opt = xmalloc(sizeof(*opt));
+ opt->item = item;
+ opt->flags = item->flags;
+ opt->count = 0;
+ pre->opts[pre->opt_count++] = opt;
+ if (item->letter)
+ pre->shortopt[(int) item->letter] = opt;
+ if (!(opt->flags & OPT_VALUE_FLAGS) &&
+ (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER)) {
+ fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name);
+ ASSERT(0);
+ }
+ else
+ opt->flags |= opt_default_value_flags[item->cls];
+ }
+ }