diff --git a/Makefile b/Makefile
index deab489..8da31c3 100644
--- a/Makefile
+++ b/Makefile
@@ -96 +96 @@ cflags = -fno-strict-aliasing
-cflags += -Wall -Wwrite-strings
+cflags += -Wall -Wwrite-strings -Wno-missing-braces

diff --git a/lib.h b/lib.h
index 3e565c6..0aff45e 100644
--- a/lib.h
+++ b/lib.h
@@ -198 +198 @@ extern int dbg_postorder;
-extern unsigned int fmax_warnings;
+extern unsigned fmax_warnings;
@@ -200 +200 @@ extern int fmem_report;
-extern unsigned long fdump_ir;
+extern unsigned fdump_ir;
@@ -202 +202 @@ extern unsigned long long fmemcpy_max_count;
-extern unsigned long fpasses;
+extern unsigned fpasses;
@@ -248,0 +249,10 @@ extern void report_stats(void);
+extern int sparse_add_switch(int action, const char *name, ...);
+
+#define OPT_INVERSE 0x0001
+#define OPT_VAL     0x0002
+#define OPT_MASK    0x0004
+#define OPT_NO_SEP  0x0008
+#define OPT_INEXT   0x0010
+#define OPT_INLINE  0x0020
+#define OPT_NO_RET  0x0040
+#define OPT_INPUT   (OPT_INLINE | OPT_INEXT)

