[m-rev.] for review: Replace GNU getopt implementation with ya_getopt.

Peter Wang novalazy at gmail.com
Sun Jun 2 11:21:01 AEST 2019


Assuming ya_getopt is fine, I intend to:
  - add the files ya_getopt/getopt.[ch], ya_getopt/README.md
  - apply the following patch
  - delete runtime/GETOPT

I have omitted deletion lines in files that are wholely replaced
in the following diff.

----

runtime/process_getopt:
    Produce mercury_getopt.[ch] from ya_getopt/*.[ch] instead of
    runtime/GETOPT/*.[ch]

runtime/mercury_getopt.c:
runtime/mercury_getopt.h:
    Add files generated by process_getopt from ya_getopt.

runtime/Mmakefile:
runtime/RESERVED_MACRO_NAMES:
runtime/mercury_getopt1.c:
    Delete other vestiges of GNU getopt implementation.

util/Mmakefile:
    Use ya_getopt for utilities if the system C library does not include
    getopt().

tests/hard_coded/runtime_opt.exp:
    Update expected error message. ya_getopt uses single quotes instead
    of `...' convention. (Newer GNU getopt implementations also use
    single quotes.)

LICENSE:
    Add licensing information about ya_getopt files.

diff --git a/LICENSE b/LICENSE
index 283fba5a4..5bb3faa8d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -27,6 +27,12 @@ entirety.
 
 --------
 
+The files in the `ya_getopt' directory, runtime/mercury_getopt.c and
+runtime/mercury_getopt.h are distributed under a 2-clause BSD-style license.
+See those files for copyright information.
+
+--------
+
 A few files (config.guess, config.sub) are derived from code that is
 copyright by the Free Software Foundation, Inc, and are distributed
 under the GNU General Public License version 2.
diff --git a/runtime/Mmakefile b/runtime/Mmakefile
index 6519383d6..21492edc5 100644
--- a/runtime/Mmakefile
+++ b/runtime/Mmakefile
@@ -173,7 +173,6 @@ CFILES = \
 	mercury_file.c				\
 	mercury_float.c				\
 	mercury_getopt.c			\
-	mercury_getopt1.c			\
 	mercury_grade.c				\
 	mercury_hash_table.c			\
 	mercury_heap_profile.c			\
diff --git a/runtime/RESERVED_MACRO_NAMES b/runtime/RESERVED_MACRO_NAMES
index 9e1c203b3..39251a071 100644
--- a/runtime/RESERVED_MACRO_NAMES
+++ b/runtime/RESERVED_MACRO_NAMES
@@ -1,5 +1,6 @@
 #-----------------------------------------------------------------------------#
 # Copyright (C) 2000-2004, 2006, 2011 The University of Melbourne.
+# Copyright (C) 2013-2014, 2019 The Mercury team
 # This file may only be copied under the terms of the GNU General
 # Public License - see the file COPYING in the Mercury distribution.
 #-----------------------------------------------------------------------------#
@@ -10,11 +11,6 @@
 # directory.
 #
 #-----------------------------------------------------------------------------#
-# This is defined by mercury_getopt.h
-# That header file is NOT #included by any of the other headers,
-# so this doesn't cause any name space polution.
-__GNU_LIBRARY__
-#-----------------------------------------------------------------------------#
 # These are defined by mercury_signal.h. The first is defined only temporarily,
 # and the second eliminates gratuitous differences between Linux versions.
 # Neither really pollutes the namespace.
diff --git a/runtime/mercury_getopt.c b/runtime/mercury_getopt.c
index 876fa6afe..01f86e729 100644
--- a/runtime/mercury_getopt.c
+++ b/runtime/mercury_getopt.c
@@ -1,1052 +1,312 @@
+/* -*- indent-tabs-mode: nil -*-
+ *
+ * ya_getopt  - Yet another getopt
+ * https://github.com/kubo/ya_getopt
+ *
+ * Copyright 2015 Kubo Takehiro <kubo at jiubao.org>
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *       of conditions and the following disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of the authors.
+ *
+ */
 #include <stdio.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include "mercury_getopt.h"
 
 char *MR_optarg = NULL;
 int MR_optind = 1;
 int MR_opterr = 1;
 int MR_optopt = '?';
