turbocat a27452493c OpenTyrian: Sources uploaded
git-svn-id: svn://kolibrios.org@9169 a494cfbc-eb01-0410-851d-a64ba20cac60
2021-08-31 18:22:39 +00:00

249 lines
6.2 KiB
C

/*
* OpenTyrian: A modern cross-platform port of Tyrian
* Copyright (C) 2007-2009 The OpenTyrian Development Team
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "arg_parse.h"
#include "std_support.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt );
static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option );
static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option );
Option parse_args( int argc, const char *argv[], const Options *options )
{
static int argn = 1;
static bool no_more_options = false;
static int first_nonopt = 1;
Option option = { NOT_OPTION, NULL, 0 };
option.argn = first_nonopt;
while (argn < argc)
{
size_t arg_len = strlen(argv[argn]);
if (!no_more_options &&
argv[argn][0] == '-' && // first char is '-'
arg_len > 1) // option is not "-"
{
option.argn = argn;
if (argv[argn][1] == '-') // string begins with "--"
{
if (arg_len == 2) // "--" alone indicates end of options
{
++argn;
no_more_options = true;
}
else
{
argn = parse_long_opt(argc, argv, options, &option);
}
}
else
{
argn = parse_short_opt(argc, argv, options, &option);
}
// shift option in front of non-options
permute(argv, &first_nonopt, &option.argn, argn);
// don't include "--" in non-options
if (no_more_options)
++option.argn;
break;
}
else
{
// skip non-options, permute later when option encountered
++argn;
}
}
return option;
}
static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt )
{
const int nonopts = *first_opt - *first_nonopt;
// slide each of the options in front of the non-options
for (int i = *first_opt; i < after_opt; ++i)
{
for (int j = i; j > *first_nonopt; --j)
{
// swap argv[j] and argv[j - 1]
const char *temp = argv[j];
argv[j] = argv[j - 1];
argv[j - 1] = temp;
}
// position of first non-option shifts right once for each option
++(*first_nonopt);
}
// position of first option is initial position of first non-option
*first_opt -= nonopts;
}
static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option )
{
static size_t offset = 1; // ignore the "-"
int argn = option->argn;
const char *arg = argv[argn];
const size_t arg_len = strlen(arg);
const bool arg_attached = (offset + 1 < arg_len), // possible argument attached?
last_in_argv = (argn == argc - 1);
option->value = INVALID_OPTION;
for (; !(options->short_opt == 0 &&
options->long_opt == NULL); ++options)
{
if (options->short_opt != 0 &&
options->short_opt == arg[offset])
{
option->value = options->value;
if (options->has_arg)
{
if (arg_attached) // arg direclty follows option
{
option->arg = arg + offset + 1;
offset = arg_len;
}
else if (!last_in_argv) // arg is next in argv
{
option->arg = argv[++argn];
offset = arg_len;
}
else
{
option->value = OPTION_MISSING_ARG;
break;
}
}
break;
}
}
switch (option->value)
{
case INVALID_OPTION:
fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], argv[option->argn][offset]);
break;
case OPTION_MISSING_ARG:
fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], argv[option->argn][offset]);
break;
}
if (++offset >= arg_len)
{
++argn;
offset = 1;
}
return argn; // which arg in argv that parse_args() should examine when called again
}
static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option )
{
int argn = option->argn;
const char *arg = argv[argn] + 2; // ignore the "--"
const size_t arg_len = strlen(arg),
arg_opt_len = ot_strchrnul(arg, '=') - arg; // length before "="
const bool arg_attached = (arg_opt_len < arg_len), // argument attached using "="?
last_in_argv = (argn == argc - 1);
option->value = INVALID_OPTION;
for (; !(options->short_opt == 0 &&
options->long_opt == NULL); ++options)
{
if (options->long_opt != NULL &&
strncmp(options->long_opt, arg, arg_opt_len) == 0) // matches (partially, at least)
{
if (option->value != INVALID_OPTION) // other match already found
{
option->value = AMBIGUOUS_OPTION;
break;
}
option->value = options->value;
if (options->has_arg)
{
if (arg_attached) // arg is after "="
{
option->arg = arg + arg_opt_len + 1;
}
else if (!last_in_argv) // arg is next in argv
{
option->arg = argv[++argn];
}
else // arg is missing
{
option->value = OPTION_MISSING_ARG;
// can't break, gotta check for ambiguity
}
}
if (arg_opt_len == strlen(options->long_opt)) // exact match
break;
// can't break for partial match, gotta check for ambiguity
}
}
switch (option->value)
{
case INVALID_OPTION:
fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[option->argn]);
break;
case AMBIGUOUS_OPTION:
fprintf(stderr, "%s: option '%s' is ambiguous\n", argv[0], argv[option->argn]);
break;
case OPTION_MISSING_ARG:
fprintf(stderr, "%s: option '%s' requires an argument\n", argv[0], argv[option->argn]);
break;
}
++argn;
return argn; // which arg in argv that parse_args() should examine when called again
}