mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add getopt(3) and getopt_long(3).
This commit is contained in:
parent
bb2a1b3e73
commit
d5a7901b25
5 changed files with 362 additions and 4 deletions
|
@ -220,6 +220,8 @@ fsmarshall/fsm_listen.o \
|
|||
fsmarshall/fsm_mkserver.o \
|
||||
fsmarshall/fsm_recv.o \
|
||||
fsmarshall/fsm_send.o \
|
||||
getopt/getopt_long.o \
|
||||
getopt/getopt.o \
|
||||
grp/grent.o \
|
||||
init/init.o \
|
||||
libgen/basename.o \
|
||||
|
|
31
libc/getopt/getopt.cpp
Normal file
31
libc/getopt/getopt.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
getopt/getopt.cpp
|
||||
Command-line parsing utility function.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <getopt.h>
|
||||
|
||||
extern "C" int getopt(int argc, char* const* argv, const char* shortopts)
|
||||
{
|
||||
return getopt_long(argc, argv, shortopts, NULL, NULL);
|
||||
}
|
255
libc/getopt/getopt_long.cpp
Normal file
255
libc/getopt/getopt_long.cpp
Normal file
|
@ -0,0 +1,255 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
getopt/getopt_long.cpp
|
||||
Command-line parsing utility function.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
|
||||
extern "C" { char* optarg = NULL; }
|
||||
extern "C" { int opterr = 1; }
|
||||
extern "C" { int optind = 1; }
|
||||
extern "C" { int optopt = 0; }
|
||||
|
||||
static char* const* optcurargv;
|
||||
static const char* optcurarg;
|
||||
static int optcurind;
|
||||
static size_t optcurpos;
|
||||
|
||||
static bool optcurvalid = false;
|
||||
|
||||
static bool is_supported_option_declaration(const char* shortopts)
|
||||
{
|
||||
// Optional parameters to options only make sense after a option character.
|
||||
if ( shortopts[0] == ':' && shortopts[1] == ':' )
|
||||
return false;
|
||||
|
||||
for ( size_t i = 0; shortopts[i]; i++ )
|
||||
{
|
||||
if ( shortopts[i] == '-' || shortopts[i] == '+' || shortopts[i] == ';' )
|
||||
return false;
|
||||
|
||||
// We support a: and a::, but a::: doesn't make sense.
|
||||
if ( shortopts[i] == ':' &&
|
||||
shortopts[i+1] == ':' &&
|
||||
shortopts[i+2] == ':' )
|
||||
return false;
|
||||
|
||||
// We don't support that -W foo is the same as --foo.
|
||||
if ( shortopts[i] == 'W' && shortopts[i+1] == ';' )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: It would appear that it is allowed to abbreviate options if the
|
||||
// abbreviation isn't ambiguous. (WTF)
|
||||
static
|
||||
const struct option* find_long_option(char* arg,
|
||||
const struct option* longopts,
|
||||
int* option_index,
|
||||
char** option_arg)
|
||||
{
|
||||
char* argname = arg + 2;
|
||||
size_t argname_length = strcspn(arg, "=");
|
||||
|
||||
for ( int i = 0; longopts && longopts[i].name; i++ )
|
||||
{
|
||||
if ( longopts[i].has_arg == no_argument &&
|
||||
argname[argname_length] == '=' )
|
||||
continue;
|
||||
if ( strncmp(argname, longopts[i].name, argname_length) != 0 )
|
||||
continue;
|
||||
if ( longopts[i].name[argname_length] != '\0' )
|
||||
continue;
|
||||
return *option_arg = (argname[argname_length] == '=' ?
|
||||
argname + argname_length + 1 :
|
||||
NULL),
|
||||
*option_index = i,
|
||||
&longopts[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
int getopt_long(int argc, char* const* argv, const char* shortopts,
|
||||
const struct option* longopts, int* longindex)
|
||||
{
|
||||
assert(shortopts);
|
||||
assert(0 <= optind);
|
||||
|
||||
// Verify that the input string was compatible.
|
||||
if ( !is_supported_option_declaration(shortopts) )
|
||||
return errno = EINVAL, -1;
|
||||
|
||||
// The caller will handle missing arguments if the short options string
|
||||
// start with a colon character.
|
||||
bool caller_handles_missing_argument = false;
|
||||
if ( shortopts[0] == ':' )
|
||||
shortopts++,
|
||||
caller_handles_missing_argument = false;
|
||||
|
||||
// Check if we have finished processing the command line.
|
||||
if ( argc <= optind )
|
||||
return optcurvalid = false, -1;
|
||||
|
||||
char* arg = argv[optind];
|
||||
|
||||
// Stop if we encountered a NULL pointer.
|
||||
if ( !arg )
|
||||
return optcurvalid = false, -1;
|
||||
|
||||
// Stop if we encounteed an argument that didn't start with -.
|
||||
if ( arg[0] != '-' )
|
||||
return optcurvalid = false, -1;
|
||||
|
||||
// Stop if we encounted the string "-" as an argument.
|
||||
if ( arg[1] == '\0' )
|
||||
return optcurvalid = false, -1;
|
||||
|
||||
// Stop and skip the argument if we encountered the string "--".
|
||||
if ( arg[1] == '-' && arg[2] == '\0' )
|
||||
return optcurvalid = false, optind++, -1;
|
||||
|
||||
// Check if we encountered a long option.
|
||||
if ( arg[1] == '-' && arg[2] != '\0' )
|
||||
{
|
||||
// Locate the long option.
|
||||
int option_index;
|
||||
char* option_arg;
|
||||
const struct option* option =
|
||||
find_long_option(arg, longopts, &option_index, &option_arg);
|
||||
|
||||
// Increment so we don't process the next argument on the next call.
|
||||
optind++;
|
||||
|
||||
// Verify the long option is allowed.
|
||||
if ( !option )
|
||||
return '?';
|
||||
|
||||
// Check whether the next argument is the parameter to this option.
|
||||
if ( !option_arg && option->has_arg != no_argument )
|
||||
{
|
||||
if ( optind + 1 < argc && argv[optind] &&
|
||||
(option->has_arg == required_argument || argv[optind][0] != '-') )
|
||||
option_arg = argv[optind++];
|
||||
}
|
||||
|
||||
// Return an error if a required option parameter was missing.
|
||||
if ( !option_arg && option->has_arg == required_argument )
|
||||
{
|
||||
if ( caller_handles_missing_argument )
|
||||
return *longindex = option_index, ':';
|
||||
if ( opterr )
|
||||
error(0, 0, "option requires an argument `--%s'", option->name);
|
||||
return '?';
|
||||
}
|
||||
|
||||
return optarg = option_arg,
|
||||
*longindex = option_index,
|
||||
option->flag ? (*option->flag = option->val, 0) : option->val;
|
||||
}
|
||||
|
||||
// Unfortunately, there is no standardized optpos global variable, and
|
||||
// programs are supposed to be able to restart the parsing by setting optind
|
||||
// to 1. We need to keep track of the current position in the current
|
||||
// argument, but it would have to be invalidated whenever we restart the
|
||||
// parsing. We keep track of what the parsing input is supposed to be so we
|
||||
// can easily invalidate the current position in the current argument
|
||||
// whenever something fishy is going on.
|
||||
if ( !optcurvalid || optcurargv != argv || optcurarg != arg || optcurind != optind )
|
||||
{
|
||||
optcurargv = argv;
|
||||
optcurarg = arg;
|
||||
optcurind = optind;
|
||||
optcurpos = 1;
|
||||
optcurvalid = true;
|
||||
}
|
||||
|
||||
int short_option = (unsigned char) arg[optcurpos++];
|
||||
|
||||
// The character : can't be a option character.
|
||||
if ( short_option == ':' )
|
||||
short_option = '?';
|
||||
|
||||
// Verify the short option is allowed.
|
||||
char* opt_decl = strchr(shortopts, short_option);
|
||||
if ( !opt_decl )
|
||||
{
|
||||
optopt = short_option;
|
||||
short_option = '?';
|
||||
}
|
||||
|
||||
// Check whether the short option requires an parameter.
|
||||
if ( opt_decl[1] == ':' )
|
||||
{
|
||||
// This argument cannot contain more short options after this.
|
||||
optcurvalid = false;
|
||||
|
||||
// A double :: means optional parameter.
|
||||
bool required = opt_decl[2] != ':';
|
||||
|
||||
// The rest of the argument is the parameter if non-empty.
|
||||
if ( arg[optcurpos] )
|
||||
{
|
||||
optarg = arg + optcurpos;
|
||||
optind += 1;
|
||||
}
|
||||
|
||||
// Otherwise, the next element (if any) is the parameter.
|
||||
else if ( optind + 1 < argc && argv[optind+1] &&
|
||||
(required || argv[optind+1][0] != '-') )
|
||||
{
|
||||
optarg = argv[optind+1];
|
||||
optind += 2;
|
||||
}
|
||||
|
||||
// It's okay to have a missing parameter if it was optional.
|
||||
else if ( !required )
|
||||
{
|
||||
optarg = NULL;
|
||||
optind += 1;
|
||||
}
|
||||
|
||||
// Return an error if a required option parameter was missing.
|
||||
else
|
||||
{
|
||||
if ( caller_handles_missing_argument )
|
||||
return optopt = short_option, ':';
|
||||
if ( opterr )
|
||||
error(0, 0, "option requires an argument -- '%c'", short_option);
|
||||
return optopt = short_option, '?';
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached the last option, use the next index next time.
|
||||
else if ( !arg[optcurpos] )
|
||||
{
|
||||
optind++;
|
||||
optcurvalid = false;
|
||||
}
|
||||
|
||||
return short_option;
|
||||
}
|
59
libc/include/getopt.h
Normal file
59
libc/include/getopt.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
getopt.h
|
||||
Command-line parsing facility.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef INCLUDE_GETOPT_H
|
||||
#define INCLUDE_GETOPT_H
|
||||
|
||||
#include <features.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/* These declarations are repeated in <unistd.h>. */
|
||||
#ifndef __getopt_unistd_shared_declared
|
||||
#define __getopt_unistd_shared_declared
|
||||
extern char* optarg;
|
||||
extern int opterr;
|
||||
extern int optind;
|
||||
extern int optopt;
|
||||
|
||||
int getopt(int, char* const*, const char*);
|
||||
#endif
|
||||
|
||||
struct option
|
||||
{
|
||||
const char* name;
|
||||
int has_arg;
|
||||
int* flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
int getopt_long(int, char* const*, const char*, const struct option*, int*);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
|
@ -271,7 +271,7 @@ int fexecve(int, char* const [], char* const []);
|
|||
long fpathconf(int, int);
|
||||
int getgroups(int, gid_t []);
|
||||
long gethostid(void);
|
||||
int getopt(int, char* const [], const char*);
|
||||
|
||||
pid_t getpgrp(void);
|
||||
pid_t getsid(pid_t);
|
||||
int lockf(int, int, off_t);
|
||||
|
@ -289,9 +289,6 @@ int ttyname_r(int, char*, size_t);
|
|||
#if __POSIX_OBSOLETE <= 200801
|
||||
pid_t setpgrp(void);
|
||||
#endif
|
||||
|
||||
extern char* optarg;
|
||||
extern int opterr, optind, optopt;
|
||||
#endif
|
||||
|
||||
int access(const char*, int);
|
||||
|
@ -384,6 +381,20 @@ size_t writeleast(int fd, const void* buf, size_t least, size_t max);
|
|||
void* sbrk(__intptr_t increment);
|
||||
#endif
|
||||
|
||||
/* For compatibility with POSIX, declare getopt(3) here. */
|
||||
#if !defined(_SORTIX_SOURCE)
|
||||
/* These declarations are repeated in <getopt.h>. */
|
||||
#ifndef __getopt_unistd_shared_declared
|
||||
#define __getopt_unistd_shared_declared
|
||||
extern char* optarg;
|
||||
extern int opterr;
|
||||
extern int optind;
|
||||
extern int optopt;
|
||||
|
||||
int getopt(int, char* const*, const char*);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue