[m-rev.] for review: Use musl getopt implementation.

Peter Wang novalazy at gmail.com
Fri Jun 7 15:29:54 AEST 2019


LICENSE:
    Mention files derived from musl library.

runtime/process_getopt:
    Process files from ../getopt/*.[ch] to produce
    mercury_getopt.h, mercury_getopt.c, mercury_getopt_long.c.

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

runtime/Mmakefile:
    Update list of source files.

util/Mmakefile:
    Use musl getopt if the system C library does not have getopt().

tests/debugger/browser_test.exp:
tests/debugger/browser_test.exp2:
tests/debugger/browser_test.exp3:
tests/hard_coded/runtime_opt.exp:
    Update expected error messages to match new getopt() output.

diff --git a/LICENSE b/LICENSE
index 283fba5a4..7735f474f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -27,6 +27,12 @@ entirety.
 
 --------
 
+The files in the `getopt' directory, as well as runtime/mercury_getopt.c,
+runtime/mercury_getopt_long.c and runtime/mercury_getopt.h, are derived
+from the musl library. See the 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..528fd9e53 100644
--- a/runtime/Mmakefile
+++ b/runtime/Mmakefile
@@ -173,7 +173,7 @@ CFILES = \
 	mercury_file.c				\
 	mercury_float.c				\
 	mercury_getopt.c			\
-	mercury_getopt1.c			\
+	mercury_getopt_long.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 d59fe56d1..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.
 #-----------------------------------------------------------------------------#
diff --git a/runtime/mercury_getopt.c b/runtime/mercury_getopt.c
new file mode 100644
index 000000000..2056d5dc1
--- /dev/null
+++ b/runtime/mercury_getopt.c
@@ -0,0 +1,119 @@
+/*
+Copyright © 2005-2014 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <string.h>
+#include <stdio.h>
+#define GETOPT_IMPL
+#include "mercury_getopt.h"
+
+char *MR_optarg;
+int MR_optind=1, MR_opterr=1, MR_optopt, MR__optpos, MR_optreset=0;
+
+#define optpos MR__optpos
+
+void MR__getopt_msg(const char *a, const char *b, const char *c, size_t l)
+{
+	FILE *f = stderr;
+	if (
+		fputs(a, f)>=0
+		&& fwrite(b, strlen(b), 1, f)
+		&& fwrite(c, 1, l, f)==l
+		&& putc('\n', f)
+	) { }
+}
+
+int MR_getopt(int argc, char * const argv[], const char *optstring)
+{
+	int i;
+	int c, d;
+	int k, l;
+	char *optchar;
+
+	if (!MR_optind || MR_optreset) {
+		MR_optreset = 0;
+		MR__optpos = 0;
+		MR_optind = 1;
+	}
+
+	if (MR_optind >= argc || !argv[MR_optind])
+		return -1;
+
+	if (argv[MR_optind][0] != '-') {
+		if (optstring[0] == '-') {
+			MR_optarg = argv[MR_optind++];
+			return 1;
+		}
+		return -1;
+	}
+
+	if (!argv[MR_optind][1])
+		return -1;
+
+	if (argv[MR_optind][1] == '-' && !argv[MR_optind][2])
+		return MR_optind++, -1;
+
+	if (!optpos) optpos++;
+	c = argv[MR_optind][optpos];
+	k = 1;
+	optchar = argv[MR_optind]+optpos;
+	optpos += k;
+
+	if (!argv[MR_optind][optpos]) {
+		MR_optind++;
+		optpos = 0;
+	}
+
+	if (optstring[0] == '-' || optstring[0] == '+')
+		optstring++;
+
+	i = 0;
+	d = 0;
+	do {
+		d = optstring[i];
+		l = d ? 1 : 0;
+		if (l>0) i+=l; else i++;
+	} while (l && d != c);
+
+	if (d != c || c == ':') {
+		MR_optopt = c;
+		if (optstring[0] != ':' && MR_opterr)
+			MR__getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
+		return '?';
+	}
+	if (optstring[i] == ':') {
+		MR_optarg = 0;
+		if (optstring[i+1] != ':' || optpos) {
+			MR_optarg = argv[MR_optind++] + optpos;
+			optpos = 0;
+		}
+		if (MR_optind > argc) {
+			MR_optopt = c;
+			if (optstring[0] == ':') return ':';
+			if (MR_opterr) MR__getopt_msg(argv[0],
+				": option requires an argument: ",
+				optchar, k);
+			return '?';
+		}
+	}
+	return c;
+}
diff --git a/runtime/mercury_getopt.h b/runtime/mercury_getopt.h
new file mode 100644
index 000000000..85885a60a
--- /dev/null
+++ b/runtime/mercury_getopt.h
@@ -0,0 +1,57 @@
+/*
+Copyright © 2005-2014 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MERCURY_GETOPT_H
+#define MERCURY_GETOPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int MR_getopt(int, char * const [], const char *);
+extern char *MR_optarg;
+extern int MR_optind, MR_opterr, MR_optopt, MR_optreset;
+
+struct MR_option {
+	const char *name;
+	int has_arg;
+	int *flag;
+	int val;
+};
+
+int MR_getopt_long(int, char *const *, const char *, const struct MR_option *, int *);
+int MR_getopt_long_only(int, char *const *, const char *, const struct MR_option *, int *);
+
+#define MR_no_argument        0
+#define MR_required_argument  1
+#define MR_optional_argument  2
+
+#ifdef GETOPT_IMPL
+void MR__getopt_msg(const char *a, const char *b, const char *c, size_t l);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/runtime/mercury_getopt_long.c b/runtime/mercury_getopt_long.c
new file mode 100644
index 000000000..c39a3e9e4
--- /dev/null
+++ b/runtime/mercury_getopt_long.c
@@ -0,0 +1,167 @@
+/*
+Copyright © 2005-2014 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#define GETOPT_IMPL
+#include "mercury_getopt.h"
+
+extern int MR__optpos;
+
+static void permute(char *const *argv, int dest, int src)
+{
+	char **av = (char **)argv;
+	char *tmp = av[src];
+	int i;
+	for (i=src; i>dest; i--)
+		av[i] = av[i-1];
+	av[dest] = tmp;
+}
+
+static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct MR_option *longopts, int *idx, int longonly);
+
+static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct MR_option *longopts, int *idx, int longonly)
+{
+	int ret, skipped, resumed;
+	if (!MR_optind || MR_optreset) {
+		MR_optreset = 0;
+		MR__optpos = 0;
+		MR_optind = 1;
+	}
+	if (MR_optind >= argc || !argv[MR_optind]) return -1;
+	skipped = MR_optind;
+	if (optstring[0] != '+' && optstring[0] != '-') {
+		int i;
+		for (i=MR_optind; ; i++) {
+			if (i >= argc || !argv[i]) return -1;
+			if (argv[i][0] == '-' && argv[i][1]) break;
+		}
+		MR_optind = i;
+	}
+	resumed = MR_optind;
+	ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
+	if (resumed > skipped) {
+		int i, cnt = MR_optind-resumed;
+		for (i=0; i<cnt; i++)
+			permute(argv, skipped, MR_optind-1);
+		MR_optind = skipped + cnt;
+	}
+	return ret;
+}
+
+static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct MR_option *longopts, int *idx, int longonly)
+{
+	MR_optarg = 0;
+	if (longopts && argv[MR_optind][0] == '-' &&
+		((longonly && argv[MR_optind][1] && argv[MR_optind][1] != '-') ||
+		 (argv[MR_optind][1] == '-' && argv[MR_optind][2])))
+	{
+		int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
+		int i, cnt, match;
+		char *arg, *opt, *start = argv[MR_optind]+1;
+		for (cnt=i=0; longopts[i].name; i++) {
+			const char *name = longopts[i].name;
+			opt = start;
+			if (*opt == '-') opt++;
+			while (*opt && *opt != '=' && *opt == *name)
+				name++, opt++;
+			if (*opt && *opt != '=') continue;
+			arg = opt;
+			match = i;
+			if (!*name) {
+				cnt = 1;
+				break;
+			}
+			cnt++;
+		}
+		if (cnt==1 && longonly && arg-start == 1) {
+			int l = arg-start;
+			for (i=0; optstring[i]; i++) {
+				int j;
+				for (j=0; j<l && start[j]==optstring[i+j]; j++);
+				if (j==l) {
+					cnt++;
+					break;
+				}
+			}
+		}
+		if (cnt==1) {
+			i = match;
+			opt = arg;
+			MR_optind++;
+			if (*opt == '=') {
+				if (!longopts[i].has_arg) {
+					MR_optopt = longopts[i].val;
+					if (colon || !MR_opterr)
+						return '?';
+					MR__getopt_msg(argv[0],
+						": option does not take an argument: ",
+						longopts[i].name,
+						strlen(longopts[i].name));
+					return '?';
+				}
+				MR_optarg = opt+1;
+			} else if (longopts[i].has_arg == MR_required_argument) {
+				if (!(MR_optarg = argv[MR_optind])) {
+					MR_optopt = longopts[i].val;
+					if (colon) return ':';
+					if (!MR_opterr) return '?';
+					MR__getopt_msg(argv[0],
+						": option requires an argument: ",
+						longopts[i].name,
+						strlen(longopts[i].name));
+					return '?';
+				}
+				MR_optind++;
+			}
+			if (idx) *idx = i;
+			if (longopts[i].flag) {
+				*longopts[i].flag = longopts[i].val;
+				return 0;
+			}
+			return longopts[i].val;
+		}
+		if (argv[MR_optind][1] == '-') {
+			MR_optopt = 0;
+			if (!colon && MR_opterr)
+				MR__getopt_msg(argv[0], cnt ?
+					": option is ambiguous: " :
+					": unrecognized option: ",
+					argv[MR_optind]+2,
+					strlen(argv[MR_optind]+2));
+			MR_optind++;
+			return '?';
+		}
+	}
+	return MR_getopt(argc, argv, optstring);
+}
+
+int MR_getopt_long(int argc, char *const *argv, const char *optstring, const struct MR_option *longopts, int *idx)
+{
+	return __getopt_long(argc, argv, optstring, longopts, idx, 0);
+}
+
+int MR_getopt_long_only(int argc, char *const *argv, const char *optstring, const struct MR_option *longopts, int *idx)
+{
+	return __getopt_long(argc, argv, optstring, longopts, idx, 1);
+}
diff --git a/runtime/process_getopt b/runtime/process_getopt
index 5be8ee95a..6200540fe 100755
--- a/runtime/process_getopt
+++ b/runtime/process_getopt
@@ -1,56 +1,31 @@
 #!/bin/sh
+# This generates mercury_getopt*.[ch] from the files in ../getopt
 
-chmod 644 mercury_getopt.h mercury_getopt.c mercury_getopt1.c
+chmod 644 mercury_getopt.h mercury_getopt.c mercury_getopt_long.c
 
-for file in getopt.h getopt.c getopt1.c
+for file in getopt.h getopt.c getopt_long.c
 do
 	echo processing mercury_$file
-	cp GETOPT/$file mercury_$file
+	cp ../getopt/$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/\<getopt\(.*(\)/s//MR_getopt\1/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/\<optreset\>/s//MR_optreset/g
+	g/\<__optpos\>/s//MR__optpos/g
+	g/\<__optreset\>/s//MR__optreset/g
+	g/\<__getopt_msg\>/s//MR__getopt_msg/g
+	g/\<struct option\>/s//struct MR_option/g
+	g/\<no_argument\>/s//MR_no_argument/g
+	g/\<required_argument\>/s//MR_required_argument/g
+	g/\<optional_argument\>/s//MR_optional_argument/g
+	g/\<_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 mercury_getopt_long.c
diff --git a/tests/debugger/browser_test.exp b/tests/debugger/browser_test.exp
index 5521cbaa9..e6b38c554 100644
--- a/tests/debugger/browser_test.exp
+++ b/tests/debugger/browser_test.exp
@@ -77,7 +77,7 @@ big(
   3, 
   big(big(small, 4, big(small, 5, small)), 6, small))
 mdb> print --xyzzy 1
-print: unrecognized option `--xyzzy'
+print: unrecognized option: xyzzy
 mdb: print: usage error -- type `help print' for help.
 mdb> browse 1; print; quit
 big(
diff --git a/tests/debugger/browser_test.exp2 b/tests/debugger/browser_test.exp2
index d047d8636..3a534cd8a 100644
--- a/tests/debugger/browser_test.exp2
+++ b/tests/debugger/browser_test.exp2
@@ -53,7 +53,7 @@ big(
   3, 
   big(big(small, 4, big(small, 5, small)), 6, small))
 mdb> print --xyzzy 1
-print: unrecognized option `--xyzzy'
+print: unrecognized option: xyzzy
 mdb: print: usage error -- type `help print' for help.
 mdb> browse 1; print; quit
 big(big(big(small, 1, small), 2, small), 3, big(big(small, 4, big/3), 6, small))
diff --git a/tests/debugger/browser_test.exp3 b/tests/debugger/browser_test.exp3
index 21b2755b9..f570261f8 100644
--- a/tests/debugger/browser_test.exp3
+++ b/tests/debugger/browser_test.exp3
@@ -62,7 +62,7 @@ big(
   3, 
   big(big(small, 4, big(small, 5, small)), 6, small))
 mdb> print --xyzzy 1
-print: unrecognized option `--xyzzy'
+print: unrecognized option: xyzzy
 mdb: print: usage error -- type `help print' for help.
 mdb> browse 1; print; quit
 big(
diff --git a/tests/hard_coded/runtime_opt.exp b/tests/hard_coded/runtime_opt.exp
index 0469d083c..9907a6be7 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..6854dac51 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=../getopt/getopt.c
 else
-	GETOPT_SRC=
+    GETOPT_SRC=
 endif
 
 # mkinit.c needs `struct stat'
-- 
2.21.0



More information about the reviews mailing list