+static char *MR_optnext = NULL;
+static int posixly_correct = -1;
+static int handle_nonopt_argv = 0;
 
+static void MR_getopt_error(const char *optstring, const char *format, ...);
+static void check_gnu_extension(const char *optstring);
+static int MR_getopt_internal(int argc, char * const argv[], const char *optstring, const struct MR_option *longopts, int *longindex, int long_only);
+static int MR_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only);
+static int MR_getopt_longopts(int argc, char * const argv[], char *arg, const char *optstring, const struct MR_option *longopts, int *longindex, int *long_only_flag);
 
+static void MR_getopt_error(const char *optstring, const char *format, ...)
 {
+    if (MR_opterr && optstring[0] != ':') {
+        va_list ap;
+        va_start(ap, format);
+        vfprintf(stderr, format, ap);
+        va_end(ap);
+    }
+}
 
+static void check_gnu_extension(const char *optstring)
 {
+    if (optstring[0] == '+' || getenv("POSIXLY_CORRECT") != NULL) {
+        posixly_correct = 1;
+    } else {
+        posixly_correct = 0;
+    }
+    if (optstring[0] == '-') {
+        handle_nonopt_argv = 1;
+    } else {
+        handle_nonopt_argv = 0;
     }
 }
 
+int MR_getopt(int argc, char * const argv[], const char *optstring)
 {
+    return MR_getopt_internal(argc, argv, optstring, NULL, NULL, 0);
 }
 
+int MR_getopt_long(int argc, char * const argv[], const char *optstring, const struct MR_option *longopts, int *longindex)
 {
+    return MR_getopt_internal(argc, argv, optstring, longopts, longindex, 0);
 }
 
+int MR_getopt_long_only(int argc, char * const argv[], const char *optstring, const struct MR_option *longopts, int *longindex)
 {
+    return MR_getopt_internal(argc, argv, optstring, longopts, longindex, 1);
 }
 
+static int MR_getopt_internal(int argc, char * const argv[], const char *optstring, const struct MR_option *longopts, int *longindex, int long_only)
 {
+    static int start, end;
 
+    if (MR_optopt == '?') {
+        MR_optopt = 0;
     }
 
+    if (posixly_correct == -1) {
+        check_gnu_extension(optstring);
     }
 
+    if (MR_optind == 0) {
+        check_gnu_extension(optstring);
+        MR_optind = 1;
+        MR_optnext = NULL;
     }
 
+    switch (optstring[0]) {
+    case '+':
+    case '-':
+        optstring++;
+    }
 
+    if (MR_optnext == NULL && start != 0) {
+        int last_pos = MR_optind - 1;
+
+        MR_optind -= end - start;
+        if (MR_optind <= 0) {
+            MR_optind = 1;
+        }
+        while (start < end--) {
+            int i;
+            char *arg = argv[end];
+
+            for (i = end; i < last_pos; i++) {
+                ((char **)argv)[i] = argv[i + 1];
+            }
+            ((char const **)argv)[i] = arg;
+            last_pos--;
+        }
+        start = 0;
+    }
 
+    if (MR_optind >= argc) {
+        MR_optarg = NULL;
+        return -1;
+    }
+    if (MR_optnext == NULL) {
+        const char *arg = argv[MR_optind];
+        if (*arg != '-') {
+            if (handle_nonopt_argv) {
+                MR_optarg = argv[MR_optind++];
+                start = 0;
+                return 1;
+            } else if (posixly_correct) {
+                MR_optarg = NULL;
+                return -1;
+            } else {
+                int i;
+
+                start = MR_optind;
+                for (i = MR_optind + 1; i < argc; i++) {
+                    if (argv[i][0] == '-') {
+                        end = i;
+                        break;
+                    }
+                }
+                if (i == argc) {
+                    MR_optarg = NULL;
+                    return -1;
+                }
+                MR_optind = i;
+                arg = argv[MR_optind];
+            }
+        }
+        if (strcmp(arg, "--") == 0) {
+            MR_optind++;
+            return -1;
+        }
+        if (longopts != NULL && arg[1] == '-') {
+            return MR_getopt_longopts(argc, argv, argv[MR_optind] + 2, optstring, longopts, longindex, NULL);
+        }
+    }
 
+    if (MR_optnext == NULL) {
+        MR_optnext = argv[MR_optind] + 1;
+    }
+    if (long_only) {
+        int long_only_flag = 0;
+        int rv = MR_getopt_longopts(argc, argv, MR_optnext, optstring, longopts, longindex, &long_only_flag);
+        if (!long_only_flag) {
+            MR_optnext = NULL;
+            return rv;
+        }
+    }
 
+    return MR_getopt_shortopts(argc, argv, optstring, long_only);
 }
 
+static int MR_getopt_shortopts(int argc, char * const argv[], const char *optstring, int long_only)
 {
+    int opt = *MR_optnext;
+    const char *os = strchr(optstring, opt);
+
+    if (os == NULL) {
+        MR_optarg = NULL;
+        if (long_only) {
+            MR_getopt_error(optstring, "%s: unrecognized option '-%s'\n", argv[0], MR_optnext);
+            MR_optind++;
+            MR_optnext = NULL;
+        } else {
+            MR_optopt = opt;
+            MR_getopt_error(optstring, "%s: invalid option -- '%c'\n", argv[0], opt);
+            if (*(++MR_optnext) == 0) {
+                MR_optind++;
+                MR_optnext = NULL;
+            }
+        }
+        return '?';
+    }
+    if (os[1] == ':') {
+        if (MR_optnext[1] == 0) {
+            MR_optind++;
+            if (os[2] == ':') {
+                /* optional argument */
+                MR_optarg = NULL;
+            } else {
+                if (MR_optind == argc) {
+                    MR_optarg = NULL;
+                    MR_optopt = opt;
+                    MR_getopt_error(optstring, "%s: option requires an argument -- '%c'\n", argv[0], opt);
+                    if (optstring[0] == ':') {
+                        return ':';
+                    } else {
+                        return '?';
+                    }
+                }
+                MR_optarg = argv[MR_optind];
+                MR_optind++;
+            }
+        } else {
+            MR_optarg = MR_optnext + 1;
+            MR_optind++;
+        }
+        MR_optnext = NULL;
+    } else {
+        MR_optarg = NULL;
+        if (MR_optnext[1] == 0) {
+            MR_optnext = NULL;
+            MR_optind++;
+        } else {
+            MR_optnext++;
+        }
+    }
+    return opt;
 }
 
+static int MR_getopt_longopts(int argc, char * const argv[], char *arg, const char *optstring, const struct MR_option *longopts, int *longindex, int *long_only_flag)
 {
+    char *val = NULL;
+    const struct MR_option *opt;
+    size_t namelen;
+    int idx;
+
+    for (idx = 0; longopts[idx].name != NULL; idx++) {
+        opt = &longopts[idx];
+        namelen = strlen(opt->name);
+        if (strncmp(arg, opt->name, namelen) == 0) {
+            switch (arg[namelen]) {
+            case '\0':
+                switch (opt->has_arg) {
+                case MR_required_argument:
+                    MR_optind++;
+                    if (MR_optind == argc) {
+                        MR_optarg = NULL;
+                        MR_optopt = opt->val;
+                        MR_getopt_error(optstring, "%s: option '--%s' requires an argument\n", argv[0], opt->name);
+                        if (optstring[0] == ':') {
+                            return ':';
+                        } else {
+                            return '?';
+                        }
+                    }
+                    val = argv[MR_optind];
+                    break;
+                }
+                goto found;
+            case '=':
+                if (opt->has_arg == MR_no_argument) {
+                    const char *hyphens = (argv[MR_optind][1] == '-') ? "--" : "-";
+
+                    MR_optind++;
+                    MR_optarg = NULL;
+                    MR_optopt = opt->val;
+                    MR_getopt_error(optstring, "%s: option '%s%s' doesn't allow an argument\n", argv[0], hyphens, opt->name);
+                    return '?';
+                }
+                val = arg + namelen + 1;
+                goto found;
+            }
+        }
     }
+    if (long_only_flag) {
+        *long_only_flag = 1;
+    } else {
+        MR_getopt_error(optstring, "%s: unrecognized option '%s'\n", argv[0], argv[MR_optind]);
+        MR_optind++;
     }
+    return '?';
+found:
+    MR_optarg = val;
+    MR_optind++;
+    if (opt->flag) {
+        *opt->flag = opt->val;
+    }
+    if (longindex) {
+        *longindex = idx;
+    }
+    return opt->flag ? 0 : opt->val;
 }

diff --git a/runtime/mercury_getopt.h b/runtime/mercury_getopt.h
index 20dabc004..4f4e0033c 100644
--- a/runtime/mercury_getopt.h
+++ b/runtime/mercury_getopt.h
@@ -1,133 +1,77 @@
+/* -*- indent-tabs-mode: nil -*-
+ *
+ * ya_getopt  - Yet another getopt
+ * https://github.com/kubo/ya_getopt
+ *
+ * Copyright 2015 Kubo Takehiro <kubo at jiubao.org>
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *       of conditions and the following disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of the authors.
+ *
+ */
 #ifndef MERCURY_GETOPT_H
 #define MERCURY_GETOPT_H 1
 
+#if defined(__cplusplus)
 extern "C" {
 #endif
 
+#define MR_no_argument        0
+#define MR_required_argument  1
+#define MR_optional_argument  2
 
+struct MR_option {
+    const char *name;
+    int has_arg;
+    int *flag;
+    int val;
 };
 
+int MR_getopt(int argc, char * const argv[], const char *optstring);
+int MR_getopt_long(int argc, char * const argv[], const char *optstring,
+                   const struct MR_option *longopts, int *longindex);
+int MR_getopt_long_only(int argc, char * const argv[], const char *optstring,
+                        const struct MR_option *longopts, int *longindex);
 
+extern char *MR_optarg;
+extern int MR_optind, MR_opterr, MR_optopt;
+
+#if 0
+#define getopt ya_getopt
+#define getopt_long ya_getopt_long
+#define getopt_long_only ya_getopt_long_only
+#define optarg MR_optarg
+#define optind MR_optind
+#define opterr MR_opterr
+#define optopt MR_optopt
+#define no_argument MR_no_argument
+#define required_argument MR_required_argument
+#define optional_argument MR_optional_argument
+#endif
 
+#if defined(__cplusplus)
 }
 #endif
 
+#endif

diff --git a/runtime/mercury_getopt1.c b/runtime/mercury_getopt1.c
deleted file mode 100644
index 4a1f2e012..000000000
--- a/runtime/mercury_getopt1.c
+++ /dev/null

diff --git a/runtime/process_getopt b/runtime/process_getopt
index 5be8ee95a..4ac453698 100755
--- a/runtime/process_getopt
+++ b/runtime/process_getopt
@@ -1,56 +1,29 @@
 #!/bin/sh
+# This generates mercury_getopt.[ch] from ../ya_getopt/getopt.[ch]
 
-chmod 644 mercury_getopt.h mercury_getopt.c mercury_getopt1.c
+chmod 644 mercury_getopt.h mercury_getopt.c
 
-for file in getopt.h getopt.c getopt1.c
+for file in getopt.h getopt.c
 do
 	echo processing mercury_$file
-	cp GETOPT/$file mercury_$file
+	cp ../ya_getopt/ya_$file mercury_$file
 	ex mercury_$file << END
-	g/#include/s/getopt/mercury_getopt/
-	g/\<getopt/s//MR_getopt/g
-	g/\<optarg/s//MR_optarg/g
-	g/\<optind/s//MR_optind/g
-	g/\<opterr/s//MR_opterr/g
-	g/\<optopt/s//MR_optopt/g
-	g/\<option/s//MR_option/g
-	g/\<_getopt/s//MR__getopt/g
-	g/\<_GETOPT/s//MERCURY_GETOPT/g
-	g/no_argument/s//MR_no_argument/g
-	g/required_argument/s//MR_required_argument/g
-	g/#ifndef/s/ELIDE_CODE/XXXELIDE_CODEXXX/
+	g/#include/s/ya_getopt/mercury_getopt/
+	g/\<ya_getopt\(.*(\)/s//MR_getopt\1/g
+	g/\<ya_optarg\>/s//MR_optarg/g
+	g/\<ya_optind\>/s//MR_optind/g
+	g/\<ya_opterr\>/s//MR_opterr/g
+	g/\<ya_optopt\>/s//MR_optopt/g
+	g/\<ya_optnext\>/s//MR_optnext/g
+	g/\<struct option\>/s//struct MR_option/g
+	g/\<ya_no_argument\>/s//MR_no_argument/g
+	g/\<ya_required_argument\>/s//MR_required_argument/g
+	g/\<ya_optional_argument\>/s//MR_optional_argument/g
+	g/#ifndef YA_GETOPT_NO_COMPAT_MACRO/s//#if 0/
+	g/\<YA_GETOPT_H\>/s//MERCURY_GETOPT_H/g
 	w
 	q
 END
 done
 
-echo finalizing mercury_getopt.h
-ex mercury_getopt.h << END
-1i
-#undef __GNU_LIBRARY__
-#define __GNU_LIBRARY__
-.
-w
-q
-END
-
-echo finalizing mercury_getopt.c
-ex mercury_getopt.c << END
-1i
-extern	char	*getenv(const char *);
-.
-g/nextchar/s//MR_nextchar/g
-g/ordering/s//MR_ordering/g
-g/posixly_correct/s//MR_posixly_correct/g
-g/first_nonopt/s//MR_first_nonopt/g
-g/last_nonopt/s//MR_last_nonopt/g
-g/__getopt_initialized/s//MR____getopt_initialized/g
-g/%s: MR_option/s//%s: option/g
-g/%s: unrecognized MR_option/s//%s: unrecognized option/g
-g/%s: illegal MR_option/s//%s: illegal option/g
-g/%s: invalid MR_option/s//%s: invalid option/g
-w
-q
-END
-
-chmod 444 mercury_getopt.h mercury_getopt.c mercury_getopt1.c
+chmod 444 mercury_getopt.h mercury_getopt.c
diff --git a/tests/hard_coded/runtime_opt.exp b/tests/hard_coded/runtime_opt.exp
index 0469d083c..74682ba0d 100644
--- a/tests/hard_coded/runtime_opt.exp
+++ b/tests/hard_coded/runtime_opt.exp
@@ -8,7 +8,7 @@ Option `--detstack-size=128 --small-detstack-size=128':
 Hello world (with non-standard options).
 
 Option `--this-is-not-a-real-option':
-mercury_runtime: unrecognized option `--this-is-not-a-real-option'
+mercury_runtime: unrecognized option '--this-is-not-a-real-option'
 The MERCURY_OPTIONS environment variable contains an invalid option.
 Please refer to the Environment Variables section of the Mercury
 User's Guide for details.
diff --git a/util/Mmakefile b/util/Mmakefile
index 73bbc236a..0726bf0a2 100644
--- a/util/Mmakefile
+++ b/util/Mmakefile
@@ -2,6 +2,7 @@
 # vim: ts=8 sw=8 noexpandtab
 #-----------------------------------------------------------------------------#
 # Copyright (C) 1995-2002, 2005, 2006-2007, 2010, 2012 The University of Melbourne.
+# Copyright (C) 2013, 2019 The Mercury team.
 # This file may only be copied under the terms of the GNU General
 # Public License - see the file COPYING in the Mercury distribution.
 #-----------------------------------------------------------------------------#
@@ -25,12 +26,10 @@ PROGFILENAMES=$(PROGS:%=%$(EXT_FOR_EXE))
 SRC=$(PROGS:%=%.c)
 
 # Link in local copy of getopt() only if required.
-# XXX The files in runtime/GETOPT may not compile cleanly with modern
-# compilers. We should replace it with another implementation.
 ifeq ("$(HAVE_GETOPT)","no")
-	GETOPT_SRC=$(RUNTIME_DIR)/GETOPT/getopt.c $(RUNTIME_DIR)/GETOPT/getopt1.c
+    GETOPT_SRC=../ya_getopt/ya_getopt.c ../ya_getopt/ya_getopt.h
 else
-	GETOPT_SRC=
+    GETOPT_SRC=
 endif
 
 # mkinit.c needs `struct stat'
-- 
2.21.0



More information about the reviews mailing list