aboutsummaryrefslogtreecommitdiff
path: root/src/port/getopt_long.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/port/getopt_long.c')
-rw-r--r--src/port/getopt_long.c53
1 files changed, 38 insertions, 15 deletions
diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c
index c9892769883..f83de0dff97 100644
--- a/src/port/getopt_long.c
+++ b/src/port/getopt_long.c
@@ -50,8 +50,11 @@
* This implementation does not use optreset. Instead, we guarantee that
* it can be restarted on a new argv array after a previous call returned -1,
* if the caller resets optind to 1 before the first call of the new series.
- * (Internally, this means we must be sure to reset "place" to EMSG before
- * returning -1.)
+ * (Internally, this means we must be sure to reset "place" to EMSG,
+ * "nonopt_start" to -1, and "force_nonopt" to false before returning -1.)
+ *
+ * Note that this routine reorders the pointers in argv (despite the const
+ * qualifier) so that all non-options will be at the end when -1 is returned.
*/
int
getopt_long(int argc, char *const argv[],
@@ -60,38 +63,58 @@ getopt_long(int argc, char *const argv[],
{
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
+ static int nonopt_start = -1;
+ static bool force_nonopt = false;
if (!*place)
{ /* update scanning pointer */
- if (optind >= argc)
+ char **args = (char **) argv;
+
+retry:
+
+ /*
+ * If we are out of arguments or only non-options remain, return -1.
+ */
+ if (optind >= argc || optind == nonopt_start)
{
place = EMSG;
+ nonopt_start = -1;
+ force_nonopt = false;
return -1;
}
place = argv[optind];
- if (place[0] != '-')
+ /*
+ * An argument is a non-option if it meets any of the following
+ * criteria: it follows an argument that is equivalent to the string
+ * "--", it does not start with '-', or it is equivalent to the string
+ * "-". When we encounter a non-option, we move it to the end of argv
+ * (after shifting all remaining arguments over to make room), and
+ * then we try again with the next argument.
+ */
+ if (force_nonopt || place[0] != '-' || place[1] == '\0')
{
- place = EMSG;
- return -1;
- }
+ for (int i = optind; i < argc - 1; i++)
+ args[i] = args[i + 1];
+ args[argc - 1] = place;
- place++;
+ if (nonopt_start == -1)
+ nonopt_start = argc - 1;
+ else
+ nonopt_start--;
- if (!*place)
- {
- /* treat "-" as not being an option */
- place = EMSG;
- return -1;
+ goto retry;
}
+ place++;
+
if (place[0] == '-' && place[1] == '\0')
{
/* found "--", treat it as end of options */
++optind;
- place = EMSG;
- return -1;
+ force_nonopt = true;
+ goto retry;
}
if (place[0] == '-' && place[1])