ref: 6e59bcb60efd0aa39bf30d68a387b3def02382ce
parent: a40d599cd79610b3f731a9e167f4c6d4ba446183
parent: 0649e6d65f5cca368f86986ca970fd3557db167c
author: Eldred Habert <[email protected]>
date: Tue Nov 5 21:34:26 EST 2019
Merge pull request #447 from ISSOtm/long_opts Add long options
--- a/Makefile
+++ b/Makefile
@@ -67,6 +67,7 @@
src/asm/symbol.o \
src/asm/util.o \
src/extern/err.o \
+ src/extern/getopt.o \
src/extern/utf8decoder.o \
src/version.o
@@ -82,6 +83,7 @@
src/link/section.o \
src/link/symbol.o \
src/extern/err.o \
+ src/extern/getopt.o \
src/hashmap.o \
src/version.o
@@ -88,6 +90,7 @@
rgbfix_obj := \
src/fix/main.o \
src/extern/err.o \
+ src/extern/getopt.o \
src/version.o
rgbgfx_obj := \
@@ -95,6 +98,7 @@
src/gfx/main.o \
src/gfx/makepng.o \
src/extern/err.o \
+ src/extern/getopt.o \
src/version.o
rgbasm: ${rgbasm_obj}
--- /dev/null
+++ b/include/extern/getopt.h
@@ -1,0 +1,45 @@
+/*
+ * Copyright © 2005-2019 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.
+ */
+
+/* This implementation was taken from musl and modified for RGBDS */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H
+
+extern char *optarg;
+extern int optind, opterr, optopt, optreset;
+
+struct option {
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+int getopt_long_only(int, char *const *, const char *, const struct option *, int *);
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#endif
--- a/src/asm/main.c
+++ b/src/asm/main.c
@@ -14,7 +14,6 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
-#include <unistd.h>
#include "asm/symbol.h"
#include "asm/fstack.h"
@@ -24,6 +23,7 @@
#include "asm/charmap.h"
#include "extern/err.h"
+#include "extern/getopt.h"
#include "helpers.h"
#include "version.h"
@@ -287,6 +287,37 @@
va_end(args);
}
+/* Short options */
+static char const *optstring = "b:D:Eg:hi:LM:o:p:r:Vvw";
+
+/*
+ * Equivalent long options
+ * Please keep in the same order as short opts
+ *
+ * Also, make sure long opts don't create ambiguity:
+ * A long opt's name should start with the same letter as its short opt,
+ * except if it doesn't create any ambiguity (`verbose` versus `version`).
+ * This is because long opt matching, even to a single char, is prioritized
+ * over short opt matching
+ */
+static struct option const longopts[] = {
+ { "binary-digits", required_argument, NULL, 'b' },
+ { "define", required_argument, NULL, 'D' },
+ { "export-all", no_argument, NULL, 'E' },
+ { "gfx-chars", required_argument, NULL, 'g' },
+ { "halt-without-nop", no_argument, NULL, 'h' },
+ { "include", required_argument, NULL, 'i' },
+ { "preserve-ld", no_argument, NULL, 'L' },
+ { "dependfile", required_argument, NULL, 'M' },
+ { "output", required_argument, NULL, 'o' },
+ { "pad-value", required_argument, NULL, 'p' },
+ { "recursion-depth", required_argument, NULL, 'r' },
+ { "version", no_argument, NULL, 'V' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "warning", no_argument, NULL, 'w' },
+ { NULL, no_argument, NULL, 0 }
+};
+
static void print_usage(void)
{
printf(
@@ -338,7 +369,8 @@
newopt = CurrentOptions;
- while ((ch = getopt(argc, argv, "b:D:Eg:hi:LM:o:p:r:Vvw")) != -1) {
+ while ((ch = getopt_long_only(argc, argv, optstring, longopts,
+ NULL)) != -1) {
switch (ch) {
case 'b':
if (strlen(optarg) == 2) {
--- /dev/null
+++ b/src/extern/getopt.c
@@ -1,0 +1,176 @@
+/*
+ * Copyright © 2005-2019 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.
+ */
+
+/* This implementation was taken from musl and modified for RGBDS */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern/getopt.h"
+
+int __optpos, __optreset;
+
+void __getopt_msg(const char *a, const char *b, const char *c, size_t l)
+{
+ FILE *f = stderr;
+ (void)(fputs(a, f)>=0
+ && fwrite(b, strlen(b), 1, f)
+ && fwrite(c, 1, l, f)==l
+ && putc('\n', f));
+}
+
+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 option *longopts, int *idx, int longonly);
+
+static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
+{
+ int ret, skipped, resumed;
+ if (!optind || __optreset) {
+ __optreset = 0;
+ __optpos = 0;
+ optind = 1;
+ }
+ if (optind >= argc || !argv[optind]) return -1;
+ skipped = optind;
+ if (optstring[0] != '+' && optstring[0] != '-') {
+ int i;
+ for (i=optind; ; i++) {
+ if (i >= argc || !argv[i]) return -1;
+ if (argv[i][0] == '-' && argv[i][1]) break;
+ }
+ optind = i;
+ }
+ resumed = optind;
+ ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
+ if (resumed > skipped) {
+ int i, cnt = optind-resumed;
+ for (i=0; i<cnt; i++)
+ permute(argv, skipped, optind-1);
+ optind = skipped + cnt;
+ }
+ return ret;
+}
+
+static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
+{
+ optarg = 0;
+ if (longopts && argv[optind][0] == '-' &&
+ ((longonly && argv[optind][1] && argv[optind][1] != '-') ||
+ (argv[optind][1] == '-' && argv[optind][2])))
+ {
+ int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
+ int i, cnt, match;
+ char *arg, *opt, *start = argv[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 == mblen(start, MB_LEN_MAX)) {
+ 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;
+ optind++;
+ if (*opt == '=') {
+ if (!longopts[i].has_arg) {
+ optopt = longopts[i].val;
+ if (colon || !opterr)
+ return '?';
+ __getopt_msg(argv[0],
+ ": option does not take an argument: ",
+ longopts[i].name,
+ strlen(longopts[i].name));
+ return '?';
+ }
+ optarg = opt+1;
+ } else if (longopts[i].has_arg == required_argument) {
+ if (!(optarg = argv[optind])) {
+ optopt = longopts[i].val;
+ if (colon) return ':';
+ if (!opterr) return '?';
+ __getopt_msg(argv[0],
+ ": option requires an argument: ",
+ longopts[i].name,
+ strlen(longopts[i].name));
+ return '?';
+ }
+ optind++;
+ }
+ if (idx) *idx = i;
+ if (longopts[i].flag) {
+ *longopts[i].flag = longopts[i].val;
+ return 0;
+ }
+ return longopts[i].val;
+ }
+ if (argv[optind][1] == '-') {
+ optopt = 0;
+ if (!colon && opterr)
+ __getopt_msg(argv[0], cnt ?
+ ": option is ambiguous: " :
+ ": unrecognized option: ",
+ argv[optind]+2,
+ strlen(argv[optind]+2));
+ optind++;
+ return '?';
+ }
+ }
+ return getopt(argc, argv, optstring);
+}
+
+int getopt_long_only(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
+{
+ return __getopt_long(argc, argv, optstring, longopts, idx, 1);
+}
--- a/src/fix/main.c
+++ b/src/fix/main.c
@@ -11,12 +11,44 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include "extern/err.h"
+#include "extern/getopt.h"
#include "version.h"
+/* Shoft options */
+static char const *optstring = "Ccf:i:jk:l:m:n:p:sr:t:Vv";
+
+/*
+ * Equivalent long options
+ * Please keep in the same order as short opts
+ *
+ * Also, make sure long opts don't create ambiguity:
+ * A long opt's name should start with the same letter as its short opt,
+ * except if it doesn't create any ambiguity (`verbose` versus `version`).
+ * This is because long opt matching, even to a single char, is prioritized
+ * over short opt matching
+ */
+static struct option const longopts[] = {
+ { "color-only", no_argument, NULL, 'C' },
+ { "color-compatible", no_argument, NULL, 'c' },
+ { "fix-spec", required_argument, NULL, 'f' },
+ { "game-id", required_argument, NULL, 'i' },
+ { "non-japanese", no_argument, NULL, 'j' },
+ { "new-licensee", required_argument, NULL, 'k' },
+ { "old-licensee", required_argument, NULL, 'l' },
+ { "mbc-type", required_argument, NULL, 'm' },
+ { "rom-version", required_argument, NULL, 'n' },
+ { "pad-value", required_argument, NULL, 'p' },
+ { "ram-size", required_argument, NULL, 'r' },
+ { "sgb-compatible", no_argument, NULL, 's' },
+ { "title", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, no_argument, NULL, 0 }
+};
+
static void print_usage(void)
{
printf(
@@ -66,7 +98,8 @@
int version = 0; /* mask ROM version number */
int padvalue = 0; /* to pad the rom with if it changes size */
- while ((ch = getopt(argc, argv, "Ccf:i:jk:l:m:n:p:sr:t:Vv")) != -1) {
+ while ((ch = getopt_long_only(argc, argv, optstring, longopts,
+ NULL)) != -1) {
switch (ch) {
case 'C':
coloronly = true;
--- a/src/gfx/main.c
+++ b/src/gfx/main.c
@@ -9,12 +9,47 @@
#include <png.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include "gfx/main.h"
+#include "extern/getopt.h"
#include "version.h"
+/* Short options */
+static char const *optstring = "Aa:CDd:Ffhmo:Pp:Tt:uVvx:";
+
+/*
+ * Equivalent long options
+ * Please keep in the same order as short opts
+ *
+ * Also, make sure long opts don't create ambiguity:
+ * A long opt's name should start with the same letter as its short opt,
+ * except if it doesn't create any ambiguity (`verbose` versus `version`).
+ * This is because long opt matching, even to a single char, is prioritized
+ * over short opt matching
+ */
+static struct option const longopts[] = {
+ { "output-attr-map", no_argument, NULL, 'A' },
+ { "attr-map", required_argument, NULL, 'a' },
+ { "color-curve", no_argument, NULL, 'C' },
+ { "debug", no_argument, NULL, 'D' },
+ { "depth", required_argument, NULL, 'd' },
+ { "fix", no_argument, NULL, 'f' },
+ { "fix-and-save", no_argument, NULL, 'F' },
+ { "horizontal", no_argument, NULL, 'h' },
+ { "mirror-tiles", no_argument, NULL, 'm' },
+ { "output", required_argument, NULL, 'o' },
+ { "output-palette", no_argument, NULL, 'P' },
+ { "palette", required_argument, NULL, 'p' },
+ { "output-tilemap", no_argument, NULL, 'T' },
+ { "tilemap", required_argument, NULL, 't' },
+ { "unique-tiles", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'V' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "trim-end", required_argument, NULL, 'x' },
+ { NULL, no_argument, NULL, 0 }
+};
+
static void print_usage(void)
{
printf(
@@ -45,7 +80,8 @@
depth = 2;
- while ((ch = getopt(argc, argv, "Aa:CDd:Ffhmo:Tt:uPp:Vvx:")) != -1) {
+ while ((ch = getopt_long_only(argc, argv, optstring, longopts,
+ NULL)) != -1) {
switch (ch) {
case 'A':
opts.attrmapout = true;
--- a/src/link/main.c
+++ b/src/link/main.c
@@ -8,7 +8,6 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
@@ -22,6 +21,7 @@
#include "link/output.h"
#include "extern/err.h"
+#include "extern/getopt.h"
#include "version.h"
bool isDmgMode; /* -d */
@@ -48,6 +48,35 @@
return file;
}
+/* Short options */
+static char const *optstring = "dl:m:n:O:o:p:s:tVvw";
+
+/*
+ * Equivalent long options
+ * Please keep in the same order as short opts
+ *
+ * Also, make sure long opts don't create ambiguity:
+ * A long opt's name should start with the same letter as its short opt,
+ * except if it doesn't create any ambiguity (`verbose` versus `version`).
+ * This is because long opt matching, even to a single char, is prioritized
+ * over short opt matching
+ */
+static struct option const longopts[] = {
+ { "dmg", no_argument, NULL, 'd' },
+ { "linkerscript", required_argument, NULL, 'l' },
+ { "map", required_argument, NULL, 'm' },
+ { "sym", required_argument, NULL, 'n' },
+ { "overlay", required_argument, NULL, 'O' },
+ { "output", required_argument, NULL, 'o' },
+ { "pad", required_argument, NULL, 'p' },
+ { "smart", required_argument, NULL, 's' },
+ { "tiny", no_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "wramx", no_argument, NULL, 'w' },
+ { NULL, no_argument, NULL, 0 }
+};
+
/**
* Prints the program's usage to stdout.
*/
@@ -73,7 +102,8 @@
unsigned long value; /* For storing `strtoul`'s return value */
/* Parse options */
- while ((optionChar = getopt(argc, argv, "dl:m:n:O:o:p:s:tVvw")) != -1) {
+ while ((optionChar = getopt_long_only(argc, argv, optstring, longopts,
+ NULL)) != -1) {
switch (optionChar) {
case 'd':
isDmgMode = true;