From 3bc17b2c279cae98da98c106fe9e203e836ce3e3 Mon Sep 17 00:00:00 2001
From: midipix <writeonce@midipix.org>
Date: Fri, 1 Jan 2016 02:58:20 -0500
Subject: argv.h: add support for hybrid options (i.e. -std, -pipe).

---
 src/internal/argv/argv.h     | 108 ++++++++++++++++++++++++++++++++++++++++---
 src/skin/sfrt_skin_default.c |   8 ++--
 2 files changed, 106 insertions(+), 10 deletions(-)

(limited to 'src')

diff --git a/src/internal/argv/argv.h b/src/internal/argv/argv.h
index a3c353d..dd6d18c 100644
--- a/src/internal/argv/argv.h
+++ b/src/internal/argv/argv.h
@@ -25,6 +25,38 @@
 #define ARGV_TAB_WIDTH			8
 #endif
 
+/*******************************************/
+/*                                         */
+/* support of hybrid options               */
+/* -------------------------               */
+/* hybrid options are very similar to      */
+/* long options, yet are prefixed by       */
+/* a single dash rather than two           */
+/* (i.e. -std, -isystem).                  */
+/* hybrid options are supported by this    */
+/* driver for compatibility with legacy    */
+/* tools; note, however, that the use      */
+/* of hybrid options should be strongly    */
+/* discouraged due to the limitations      */
+/* they impose on short options (for       */
+/* example, a driver implementing -std     */
+/* may not provide -s as a short option    */
+/* that takes an arbitrary value).         */
+/*                                         */
+/* SPACE: -hybrid VALUE (i.e. -MF file)    */
+/* EQUAL: -hybrid=VALUE (i.e. -std=c99)    */
+/* ONLY:  -opt accepted, --opt rejected    */
+/*                                         */
+/*******************************************/
+
+
+#define ARGV_OPTION_HYBRID_NONE		0x00
+#define ARGV_OPTION_HYBRID_ONLY		0x01
+#define ARGV_OPTION_HYBRID_SPACE	0x02
+#define ARGV_OPTION_HYBRID_EQUAL	0x04
+#define ARGV_OPTION_HYBRID_SWITCH	(ARGV_OPTION_HYBRID_SPACE \
+					| ARGV_OPTION_HYBRID_EQUAL)
+
 enum argv_optarg {
 	ARGV_OPTARG_NONE,
 	ARGV_OPTARG_REQUIRED,
@@ -44,6 +76,10 @@ enum argv_error {
 	ARGV_ERROR_OPTARG_NONE,
 	ARGV_ERROR_OPTARG_REQUIRED,
 	ARGV_ERROR_OPTARG_PARADIGM,
+	ARGV_ERROR_HYBRID_NONE,
+	ARGV_ERROR_HYBRID_ONLY,
+	ARGV_ERROR_HYBRID_SPACE,
+	ARGV_ERROR_HYBRID_EQUAL,
 };
 
 struct argv_option {
@@ -51,6 +87,7 @@ struct argv_option {
 	const char		short_name;
 	int			tag;
 	enum argv_optarg	optarg;
+	int			flags;
 	const char *		paradigm;
 	const char *		argname;
 	const char *		description;
@@ -169,6 +206,26 @@ static inline bool is_last_option(const char * arg)
 	return (arg[0]=='-') && (arg[1]=='-') && !arg[2];
 }
 
+static inline bool is_hybrid_option(
+	const char *			arg,
+	const struct argv_option	options[])
+{
+	const struct argv_option *	option;
+	struct argv_entry		entry;
+
+	if (!is_short_option(arg))
+		return false;
+
+	if (!(option = argv_long_option(++arg,options,&entry)))
+		return false;
+
+	if (!(option->flags & ARGV_OPTION_HYBRID_SWITCH))
+		if (argv_short_option(arg,options,&entry))
+			return false;
+
+	return true;
+}
+
 static inline bool is_arg_in_paradigm(const char * arg, const char * paradigm)
 {
 	size_t		len;
@@ -217,6 +274,7 @@ static void argv_scan(
 	bool				fval;
 	bool				fnext;
 	bool				fshort;
+	bool				fhybrid;
 	bool				fnoscan;
 
 	argv++;
@@ -229,7 +287,8 @@ static void argv_scan(
 	mentry	= meta ? meta->entries : 0;
 
 	while (ch && (ferror == ARGV_ERROR_OK)) {
-		option = 0;
+		option  = 0;
+		fhybrid = false;
 
 		if (fnoscan)
 			fval = true;
@@ -237,7 +296,10 @@ static void argv_scan(
 		else if (is_last_option(ch))
 			fnoscan = true;
 
-		else if ((fshort || is_short_option(ch))) {
+		else if (!fshort && is_hybrid_option(ch,options))
+			fhybrid = true;
+
+		if (!fnoscan && !fhybrid && (fshort || is_short_option(ch))) {
 			if (!fshort)
 				ch++;
 
@@ -285,8 +347,10 @@ static void argv_scan(
 			} else
 				ferror = ARGV_ERROR_SHORT_OPTION;
 
-		} else if ((is_long_option(ch))) {
-			if ((option = argv_long_option(ch+=2,options,&entry))) {
+		} else if (!fnoscan && (fhybrid || is_long_option(ch))) {
+			ch += (fhybrid ? 1 : 2);
+
+			if ((option = argv_long_option(ch,options,&entry))) {
 				val = ch + strlen(option->long_name);
 
 				/* val[0] is either '=' or '\0' */
@@ -295,13 +359,21 @@ static void argv_scan(
 					ch = *parg;
 				}
 
-				if (option->optarg == ARGV_OPTARG_NONE) {
+				if (fhybrid && !(option->flags & ARGV_OPTION_HYBRID_SWITCH))
+					ferror = ARGV_ERROR_HYBRID_NONE;
+				else if (option->optarg == ARGV_OPTARG_NONE) {
 					if (val[0]) {
 						ferror = ARGV_ERROR_OPTARG_NONE;
 						ctx->errch = val + 1;
 					} else
 						fval = false;
-				} else if (val[0] && !val[1])
+				} else if (!fhybrid && (option->flags & ARGV_OPTION_HYBRID_ONLY))
+					ferror = ARGV_ERROR_HYBRID_ONLY;
+				else if (fhybrid && !val[0] && !(option->flags & ARGV_OPTION_HYBRID_SPACE))
+					ferror = ARGV_ERROR_HYBRID_SPACE;
+				else if (fhybrid && val[0] && !(option->flags & ARGV_OPTION_HYBRID_EQUAL))
+					ferror = ARGV_ERROR_HYBRID_EQUAL;
+				else if (val[0] && !val[1])
 					ferror = ARGV_ERROR_OPTARG_REQUIRED;
 				else if (val[0] && val[1]) {
 					fval = true;
@@ -447,6 +519,30 @@ static void argv_show_error(struct argv_ctx * ctx)
 				ctx->erropt->paradigm);
 			break;
 
+		case ARGV_ERROR_HYBRID_NONE:
+			fprintf(stderr,"-%s is not a synonym for --%s\n",
+				ctx->erropt->long_name,
+				ctx->erropt->long_name);
+			break;
+
+		case ARGV_ERROR_HYBRID_ONLY:
+			fprintf(stderr,"--%s is not a synonym for -%s\n",
+				ctx->erropt->long_name,
+				ctx->erropt->long_name);
+			break;
+
+		case ARGV_ERROR_HYBRID_SPACE:
+			fprintf(stderr,"-%s: illegal value assignment; valid syntax is -%s=VAL\n",
+				ctx->erropt->long_name,
+				ctx->erropt->long_name);
+			break;
+
+		case ARGV_ERROR_HYBRID_EQUAL:
+			fprintf(stderr,"-%s: illegal value assignment; valid syntax is -%s VAL\n",
+				ctx->erropt->long_name,
+				ctx->erropt->long_name);
+			break;
+
 		case ARGV_ERROR_INTERNAL:
 			fputs("internal error",stderr);
 			break;
diff --git a/src/skin/sfrt_skin_default.c b/src/skin/sfrt_skin_default.c
index 875fd54..24e5d22 100644
--- a/src/skin/sfrt_skin_default.c
+++ b/src/skin/sfrt_skin_default.c
@@ -2,16 +2,16 @@
 #include "argv/argv.h"
 
 const struct argv_option sfrt_default_options[] = {
-	{"version",		'v',TAG_VERSION,ARGV_OPTARG_NONE,0,0,
+	{"version",		'v',TAG_VERSION,ARGV_OPTARG_NONE,0,0,0,
 				"show version information"},
 
-	{"help",		'h',TAG_HELP,ARGV_OPTARG_OPTIONAL,"short|long",0,
+	{"help",		'h',TAG_HELP,ARGV_OPTARG_OPTIONAL,0,"short|long",0,
 				"show usage information [listing %s options only]"},
 
-	{"output-dummy",	'o',TAG_OUTPUT_DUMMY,ARGV_OPTARG_REQUIRED,0,"<anystring>",	/* dummy */
+	{"output-dummy",	'o',TAG_OUTPUT_DUMMY,ARGV_OPTARG_REQUIRED,0,0,"<anystring>",	/* dummy */
 				"output %s"},							/* dummy */
 												/* dummy */
-	{"output-property",	'p',TAG_OUTPUT_PROPERTY,ARGV_OPTARG_REQUIRED,"name|address",0,	/* dummy */
+	{"output-property",	'p',TAG_OUTPUT_PROPERTY,ARGV_OPTARG_REQUIRED,0,"name|address",0,/* dummy */
 				"output %s"},							/* dummy */
 												/* dummy */
 	{0}
-- 
cgit v1.2.3