diff --git a/libc/Makefile b/libc/Makefile
index c7843348..2a702cca 100644
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -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 \
diff --git a/libc/getopt/getopt.cpp b/libc/getopt/getopt.cpp
new file mode 100644
index 00000000..75776e79
--- /dev/null
+++ b/libc/getopt/getopt.cpp
@@ -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 .
+
+ getopt/getopt.cpp
+ Command-line parsing utility function.
+
+*******************************************************************************/
+
+#include
+#include
+
+extern "C" int getopt(int argc, char* const* argv, const char* shortopts)
+{
+ return getopt_long(argc, argv, shortopts, NULL, NULL);
+}
diff --git a/libc/getopt/getopt_long.cpp b/libc/getopt/getopt_long.cpp
new file mode 100644
index 00000000..33fdefe5
--- /dev/null
+++ b/libc/getopt/getopt_long.cpp
@@ -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 .
+
+ getopt/getopt_long.cpp
+ Command-line parsing utility function.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+
+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;
+}
diff --git a/libc/include/getopt.h b/libc/include/getopt.h
new file mode 100644
index 00000000..ee0047f8
--- /dev/null
+++ b/libc/include/getopt.h
@@ -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 .
+
+ getopt.h
+ Command-line parsing facility.
+
+*******************************************************************************/
+
+#ifndef INCLUDE_GETOPT_H
+#define INCLUDE_GETOPT_H
+
+#include
+
+__BEGIN_DECLS
+
+/* These declarations are repeated in . */
+#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
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index a695b262..00f93bc7 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -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 . */
+#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