diff --git a/lib.c b/lib.c
index 60a6941..4e400b2 100644
--- a/lib.c
+++ b/lib.c
@@ -5,0 +6 @@
+ *               2020 Xan Phung
@@ -33,0 +35 @@
+#include <inttypes.h>
@@ -310 +312 @@ int dbg_postorder = 0;
-unsigned long fdump_ir;
+unsigned fdump_ir;
@@ -313 +315 @@ unsigned long long fmemcpy_max_count = 100000;
-unsigned long fpasses = ~0UL;
+unsigned fpasses = ~0U;
@@ -366,2 +367,0 @@ static const char *match_option(const char *arg,
const char *prefix)
-#define OPT_INVERSE    1
-#define OPT_VAL                2
@@ -370,4 +370,10 @@ struct flag {
-       int *flag;
-       int (*fun)(const char *arg, const char *opt, const struct flag
*, int options);
-       unsigned long mask;
-       int val;
+       union {
+               int *flag;
+               long long *long_flag;
+               const char **string;
+       };
+       uintptr_t mask;
+       const char *in_fmt;
+       const char *out_fmt;
+       void (*out_fn)(const char *,...);
+       int action;
@@ -382 +388 @@ enum {
-static void add_cmdline_include(char *filename)
+static void add_cmdline_include(const char *fmt, ...)
@@ -383,0 +390,4 @@ static void add_cmdline_include(char *filename)
+       va_list args;
+       va_start(args, fmt);
+       char *filename = va_arg(args, char *);
+       va_end(args);
@@ -419 +429 @@ static void handle_arch_finalize(void)
-static const struct flag warnings[] = {
+static const struct flag warnings[80] = {
@@ -457 +467 @@ static const struct flag warnings[] = {
-       { "sparse-error", &Wsparse_error },
+       { "sparse-error", &Wsparse_error, .mask=1, .action=OPT_MASK },
@@ -467 +477 @@ static const struct flag warnings[] = {
-static void handle_onoff_switch_finalize(const struct flag warnings[], int n)
+static void handle_switch_finalize(const struct flag warnings[])
@@ -471,3 +481,3 @@ static void handle_onoff_switch_finalize(const
struct flag warnings[], int n)
-       for (i = 0; i < n; i++) {
-               if (*warnings[i].flag == WARNING_FORCE_OFF)
-                       *warnings[i].flag = WARNING_OFF;
+       for (i = 0; warnings[i].flag; i++) {
+               if (!warnings[i].action && *warnings[i].flag &
WARNING_FORCE_OFF)
+                       *warnings[i].flag &= ~WARNING_FORCE_OFF;
@@ -479 +489 @@ static void handle_switch_W_finalize(void)
-       handle_onoff_switch_finalize(warnings, ARRAY_SIZE(warnings));
+       handle_switch_finalize(warnings);
@@ -482 +492 @@ static void handle_switch_W_finalize(void)
-       if (-1 == Wdeclarationafterstatement) {
+       if (Wdeclarationafterstatement < 0) {




(ii) **************** New lines: *****************
The diff below is a direct continuation of the diff above, and adds
the new switch parsing functions and flag array discussed.



@@ -494,0 +505,367 @@ static void handle_switch_W_finalize(void)
+static void set_arch(const char *fmt, ...)
+{
+       unsigned bits = 0, mach = 0, min = 0, big = 0;
+       va_list args;
+       va_start(args, fmt);
+       bits = va_arg(args, int);
+       mach = va_arg(args, int);
+       va_end(args);
+       assert(fmt && sscanf(fmt, "#%*s (%*[^)]) m=%2d,%1d", &min, &big) == 2);
+       assert(big <= 1 && min % 32 == 0);
+       if (bits & ~63) {
+               arch_mach = mach | 1;
+               arch_m64  = (arch_m64 == ARCH_LP32) ? ARCH_LP64 : arch_m64;
+       } else {
+               arch_mach = mach;
+               arch_m64  = ARCH_LP32;
+       }
+       arch_big_endian = (big == '1') ? 1 : 0;
+}
+
+typedef void (*out_fn)(const char *, ...);
+static  char opt_grp[sizeof(long long)];  //Placeholder for unused
input which is parsed & discarded;
+
+#define FLAGS_SIZE             40
+#define BEGIN_flag(a)          [a] = (struct flag[FLAGS_SIZE]) {
+#define END_flag               {NULL} }
+
+#define FLAG_DATA(n,p,m,i,o,a) { n+2, (void *)&(p), (uintptr_t)m,
i"%7$n", "#%3$s "#p" "o"\n", NULL, OPT_MASK + a }
+#define FLAG_STRG(n,p,a)       FLAG_DATA((n), (p), 0,   "%3$n",
"strg=%1$s",       OPT_VAL+(a))
+#define FLAG_IVAL(n,p,i)       FLAG_DATA((n), (p), 0,   i,
"ival=%1$"PRIdPTR, OPT_VAL+OPT_INLINE )
+#define FLAG_ENUM(n,p,m)       FLAG_DATA((n), (p), (m), "\0",
"bset=%1$"PRIdPTR,-OPT_MASK+(*(n)-'@')&0x7)
+#define FLAG_LNUM(n,p,m)       FLAG_DATA((n), (p), (m),
"%1$lli","lnum=%1$"PRIdPTR, OPT_VAL)
+#define FLAG_OGRP(n,p)         FLAG_DATA((n),
(p),*(n)=='*',"\0","true=%1$"PRIdPTR, OPT_VAL+OPT_NO_SEP)
+
+#define FLAG_PRNT(n,f,m,i,o,a) { n+2, (void *)&opt_grp,
(uintptr_t)(m), (i), o"\n", f, OPT_MASK | OPT_VAL | a}
+#define FLAG_ERRM(n,m,f)       { n+2, (void *)&opt_grp, 0, NULL, (m),
(f)>>2 ? &die : (out_fn)&printf, OPT_INLINE|OPT_NO_RET }
+#define FLAG_INCL(n,a)         FLAG_PRNT((n), &add_cmdline_include,
0, 0, "#%3$s (cmdline_include) path=%1$s", (a))
+#define FLAG_PDEF(n,a,o)       FLAG_PRNT((n), &add_pre_buffer,"1",
"%3$n%7$n%*[^=]=%4$n%5$n", o, (a))
+#define FLAG_ARCH(n,f,m)       FLAG_PRNT((n), &set_arch,      (m),
"%7$n%1$2u%7$n", "#%3$s (arch) m="f",%1$d,%2$d", OPT_INLINE)
+
+static struct flag *flags[] = {
+       BEGIN_flag('D')
+               FLAG_OGRP("~D", opt_grp),
+               FLAG_PDEF("~D#", OPT_INPUT, "#define %s %s"),
+       END_flag,
+       BEGIN_flag('U')
+               FLAG_OGRP("~U", opt_grp),
+               FLAG_PDEF("~U#", OPT_INPUT, "#undef %s"),
+       END_flag,
+       BEGIN_flag('d')
+               FLAG_OGRP("~d", opt_grp),
+               FLAG_OGRP(". #M", dump_macro_defs),
+               FLAG_OGRP("* #D", dump_macro_defs),
+               FLAG_OGRP("* #N", dump_macros_only),
+               FLAG_OGRP("* #I", dump_macros_only),
+               FLAG_OGRP("* #U", dump_macros_only),
+               FLAG_OGRP("~d", opt_grp),
+               FLAG_OGRP("* #M", dump_macros_only),
+               FLAG_OGRP(". #D", dump_macros_only),
+               FLAG_OGRP(". #N", dump_macros_only),
+               FLAG_OGRP(". #I", dump_macros_only),
+               FLAG_OGRP(". #U", dump_macros_only),
+       END_flag,
+       BEGIN_flag('E')
+               FLAG_ENUM("~E", preprocess_only, 1),
+       END_flag,
+       BEGIN_flag('f')
+               FLAG_ENUM("~fmem-report", fmem_report, 1),
+               FLAG_ENUM("~funsigned-char",funsigned_char, 1),
+               FLAG_ENUM("-fsigned-char", funsigned_char, 1),
+               FLAG_ENUM("~fshort-wchar", fshort_wchar, 1),
+               FLAG_ENUM("~fpic", fpic, 1),
+               FLAG_ENUM("~fPIC", fpic, 2),
+               FLAG_ENUM("~fpie", fpie, 1),
+               FLAG_ENUM("~fPIE", fpie, 2),
+               FLAG_OGRP("~fmemcpy-max-count=", opt_grp),
+               FLAG_LNUM(". #unlimited", fmemcpy_max_count, "-1"),
+               FLAG_LNUM(". #0", fmemcpy_max_count, "-1"),
+               FLAG_IVAL(". #", fmemcpy_max_count, "%1$lld"),
+               FLAG_OGRP("~fmax-warnings=", opt_grp),
+               FLAG_ENUM(". #unlimited", fmax_warnings, ~0),
+               FLAG_IVAL(". #", fmax_warnings, "%1$d"),
+               FLAG_ERRM("~ftabstop=0", "error: wrong argument to '%3$s'", 6),
+               FLAG_IVAL("~ftabstop=", tabstop, "%1$2u"),
+               FLAG_DATA("~fdiagnostic-prefix", diag_prefix, "sparse:
", "\0", "strg=%1$s", OPT_VAL),
+               FLAG_STRG(". #", diag_prefix, OPT_INLINE),
+               FLAG_ENUM("~fdump-ir", fdump_ir, PASS_LINEARIZE),
+               FLAG_ENUM(", #linearize", fdump_ir, PASS_LINEARIZE),
+               FLAG_ENUM(", #mem2reg", fdump_ir, PASS_MEM2REG),
+               FLAG_ENUM(", #final", fdump_ir,  PASS_FINAL),
+               FLAG_ENUM("$flinearize",fpasses, PASS_LINEARIZE),
+               FLAG_ENUM("- #last", fpasses, ~(PASS_LINEARIZE |
PASS_LINEARIZE-1)),
+               FLAG_ENUM("$fmem2reg", fpasses, PASS_MEM2REG),
+               FLAG_ENUM("- #last", fpasses, ~(PASS_MEM2REG | PASS_MEM2REG-1)),
+               FLAG_ENUM("$foptim", fpasses,   PASS_OPTIM),
+               FLAG_ENUM("- #last", fpasses, ~(PASS_OPTIM | PASS_OPTIM-1)),
+       END_flag,
+       BEGIN_flag('g')
+               FLAG_STRG("~gcc-base-dir", gcc_base_dir, OPT_INEXT),
+       END_flag,
+       BEGIN_flag('G')
+               FLAG_OGRP("~G", opt_grp),
+               FLAG_STRG("~G#", opt_grp, OPT_INPUT),
+       END_flag,
+       BEGIN_flag('I')
+               FLAG_OGRP("~I", opt_grp),
+               FLAG_PDEF("~I#-", OPT_NO_SEP, "#split_include"),
+               FLAG_PDEF("~I#", OPT_NO_SEP|OPT_INPUT, "#add_include \"%s/\""),
+       END_flag,
+       BEGIN_flag('i')
+               FLAG_INCL("~include",   OPT_INPUT),
+               FLAG_INCL("~imacro",    OPT_INPUT),
+               FLAG_PDEF("~isystem",   OPT_INPUT, "#add_isystem \"%s/\""),
+               FLAG_PDEF("~idirafter", OPT_INPUT, "#add_dirafter \"%s/\""),
+       END_flag,
+       BEGIN_flag('m')
+               FLAG_ENUM("~m16", arch_m64, ARCH_LP32),
+               FLAG_ENUM("~m31", arch_m64, ARCH_LP32),
+               FLAG_ENUM("~m32", arch_m64, ARCH_LP32),
+               FLAG_ENUM("~mx32", arch_m64, ARCH_X32),
+               FLAG_ENUM("~m64", arch_m64, ARCH_LP64),
+               FLAG_ENUM("~msize-llp64", arch_m64, ARCH_LLP64),
+               FLAG_ENUM("~msize-long", arch_msize_long, 1),
+               FLAG_ENUM("~mbig-endian", arch_big_endian, 1),
+               FLAG_ENUM("-mlittle-endian",arch_big_endian, 1),
+               FLAG_STRG("~multiarch-dir", multiarch_dir, OPT_INEXT),
+               FLAG_OGRP("~mcmodel", opt_grp),
+               FLAG_ENUM(". #kernel",  arch_cmodel, CMODEL_KERNEL),
+               FLAG_ENUM(". #large",   arch_cmodel, CMODEL_LARGE),
+               FLAG_ENUM(". #medany",  arch_cmodel, CMODEL_MEDANY),
+               FLAG_ENUM(". #medium",  arch_cmodel, CMODEL_MEDIUM),
+               FLAG_ENUM(". #medlow",  arch_cmodel, CMODEL_MEDLOW),
+               FLAG_ENUM(". #small",   arch_cmodel, CMODEL_SMALL),
+               FLAG_ENUM(". #tiny",      arch_cmodel, CMODEL_TINY),
+               FLAG_OGRP("~mfloat-abi", opt_grp),
+               FLAG_ENUM(". #hard", arch_fp_abi, FP_ABI_HARD),
+               FLAG_ENUM(". #soft", arch_fp_abi, FP_ABI_SOFT),
+               FLAG_ENUM(". #softfp", arch_fp_abi, FP_ABI_HYBRID),
+               FLAG_ENUM("~mhard-float", arch_fp_abi, FP_ABI_HARD),
+               FLAG_ENUM("~msoft-float", arch_fp_abi, FP_ABI_SOFT),
+       END_flag,
+       BEGIN_flag('M')
+               FLAG_STRG("~MF", opt_grp, OPT_INEXT),
+               FLAG_STRG("~MQ", opt_grp, OPT_INEXT),
+               FLAG_STRG("~MT", opt_grp, OPT_INEXT),
+       END_flag,
+       BEGIN_flag('n')
+               FLAG_PDEF("~nostdinc", 0, "#nostdinc"),
+       END_flag,
+       BEGIN_flag('o')
+               FLAG_STRG("~o", outfile, OPT_INPUT),
+       END_flag,
+       BEGIN_flag('O')
+               FLAG_OGRP("~O", opt_grp),
+               FLAG_ENUM(". #s", optimize_size, 1),
+               FLAG_IVAL(". #",  optimize_size,  "%7$n1%1$n"), //
writes one into flag if O1 or zero otherwise
+               FLAG_OGRP("~O", opt_grp),
+               FLAG_ENUM(". #s", optimize_level, 1),
+               FLAG_IVAL(". #",  optimize_level, "%1$1u"),
+       END_flag,
+       BEGIN_flag('a')
+               FLAG_ENUM("~ansi", standard, STANDARD_C89),
+       END_flag,
+       BEGIN_flag('s')
+               FLAG_OGRP("~std=", opt_grp),
+               FLAG_ENUM(". #c89", standard, STANDARD_C89),
+               FLAG_ENUM(". #iso9899:1990", standard, STANDARD_C89),
+               FLAG_ENUM(". #c94", standard, STANDARD_C94),
+               FLAG_ENUM(". #iso9899:199409",standard, STANDARD_C94),
+               FLAG_ENUM(". #c99", standard, STANDARD_C99),
+               FLAG_ENUM(". #c9x", standard, STANDARD_C99),
+               FLAG_ENUM(". #iso9899:1999", standard, STANDARD_C99),
+               FLAG_ENUM(". #iso9899:199x", standard, STANDARD_C99),
+               FLAG_ENUM(". #gnu89", standard, STANDARD_GNU89),
+               FLAG_ENUM(". #gnu99", standard, STANDARD_GNU99),
+               FLAG_ENUM(". #gnu9x", standard, STANDARD_GNU99),
+               FLAG_ENUM(". #c11", standard, STANDARD_C11),
+               FLAG_ENUM(". #c1x", standard, STANDARD_C11),
+               FLAG_ENUM(". #iso9899:2011", standard, STANDARD_C11),
+               FLAG_ENUM(". #gnu11", standard, STANDARD_GNU11),
+               FLAG_ENUM(". #c17", standard, STANDARD_C17),
+               FLAG_ENUM(". #c18", standard, STANDARD_C17),
+               FLAG_ENUM(". #iso9899:2017", standard, STANDARD_C17),
+               FLAG_ENUM(". #iso9899:2018", standard, STANDARD_C17),
+               FLAG_ENUM(". #gnu17", standard, STANDARD_GNU17),
+               FLAG_ENUM(". #gnu18", standard, STANDARD_GNU17),
+               FLAG_ERRM(". #", "Unsupported C dialect", 6),
+       END_flag,
+       BEGIN_flag('v')
+               FLAG_OGRP("~vvv", opt_grp),
+               FLAG_DATA(". #v", verbose, "4", "\0", "ival=%1$d",
OPT_VAL+OPT_NO_SEP+OPT_INLINE),
+               FLAG_ENUM(". #", verbose, 3),
+               FLAG_ENUM("~vv", verbose, 2),
+               FLAG_ENUM("~v", verbose, 1),
+               FLAG_ENUM("@vcompound", dbg_compound, 0),
+               FLAG_ENUM("@vdomtree", dbg_domtree, 0),
+               FLAG_ENUM("@ventry", dbg_entry, 0),
+               FLAG_ENUM("@vir", dbg_ir, 0),
+               FLAG_ENUM("@vpostorder", dbg_postorder, 0),
+       END_flag,
+       BEGIN_flag('x')
+               FLAG_STRG("~x", opt_grp, OPT_INEXT),
+       END_flag,
+       ['W'] = (struct flag *)warnings,
+       BEGIN_flag('-')
+               FLAG_ERRM("~~version", SPARSE_VERSION, 10),
+               FLAG_STRG("~~param", opt_grp, OPT_INEXT),
+               FLAG_STRG(". #", opt_grp, OPT_INPUT),
+               FLAG_OGRP("~~arch", opt_grp),
+               FLAG_ARCH(". #=aarch64","64,0", MACH_ARM64),
+               FLAG_ARCH(". #=arm",    "32,0", MACH_ARM),
+               FLAG_ARCH(". #=i386",   "32,0", MACH_I386),
+               FLAG_ARCH(". #=m68k",   "32,0", MACH_M68K),
+               FLAG_ARCH(". #=mips",   "32,1", MACH_MIPS32),
+               FLAG_ARCH(". #=powerpc","32,1", MACH_PPC32),
+               FLAG_ARCH(". #=ppc",    "32,1", MACH_PPC32),
+               FLAG_ARCH(". #=riscv",  "32,0", MACH_RISCV32),
+               FLAG_ARCH(". #=s390x",  "64,1", MACH_S390X),
+               FLAG_ARCH(". #=s390",   "32,1", MACH_S390),
+               FLAG_ARCH(". #=sparc",  "32,1", MACH_SPARC32),
+               FLAG_ARCH(". #=x86_64", "64,0", MACH_X86_64),
+               FLAG_ARCH(". #=x86-64", "64,0", MACH_X86_64),
+               FLAG_ERRM(". #=", "invalid architecture: '%3$s'", 6),
+               FLAG_ERRM(". #", "missing argument for --arch option", 6),
+       END_flag,
+};
+
+static char **match_params(char *arg, char **args, const struct flag
*current, int neg)
+{
+       union { long long i; char *s; } val0 = {0}, val1 = {0}, val2 = {0};
+       const unsigned action = current->action;
+       char *ori = *args;
+
+       /* setup & mask val1 & val2 inputs (!OPT_MASK=set dirty bit,
OPT_VAL=zero val1) */
+       neg = (neg != 0) ^ (action & OPT_INVERSE);
+       val2.i = (action & OPT_MASK) ? current->mask :
WARNING_ON|WARNING_FORCE_OFF;
+       if (action & OPT_INPUT) {
+               if ((action & OPT_INLINE) && (*arg || ~action &
OPT_INEXT)) val1.s = arg;
+               else val1.s = *++args;
+       } else {
+               val1.i = (action & OPT_VAL) ? 0 : *current->flag;
+               val1.i = neg ? val1.i & ~val2.i : val1.i | val2.i;
+               *current->flag = (int)val1.i | (~action & OPT_MASK) >> 1;
+       }
+
+       /* scanf of val1: can accept int, %l, %ll, or strings (use
%3$n% and don't use %s) */
+       /* (%ll cannot assume zero init & need separate check, eg. see
-fmemcpy_max_count)  */
+       if (current->in_fmt && *current->in_fmt) {
+               int len0 = -1, len1 = -1, len2 = -1, len3 = -1, end = -1;
+               sscanf(val0.s = val1.s, current->in_fmt,
current->flag, &val2.i, &len0, &len1, &len2, &len3, &end);
+               sscanf(val0.s, current->in_fmt, &val1.i, &val2.i,
&len0, &len1, &len2, &len3, &end);
+               if (end  == 0) val1.i = *current->flag = 0;
+               else if (end  == -1 || val0.s[end] != 0) die("error:
wrong argument to \'%s\'", ori);
+               if (len0 >= 0) {
+                       *current->string = val1.s = val0.s + len0;
+                       if (len1 >= 0) val0.s[len1-1] = 0;
+               }
+               if (len2 >= 0) {
+                       val2.s = val0.s + len2;
+                       if (len3 >= 0) val0.s[len3-1] = 0;
+               }
+       }
+
+       /* printf of val1&2: print strings or int of size intptr_t
(use PRIdPTR)  */
+       if (verbose >= 2 && current->out_fmt) printf(current->out_fmt,
val1.s, val2.s, ori, arg, *args);
+       if (current->out_fn) (current->out_fn)(current->out_fmt,
val1.s, val2.s, ori);
+       if (action & OPT_NO_RET) exit(1); else return args;
+}
+
+#define peek(p,c,o) (current->action & o || *p == c)
+static char **match_suboption(char *opt, char **args, const struct
flag *parent, int neg)
+{
+       for (const struct flag *current = parent; ; opt += (*opt ==',')) {
+               char **next_args, *next_opt = opt;
+               if (!neg && !strncmp(opt, "no-", 3)) opt += neg = 3;
+
+               do if (!(++current)->name || *current->name != '#')
+                       die("wrong option '%.*s' for \'%s\'",
(int)strcspn(opt, ","), opt, *args);
+               while (!(next_opt = (char *)match_option(opt,
current->name+1)));
+
+               /* if any OPT_INPUT bit is set then remainder of opt
assumed processed by match_param */
+               next_args = match_params(next_opt, args, current, neg);
+               if (peek(next_opt, '\0', OPT_INPUT)) return next_args;
+               if (peek(next_opt, ',', OPT_NO_SEP)) (current =
parent) && (opt = next_opt);
+       }
+}
+
+static char **handle_switch(char *arg, char **args, const struct flag *current)
+{
+       char **current_args = args, **next_args = args;
+       int neg = !strncmp(arg += 1, "no-", 3) ? 3 : (!strncmp(arg-1,
"Wno", 3) ? 2 : 0);
+       int all =  !strcmp(arg += neg, "sparse-all");
+
+       if (current) for (; current->name; current++) {
+               char *opt = (char *)match_option(arg, current->name),
*next_opt = opt;
+               if (all && !current->action && !(*current->flag & ~1))
*current->flag = neg ? 0 : 1;
+               if (!opt) continue;
+
+               if (peek(opt++, '=', OPT_NO_SEP)) next_args =
match_suboption(opt, args, current, neg);
+               else if (peek(next_opt, '\0', OPT_INPUT)) next_args =
match_params(next_opt, args, current, neg);
+               if (!*next_args) die("missing argument for %s option", arg-2);
+               if (current_args < next_args) current_args = next_args;
+       }
+       return current_args;
+}
+
+int sparse_add_switch(int action, const char *name, ...)
+{
+       static struct flag *switches['z'];
+       static short sizes['z'] = { ['W'] = ARRAY_SIZE(warnings)-FLAGS_SIZE };
+       const char *ori = name;
+       int c;
+       struct flag opt = {};
+       va_list args;
+
+       va_start(args, name);
+       for (c = *name++; c == '%'; c= *name++) {
+               switch (c = *name++) {
+               case 'p':
+                       opt.flag = va_arg(args, void *);
+                       break;
+               case 'L':
+                       opt.mask = (uintptr_t) va_arg(args, long long);
+                       break;
+               case 'i':
+                       case 'd': opt.mask = va_arg(args, int);
+                       break;
+               case 's':
+                       opt.mask  = (uintptr_t) va_arg(args, char *);
+                       break;
+               case '<':
+                       opt.in_fmt  = va_arg(args, char *);
+                       break;
+               case '>':
+                       opt.out_fmt = va_arg(args, char *);
+                       break;
+               case '^':
+                       opt.out_fn = va_arg(args, void (*)(const char *,...));
+                       break;
+               default:
+                       die("unknown format %%'%c' in '%s'", c, ori);
+               }
+       }
+       va_end(args);
+
+       c = *name++;
+       opt.name = name;
+       opt.action = action;
+       if (!switches[c]) {
+               if (!flags[c]) {
+                       int size = FLAGS_SIZE*sizeof(struct flag);
+                       flags[c] = (struct flag *) malloc(size);
+                       memset(flags[c], 0, size);
+               }
+               switches[c] = flags[c];
+       }
+
+       /* never use last entry to ensure NULL guard entry*/
+       for (; --sizes[c]+FLAGS_SIZE > 1; ++switches[c]) {
+               if (!(switches[c])->name) {
+                       *switches[c] = opt;
+                       return sizes[c]+FLAGS_SIZE-1;
+               }
+       }
+       die("flags registry overflowed whilst adding entry %s", name-1);
+}
+
@@ -970 +1347 @@ struct symbol_list *sparse_initialize(int argc, char
**argv, struct string_list
-                       args = handle_switch(arg+1, args);
+                       args = handle_switch(arg+1, args, flags[(int)arg[1]]);
@@ -976 +1353 @@ struct symbol_list *sparse_initialize(int argc, char
**argv, struct string_list
-       handle_switch_v_finalize();
+       handle_switch_finalize(flags['v']);





(iii) ************* Deleted lines **************
* Approx 800 lines of code is deleted, as per patch below.  All
deleted code and functions are static scope (so there is no impact
outside of lib.c itself).  (The patch below should be applied first to
delete code, before the patch above is applied to insert new code).


diff --git a/lib.c b/lib.c
index 711e8fb..60a6941 100644
--- a/lib.c
+++ b/lib.c
@@ -358,74 +357,0 @@ void add_pre_buffer(const char *fmt, ...)
-struct val_map {
-       const char *name;
-       int val;
-};
-
-static int handle_subopt_val(const char *opt, const char *arg, const
struct val_map *map, int *flag)
-{
-       const char *name;
-
-       if (*arg++ != '=')
-               die("missing argument for option '%s'", opt);
-       for (;(name = map->name); map++) {
-               if (strcmp(name, arg) == 0 || strcmp(name, "*") == 0) {
-                       *flag = map->val;
-                       return 1;
-               }
-               if (strcmp(name, "?") == 0)
-                       die("invalid argument '%s' in option '%s'", arg, opt);
-       }
-       return 0;
-}
-
-
-struct mask_map {
-       const char *name;
-       unsigned long mask;
-};
-
-static int apply_mask(unsigned long *val, const char *str, unsigned
len, const struct mask_map *map, int neg)
-{
-       const char *name;
-
-       for (;(name = map->name); map++) {
-               if (!strncmp(name, str, len) && !name[len]) {
-                       if (neg == 0)
-                               *val |= map->mask;
-                       else
-                               *val &= ~map->mask;
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-static int handle_suboption_mask(const char *arg, const char *opt,
const struct mask_map *map, unsigned long *flag)
-{
-       if (*opt == '\0') {
-               apply_mask(flag, "", 0, map, 0);
-               return 1;
-       }
-       if (*opt++ != '=')
-               return 0;
-       while (1) {
-               unsigned int len = strcspn(opt, ",+");
-               int neg = 0;
-               if (len == 0)
-                       goto end;
-               if (!strncmp(opt, "no-", 3)) {
-                       opt += 3;
-                       len -= 3;
-                       neg = 1;
-               }
-               if (apply_mask(flag, opt, len, map, neg))
-                       die("error: wrong option '%.*s' for \'%s\'",
len, opt, arg);
-
-end:
-               opt += len;
-               if (*opt++ == '\0')
-                       break;
-       }
