2
* OpenTyrian: A modern cross-platform port of Tyrian
3
* Copyright (C) 2007-2009 The OpenTyrian Development Team
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
#include "arg_parse.h"
20
#include "mingw_fixes.h"
30
static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt );
32
static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option );
33
static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option );
35
Option parse_args( int argc, const char *argv[], const Options *options )
38
static bool no_more_options = false;
40
static int first_nonopt = 1;
42
Option option = { NOT_OPTION, NULL, 0 };
43
option.argn = first_nonopt;
47
size_t arg_len = strlen(argv[argn]);
49
if (!no_more_options &&
50
argv[argn][0] == '-' && // first char is '-'
51
arg_len > 1) // option is not "-"
55
if (argv[argn][1] == '-') // string begins with "--"
57
if (arg_len == 2) // "--" alone indicates end of options
60
no_more_options = true;
64
argn = parse_long_opt(argc, argv, options, &option);
69
argn = parse_short_opt(argc, argv, options, &option);
72
// shift option in front of non-options
73
permute(argv, &first_nonopt, &option.argn, argn);
75
// don't include "--" in non-options
82
// skip non-options, permute later when option encountered
90
static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt )
92
const int nonopts = *first_opt - *first_nonopt;
94
// slide each of the options in front of the non-options
95
for (int i = *first_opt; i < after_opt; ++i)
97
for (int j = i; j > *first_nonopt; --j)
99
// swap argv[j] and argv[j - 1]
100
const char *temp = argv[j];
101
argv[j] = argv[j - 1];
105
// position of first non-option shifts right once for each option
109
// position of first option is initial position of first non-option
110
*first_opt -= nonopts;
113
static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option )
115
static size_t offset = 1; // ignore the "-"
117
int argn = option->argn;
119
const char *arg = argv[argn];
121
const size_t arg_len = strlen(arg);
123
const bool arg_attached = (offset + 1 < arg_len), // possible argument attached?
124
last_in_argv = (argn == argc - 1);
126
option->value = INVALID_OPTION;
128
for (; !(options->short_opt == 0 &&
129
options->long_opt == NULL); ++options)
131
if (options->short_opt != 0 &&
132
options->short_opt == arg[offset])
134
option->value = options->value;
136
if (options->has_arg)
138
if (arg_attached) // arg direclty follows option
140
option->arg = arg + offset + 1;
144
else if (!last_in_argv) // arg is next in argv
146
option->arg = argv[++argn];
152
option->value = OPTION_MISSING_ARG;
161
switch (option->value)
164
fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], argv[option->argn][offset]);
166
case OPTION_MISSING_ARG:
167
fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], argv[option->argn][offset]);
171
if (++offset >= arg_len)
177
return argn; // which arg in argv that parse_args() should examine when called again
180
static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option )
182
int argn = option->argn;
184
const char *arg = argv[argn] + 2; // ignore the "--"
186
const size_t arg_len = strlen(arg),
187
arg_opt_len = strchrnul(arg, '=') - arg; // length before "="
189
const bool arg_attached = (arg_opt_len < arg_len), // argument attached using "="?
190
last_in_argv = (argn == argc - 1);
192
option->value = INVALID_OPTION;
194
for (; !(options->short_opt == 0 &&
195
options->long_opt == NULL); ++options)
197
if (options->long_opt != NULL &&
198
strncmp(options->long_opt, arg, arg_opt_len) == 0) // matches (partially, at least)
200
if (option->value != INVALID_OPTION) // other match already found
202
option->value = AMBIGUOUS_OPTION;
206
option->value = options->value;
208
if (options->has_arg)
210
if (arg_attached) // arg is after "="
212
option->arg = arg + arg_opt_len + 1;
214
else if (!last_in_argv) // arg is next in argv
216
option->arg = argv[++argn];
218
else // arg is missing
220
option->value = OPTION_MISSING_ARG;
221
// can't break, gotta check for ambiguity
225
if (arg_opt_len == strlen(options->long_opt)) // exact match
227
// can't break for partial match, gotta check for ambiguity
231
switch (option->value)
234
fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[option->argn]);
236
case AMBIGUOUS_OPTION:
237
fprintf(stderr, "%s: option '%s' is ambiguous\n", argv[0], argv[option->argn]);
239
case OPTION_MISSING_ARG:
240
fprintf(stderr, "%s: option '%s' requires an argument\n", argv[0], argv[option->argn]);
246
return argn; // which arg in argv that parse_args() should examine when called again