about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2002-08-27 21:10:30 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2002-08-27 21:10:30 +0000
commit9634760d5eae4e8618e4b9ed9752d7305b3695a9 (patch)
treefc717bec9a623d6e80f2c4544cec14b8b8eb07da /Src
parent043c302261dfee52e54e9a6c42b4ebcc2f7ccd33 (diff)
downloadzsh-9634760d5eae4e8618e4b9ed9752d7305b3695a9.tar.gz
zsh-9634760d5eae4e8618e4b9ed9752d7305b3695a9.tar.xz
zsh-9634760d5eae4e8618e4b9ed9752d7305b3695a9.zip
17582: Improved option argument handling.
unposted: Updated version to 4.1.0-dev-6 because of interface change.
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.c18
-rw-r--r--Src/Builtins/sched.c26
-rw-r--r--Src/Modules/cap.c37
-rw-r--r--Src/Modules/clone.c2
-rw-r--r--Src/Modules/datetime.c2
-rw-r--r--Src/Modules/example.c174
-rw-r--r--Src/Modules/files.c40
-rw-r--r--Src/Modules/pcre.c16
-rw-r--r--Src/Modules/socket.c12
-rw-r--r--Src/Modules/stat.c56
-rw-r--r--Src/Modules/tcp.c18
-rw-r--r--Src/Modules/termcap.c2
-rw-r--r--Src/Modules/terminfo.c2
-rw-r--r--Src/Modules/zftp.c2
-rw-r--r--Src/Modules/zprof.c4
-rw-r--r--Src/Modules/zpty.c39
-rw-r--r--Src/Modules/zselect.c2
-rw-r--r--Src/Modules/zutil.c10
-rw-r--r--Src/Zle/compctl.c34
-rw-r--r--Src/Zle/complete.c4
-rw-r--r--Src/Zle/computil.c21
-rw-r--r--Src/Zle/zle_keymap.c51
-rw-r--r--Src/Zle/zle_main.c14
-rw-r--r--Src/Zle/zle_thingy.c42
-rw-r--r--Src/builtin.c726
-rw-r--r--Src/hashtable.c2
-rw-r--r--Src/hashtable.h19
-rw-r--r--Src/init.c2
-rw-r--r--Src/jobs.c23
-rw-r--r--Src/mem.c16
-rw-r--r--Src/module.c113
-rw-r--r--Src/options.c2
-rw-r--r--Src/parse.c38
-rw-r--r--Src/watch.c4
-rw-r--r--Src/zsh.h74
35 files changed, 997 insertions, 650 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 7d20ab07b..228bba344 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -283,15 +283,15 @@ printulimit(int lim, int hard, int head)
 
 /**/
 static int
-bin_limit(char *nam, char **argv, char *ops, int func)
+bin_limit(char *nam, char **argv, Options ops, int func)
 {
     char *s;
     int hard, limnum, lim;
     rlim_t val;
     int ret = 0;
 
-    hard = ops['h'];
-    if (ops['s'] && !*argv)
+    hard = OPT_ISSET(ops,'h');
+    if (OPT_ISSET(ops,'s') && !*argv)
 	return setlimits(NULL);
     /* without arguments, display limits */
     if (!*argv) {
@@ -380,7 +380,7 @@ bin_limit(char *nam, char **argv, char *ops, int func)
 	    return 1;
 	} else
 	    limits[lim].rlim_cur = val;
-	if (ops['s'] && zsetlimit(lim, "limit"))
+	if (OPT_ISSET(ops,'s') && zsetlimit(lim, "limit"))
 	    ret++;
     }
     return ret;
@@ -391,13 +391,13 @@ bin_limit(char *nam, char **argv, char *ops, int func)
 
 /**/
 static int
-bin_unlimit(char *nam, char **argv, char *ops, int func)
+bin_unlimit(char *nam, char **argv, Options ops, int func)
 {
     int hard, limnum, lim;
     int ret = 0;
     uid_t euid = geteuid();
 
-    hard = ops['h'];
+    hard = OPT_ISSET(ops,'h');
     /* Without arguments, remove all limits. */
     if (!*argv) {
 	for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) {
@@ -409,7 +409,7 @@ bin_unlimit(char *nam, char **argv, char *ops, int func)
 	    } else
 		limits[limnum].rlim_cur = limits[limnum].rlim_max;
 	}
-	if (ops['s'])
+	if (OPT_ISSET(ops,'s'))
 	    ret += setlimits(nam);
 	if (ret)
 	    zwarnnam(nam, "can't remove hard limits", NULL, 0);
@@ -443,7 +443,7 @@ bin_unlimit(char *nam, char **argv, char *ops, int func)
 		    limits[lim].rlim_max = RLIM_INFINITY;
 	    } else
 		limits[lim].rlim_cur = limits[lim].rlim_max;
-	    if (ops['s'] && zsetlimit(lim, nam))
+	    if (OPT_ISSET(ops,'s') && zsetlimit(lim, nam))
 		ret++;
 	}
     }
@@ -454,7 +454,7 @@ bin_unlimit(char *nam, char **argv, char *ops, int func)
 
 /**/
 static int
-bin_ulimit(char *name, char **argv, char *ops, int func)
+bin_ulimit(char *name, char **argv, Options ops, int func)
 {
     int res, resmask = 0, hard = 0, soft = 0, nres = 0;
     char *options;
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index b4914899e..05e3cfb3f 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -46,7 +46,7 @@ static struct schedcmd *schedcmds;
 
 /**/
 static int
-bin_sched(char *nam, char **argv, char *ops, int func)
+bin_sched(char *nam, char **argv, Options ops, int func)
 {
     char *s = *argv++;
     time_t t;
@@ -143,9 +143,7 @@ bin_sched(char *nam, char **argv, char *ops, int func)
     of scheduled commands. */
     sch = (struct schedcmd *) zcalloc(sizeof *sch);
     sch->time = t;
-    PERMALLOC {
-	sch->cmd = zjoin(argv, ' ');
-    } LASTALLOC;
+    sch->cmd = zjoin(argv, ' ', 0);
     sch->next = NULL;
     for (sch2 = (struct schedcmd *)&schedcmds; sch2->next; sch2 = sch2->next);
     sch2->next = sch;
@@ -185,7 +183,14 @@ static struct builtin bintab[] = {
 
 /**/
 int
-boot_sched(Module m)
+setup_(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_(Module m)
 {
     if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
 	return 1;
@@ -193,11 +198,9 @@ boot_sched(Module m)
     return 0;
 }
 
-#ifdef MODULE
-
 /**/
 int
-cleanup_sched(Module m)
+cleanup_(Module m)
 {
     struct schedcmd *sch, *schn;
 
@@ -211,4 +214,9 @@ cleanup_sched(Module m)
     return 0;
 }
 
-#endif
+/**/
+int
+finish_(Module m)
+{
+    return 0;
+}
diff --git a/Src/Modules/cap.c b/Src/Modules/cap.c
index 008b6932d..ba377f876 100644
--- a/Src/Modules/cap.c
+++ b/Src/Modules/cap.c
@@ -33,7 +33,7 @@
 #ifdef HAVE_CAP_GET_PROC
 
 static int
-bin_cap(char *nam, char **argv, char *ops, int func)
+bin_cap(char *nam, char **argv, Options ops, int func)
 {
     int ret = 0;
     cap_t caps;
@@ -48,7 +48,7 @@ bin_cap(char *nam, char **argv, char *ops, int func)
 	    ret = 1;
 	}
     } else {
-	char *result;
+	char *result = NULL;
 	ssize_t length;
 	caps = cap_get_proc();
 	if(caps)
@@ -59,17 +59,17 @@ bin_cap(char *nam, char **argv, char *ops, int func)
 	} else
 	    puts(result);
     }
-    cap_free(&caps);
+    cap_free(caps);
     return ret;
 }
 
 static int
-bin_getcap(char *nam, char **argv, char *ops, int func)
+bin_getcap(char *nam, char **argv, Options ops, int func)
 {
     int ret = 0;
 
     do {
-	char *result;
+	char *result = NULL;
 	ssize_t length;
 	cap_t caps = cap_get_file(*argv);
 	if(caps)
@@ -79,13 +79,13 @@ bin_getcap(char *nam, char **argv, char *ops, int func)
 	    ret = 1;
 	} else
 	    printf("%s %s\n", *argv, result);
-	cap_free(&caps);
+	cap_free(caps);
     } while(*++argv);
     return ret;
 }
 
 static int
-bin_setcap(char *nam, char **argv, char *ops, int func)
+bin_setcap(char *nam, char **argv, Options ops, int func)
 {
     cap_t caps;
     int ret = 0;
@@ -102,7 +102,7 @@ bin_setcap(char *nam, char **argv, char *ops, int func)
 	    ret = 1;
 	}
     } while(*++argv);
-    cap_free(&caps);
+    cap_free(caps);
     return ret;
 }
 
@@ -124,18 +124,29 @@ static struct builtin bintab[] = {
 
 /**/
 int
-boot_cap(Module m)
+setup_(Module m)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
 }
 
-#ifdef MODULE
+/**/
+int
+boot_(Module m)
+{
+    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+}
 
 /**/
 int
-cleanup_cap(Module m)
+cleanup_(Module m)
 {
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
-#endif
+
+/**/
+int
+finish_(Module m)
+{
+    return 0;
+}
diff --git a/Src/Modules/clone.c b/Src/Modules/clone.c
index 1a41b7448..335f0fc80 100644
--- a/Src/Modules/clone.c
+++ b/Src/Modules/clone.c
@@ -41,7 +41,7 @@
 
 /**/
 static int
-bin_clone(char *nam, char **args, char *ops, int func)
+bin_clone(char *nam, char **args, Options ops, int func)
 {
     int ttyfd, pid;
 
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 4c2bcf791..e43f12b24 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -32,7 +32,7 @@
 #include <time.h>
 
 static int
-bin_strftime(char *nam, char **argv, char *ops, int func)
+bin_strftime(char *nam, char **argv, Options ops, int func)
 {
     int bufsize, x;
     char *endptr = NULL, *buffer = NULL;
diff --git a/Src/Modules/example.c b/Src/Modules/example.c
index 45ef3c28f..793d743fc 100644
--- a/Src/Modules/example.c
+++ b/Src/Modules/example.c
@@ -30,47 +30,203 @@
 #include "example.mdh"
 #include "example.pro"
 
+/* parameters */
+
+static zlong intparam;
+static char *strparam;
+static char **arrparam;
+
+
 /**/
 static int
-bin_example(char *nam, char **args, char *ops, int func)
+bin_example(char *nam, char **args, Options ops, int func)
 {
     unsigned char c;
+    char **oargs = args, **p = arrparam;
+    long i = 0;
 
     printf("Options: ");
     for (c = 32; ++c < 128;)
-	if (ops[c])
+	if (OPT_ISSET(ops,c))
 	    putchar(c);
     printf("\nArguments:");
-    for (; *args; args++) {
+    for (; *args; i++, args++) {
 	putchar(' ');
 	fputs(*args, stdout);
     }
     printf("\nName: %s\n", nam);
+#ifdef ZSH_64_BIT_TYPE
+    printf("\nInteger Parameter: %s\n", output64(intparam));
+#else
+    printf("\nInteger Parameter: %ld\n", intparam);
+#endif
+    printf("String Parameter: %s\n", strparam ? strparam : "");
+    printf("Array Parameter:");
+    if (p)
+	while (*p) printf(" %s", *p++);
+    printf("\n");
+
+    intparam = i;
+    zsfree(strparam);
+    strparam = ztrdup(*oargs ? *oargs : "");
+    freearray(arrparam);
+    arrparam = zarrdup(oargs);
     return 0;
 }
 
+/**/
+static int
+cond_p_len(char **a, int id)
+{
+    char *s1 = cond_str(a, 0, 0);
+
+    if (a[1]) {
+	zlong v = cond_val(a, 1);
+
+	return strlen(s1) == v;
+    } else {
+	return !s1[0];
+    }
+}
+
+/**/
+static int
+cond_i_ex(char **a, int id)
+{
+    char *s1 = cond_str(a, 0, 0), *s2 = cond_str(a, 1, 0);
+
+    return !strcmp("example", dyncat(s1, s2));
+}
+
+/**/
+static mnumber
+math_sum(char *name, int argc, mnumber *argv, int id)
+{
+    mnumber ret;
+    int f = 0;
+
+    ret.u.l = 0;
+    while (argc--) {
+	if (argv->type == MN_INTEGER) {
+	    if (f)
+		ret.u.d += (double) argv->u.l;
+	    else
+		ret.u.l += argv->u.l;
+	} else {
+	    if (f)
+		ret.u.d += argv->u.d;
+	    else {
+		ret.u.d = ((double) ret.u.l) + ((double) argv->u.d);
+		f = 1;
+	    }
+	}
+	argv++;
+    }
+    ret.type = (f ? MN_FLOAT : MN_INTEGER);
+
+    return ret;
+}
+
+/**/
+static mnumber
+math_length(char *name, char *arg, int id)
+{
+    mnumber ret;
+
+    ret.type = MN_INTEGER;
+    ret.u.l = strlen(arg);
+
+    return ret;
+}
+
+/**/
+static int
+ex_wrapper(Eprog prog, FuncWrap w, char *name)
+{
+    if (strncmp(name, "example", 7))
+	return 1;
+    else {
+	int ogd = opts[GLOBDOTS];
+
+	opts[GLOBDOTS] = 1;
+	runshfunc(prog, w, name);
+	opts[GLOBDOTS] = ogd;
+
+	return 0;
+    }
+}
+
 /*
- * boot_example is executed when the module is loaded.
+ * boot_ is executed when the module is loaded.
  */
 
 static struct builtin bintab[] = {
     BUILTIN("example", 0, bin_example, 0, -1, 0, "flags", NULL),
 };
 
+static struct conddef cotab[] = {
+    CONDDEF("len", 0, cond_p_len, 1, 2, 0),
+    CONDDEF("ex", CONDF_INFIX, cond_i_ex, 0, 0, 0),
+};
+
+static struct paramdef patab[] = {
+    INTPARAMDEF("exint", &intparam),
+    STRPARAMDEF("exstr", &strparam),
+    ARRPARAMDEF("exarr", &arrparam),
+};
+
+static struct mathfunc mftab[] = {
+    NUMMATHFUNC("sum", math_sum, 1, -1, 0),
+    STRMATHFUNC("length", math_length, 0),
+};
+
+static struct funcwrap wrapper[] = {
+    WRAPDEF(ex_wrapper),
+};
+
 /**/
 int
-boot_example(Module m)
+setup_(Module m)
 {
-    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    printf("The example module has now been set up.\n");
+    fflush(stdout);
+    return 0;
 }
 
-#ifdef MODULE
+/**/
+int
+boot_(Module m)
+{
+    intparam = 42;
+    strparam = ztrdup("example");
+    arrparam = (char **) zalloc(3 * sizeof(char *));
+    arrparam[0] = ztrdup("example");
+    arrparam[1] = ztrdup("array");
+    arrparam[2] = NULL;
+    return !(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
+	     addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
+	     addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
+	     addmathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab)) |
+	     !addwrapper(m, wrapper));
+}
 
 /**/
 int
-cleanup_example(Module m)
+cleanup_(Module m)
 {
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
+    deletemathfuncs(m->nam, mftab, sizeof(mftab)/sizeof(*mftab));
+    deletewrapper(m, wrapper);
+    return 0;
+}
+
+/**/
+int
+finish_(Module m)
+{
+    printf("Thank you for using the example module.  Have a nice day.\n");
+    fflush(stdout);
     return 0;
 }
-#endif
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index 6d962efd1..51f362631 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -56,7 +56,7 @@ ask(void)
 
 /**/
 static int
-bin_sync(char *nam, char **args, char *ops, int func)
+bin_sync(char *nam, char **args, Options ops, int func)
 {
     sync();
     return 0;
@@ -66,14 +66,14 @@ bin_sync(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
-bin_mkdir(char *nam, char **args, char *ops, int func)
+bin_mkdir(char *nam, char **args, Options ops, int func)
 {
     mode_t oumask = umask(0);
     mode_t mode = 0777 & ~oumask;
     int err = 0;
 
     umask(oumask);
-    if(ops['m']) {
+    if(OPT_ISSET(ops,'m')) {
 	char *str = *args++, *ptr;
 
 	if(!*args) {
@@ -91,7 +91,7 @@ bin_mkdir(char *nam, char **args, char *ops, int func)
 
 	while(ptr > *args + (**args == '/') && *--ptr == '/')
 	    *ptr = 0;
-	if(ops['p']) {
+	if(OPT_ISSET(ops,'p')) {
 	    char *ptr = *args;
 
 	    for(;;) {
@@ -147,7 +147,7 @@ domkdir(char *nam, char *path, mode_t mode, int p)
 
 /**/
 static int
-bin_rmdir(char *nam, char **args, char *ops, int func)
+bin_rmdir(char *nam, char **args, Options ops, int func)
 {
     int err = 0;
 
@@ -183,7 +183,7 @@ bin_rmdir(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
-bin_ln(char *nam, char **args, char *ops, int func)
+bin_ln(char *nam, char **args, Options ops, int func)
 {
     MoveFunc move;
     int flags, err = 0;
@@ -194,22 +194,22 @@ bin_ln(char *nam, char **args, char *ops, int func)
 
     if(func == BIN_MV) {
 	move = (MoveFunc) rename;
-	flags = ops['f'] ? 0 : MV_ASKNW;
+	flags = OPT_ISSET(ops,'f') ? 0 : MV_ASKNW;
 	flags |= MV_ATOMIC;
     } else {
-	flags = ops['f'] ? MV_FORCE : 0;
+	flags = OPT_ISSET(ops,'f') ? MV_FORCE : 0;
 #ifdef HAVE_LSTAT
-	if(ops['s'])
+	if(OPT_ISSET(ops,'s'))
 	    move = (MoveFunc) symlink;
 	else
 #endif
 	{
 	    move = (MoveFunc) link;
-	    if(!ops['d'])
+	    if(!OPT_ISSET(ops,'d'))
 		flags |= MV_NODIRS;
 	}
     }
-    if(ops['i'] && !ops['f'])
+    if(OPT_ISSET(ops,'i') && !OPT_ISSET(ops,'f'))
 	flags |= MV_INTER;
     for(a = args; a[1]; a++) ;
     if(a != args) {
@@ -567,18 +567,20 @@ rm_dirpost(char *arg, char *rp, struct stat const *sp, void *magic)
 
 /**/
 static int
-bin_rm(char *nam, char **args, char *ops, int func)
+bin_rm(char *nam, char **args, Options ops, int func)
 {
     struct rmmagic rmm;
     int err;
 
     rmm.nam = nam;
-    rmm.opt_force = ops['f'];
-    rmm.opt_interact = ops['i'] && !ops['f'];
-    rmm.opt_unlinkdir = ops['d'];
-    err = recursivecmd(nam, ops['f'], ops['r'] && !ops['d'], ops['s'],
+    rmm.opt_force = OPT_ISSET(ops,'f');
+    rmm.opt_interact = OPT_ISSET(ops,'i') && !OPT_ISSET(ops,'f');
+    rmm.opt_unlinkdir = OPT_ISSET(ops,'d');
+    err = recursivecmd(nam, OPT_ISSET(ops,'f'), 
+		       OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'d'),
+		       OPT_ISSET(ops,'s'),
 	args, recurse_donothing, rm_dirpost, rm_leaf, &rmm);
-    return ops['f'] ? 0 : err;
+    return OPT_ISSET(ops,'f') ? 0 : err;
 }
 
 /* chown builtin */
@@ -620,7 +622,7 @@ enum { BIN_CHOWN, BIN_CHGRP };
 
 /**/
 static int
-bin_chown(char *nam, char **args, char *ops, int func)
+bin_chown(char *nam, char **args, Options ops, int func)
 {
     struct chownmagic chm;
     char *uspec = ztrdup(*args), *p = uspec;
@@ -685,7 +687,7 @@ bin_chown(char *nam, char **args, char *ops, int func)
 	    chm.gid = -1;
     }
     free(uspec);
-    return recursivecmd(nam, 0, ops['R'], ops['s'],
+    return recursivecmd(nam, 0, OPT_ISSET(ops,'R'), OPT_ISSET(ops,'s'),
 	args + 1, chown_dochown, recurse_donothing, chown_dochown, &chm);
 }
 
diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c
index b6304ff01..36c437bc3 100644
--- a/Src/Modules/pcre.c
+++ b/Src/Modules/pcre.c
@@ -40,15 +40,15 @@ static pcre_extra *pcre_hints;
 
 /**/
 static int
-bin_pcre_compile(char *nam, char **args, char *ops, int func)
+bin_pcre_compile(char *nam, char **args, Options ops, int func)
 {
     int pcre_opts = 0, pcre_errptr;
     const char *pcre_error;
     
-    if(ops['a']) pcre_opts |= PCRE_ANCHORED;
-    if(ops['i']) pcre_opts |= PCRE_CASELESS;
-    if(ops['m']) pcre_opts |= PCRE_MULTILINE;
-    if(ops['x']) pcre_opts |= PCRE_EXTENDED;
+    if(OPT_ISSET(ops,'a')) pcre_opts |= PCRE_ANCHORED;
+    if(OPT_ISSET(ops,'i')) pcre_opts |= PCRE_CASELESS;
+    if(OPT_ISSET(ops,'m')) pcre_opts |= PCRE_MULTILINE;
+    if(OPT_ISSET(ops,'x')) pcre_opts |= PCRE_EXTENDED;
     
     pcre_hints = NULL;  /* Is this necessary? */
     
@@ -68,7 +68,7 @@ bin_pcre_compile(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
-bin_pcre_study(char *nam, char **args, char *ops, int func)
+bin_pcre_study(char *nam, char **args, Options ops, int func)
 {
     const char *pcre_error;
     
@@ -92,12 +92,12 @@ bin_pcre_study(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
-bin_pcre_match(char *nam, char **args, char *ops, int func)
+bin_pcre_match(char *nam, char **args, Options ops, int func)
 {
     int ret, capcount, *ovec, ovecsize;
     char **captures, **matches, *receptacle = NULL;
     
-    if(ops['a']) {
+    if(OPT_ISSET(ops,'a')) {
 	receptacle = *args++;
 	if(!*args) {
 	    zwarnnam(nam, "not enough arguments", NULL, 0);
diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c
index b676e2d36..2b70eba6d 100644
--- a/Src/Modules/socket.c
+++ b/Src/Modules/socket.c
@@ -58,7 +58,7 @@
 #endif
 
 static int
-bin_zsocket(char *nam, char **args, char *ops, int func)
+bin_zsocket(char *nam, char **args, Options ops, int func)
 {
     int err=1, verbose=0, test=0, targetfd=0;
     SOCKLEN_T len;
@@ -66,13 +66,13 @@ bin_zsocket(char *nam, char **args, char *ops, int func)
     struct sockaddr_un soun;
     int sfd;
 
-    if (ops['v'])
+    if (OPT_ISSET(ops,'v'))
 	verbose = 1;
 
-    if (ops['t'])
+    if (OPT_ISSET(ops,'t'))
 	test = 1;
 
-    if (ops['d']) {
+    if (OPT_ISSET(ops,'d')) {
 	targetfd = atoi(args[0]);
 	dargs = args + 1;
 	if (!targetfd) {
@@ -84,7 +84,7 @@ bin_zsocket(char *nam, char **args, char *ops, int func)
 	dargs = args;
 
 
-    if (ops['l']) {
+    if (OPT_ISSET(ops,'l')) {
 	char *localfn;
 
 	if (!dargs[0]) {
@@ -135,7 +135,7 @@ bin_zsocket(char *nam, char **args, char *ops, int func)
 	return 0;
 
     }
-    else if (ops['a'])
+    else if (OPT_ISSET(ops,'a'))
     {
 	int lfd, rfd;
 
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 335f7271e..5b5479711 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -341,7 +341,7 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags)
  */
 /**/
 static int
-bin_stat(char *name, char **args, char *ops, int func)
+bin_stat(char *name, char **args, Options ops, int func)
 {
     char **aptr, *arrnam = NULL, **array = NULL, **arrptr = NULL;
     char *hashnam = NULL, **hash = NULL, **hashptr = NULL;
@@ -376,12 +376,12 @@ bin_stat(char *name, char **args, char *ops, int func)
 	    }
 	    /* if name of link requested, turn on lstat */
 	    if (iwhich == ST_READLINK)
-		ops['L'] = 1;
+		ops->ind['L'] = 1;
 	    flags |= STF_PICK;
 	} else {
 	    for (; *arg; arg++) {
 		if (strchr("glLnNorstT", *arg))
-		    ops[STOUC(*arg)] = 1;
+		    ops->ind[STOUC(*arg)] = 1;
 		else if (*arg == 'A') {
 		    if (arg[1]) {
 			arrnam = arg+1;
@@ -404,7 +404,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 		    break;
 		} else if (*arg == 'f') {
 		    char *sfd;
-		    ops['f'] = 1;
+		    ops->ind['f'] = 1;
 		    if (arg[1]) {
 			sfd = arg+1;
 		    } else if (!(sfd = *++args)) {
@@ -425,7 +425,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 			return 1;
 		    }
 		    /* force string format in order to use time format */
-		    ops['s'] = 1;
+		    ops->ind['s'] = 1;
 		    break;
 		} else {
 		    zwarnnam(name, "bad option: -%c", NULL, *arg);
@@ -444,7 +444,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 	 * be similar to stat -A foo -A bar filename                 */
     }
 
-    if (ops['l']) {
+    if (OPT_ISSET(ops,'l')) {
 	/* list types and return:  can also list to array */
 	if (arrnam) {
 	    arrptr = array = (char **)zalloc((ST_COUNT+1)*sizeof(char *));
@@ -468,34 +468,34 @@ bin_stat(char *name, char **args, char *ops, int func)
 	return 0;
     }
 
-    if (!*args && !ops['f']) {
+    if (!*args && !OPT_ISSET(ops,'f')) {
 	zwarnnam(name, "no files given", NULL, 0);
 	return 1;
-    } else if (*args && ops['f']) {
+    } else if (*args && OPT_ISSET(ops,'f')) {
 	zwarnnam(name, "no files allowed with -f", NULL, 0);
 	return 1;
     }
 
     nargs = 0;
-    if (ops['f'])
+    if (OPT_ISSET(ops,'f'))
 	nargs = 1;
     else
 	for (aptr = args; *aptr; aptr++)
 	    nargs++;
 
-    if (ops['g']) {
+    if (OPT_ISSET(ops,'g')) {
 	flags |= STF_GMT;
-	ops['s'] = 1;
+	ops->ind['s'] = 1;
     }
-    if (ops['s'] || ops['r'])
+    if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'r'))
 	flags |= STF_STRING;
-    if (ops['r'] || !ops['s'])
+    if (OPT_ISSET(ops,'r') || !OPT_ISSET(ops,'s'))
 	flags |= STF_RAW;
-    if (ops['n'])
+    if (OPT_ISSET(ops,'n'))
 	flags |= STF_FILE;
-    if (ops['o'])
+    if (OPT_ISSET(ops,'o'))
 	flags |= STF_OCTAL;
-    if (ops['t'])
+    if (OPT_ISSET(ops,'t'))
 	flags |= STF_NAME;
 
     if (!(arrnam || hashnam)) {
@@ -505,9 +505,9 @@ bin_stat(char *name, char **args, char *ops, int func)
 	    flags |= STF_NAME;
     }
 
-    if (ops['N'] || ops['f'])
+    if (OPT_ISSET(ops,'N') || OPT_ISSET(ops,'f'))
 	flags &= ~STF_FILE;
-    if (ops['T'] || ops['H'])
+    if (OPT_ISSET(ops,'T') || OPT_ISSET(ops,'H'))
 	flags &= ~STF_NAME;
 
     if (hashnam) {
@@ -529,16 +529,18 @@ bin_stat(char *name, char **args, char *ops, int func)
 	arrptr = array = (char **)zcalloc((arrsize+1)*sizeof(char *));
     }
 
-    for (; ops['f'] || *args; args++) {
+    for (; OPT_ISSET(ops,'f') || *args; args++) {
 	char outbuf[PATH_MAX + 9]; /* "link   " + link name + NULL */
-	int rval = ops['f'] ? fstat(fd, &statbuf) :
-	    ops['L'] ? lstat(*args, &statbuf) : stat(*args, &statbuf);
+	int rval = OPT_ISSET(ops,'f') ? fstat(fd, &statbuf) :
+	    OPT_ISSET(ops,'L') ? lstat(*args, &statbuf) :
+	    stat(*args, &statbuf);
 	if (rval) {
-	    if (ops['f'])
+	    if (OPT_ISSET(ops,'f'))
 		sprintf(outbuf, "%d", fd);
-	    zwarnnam(name, "%s: %e", ops['f'] ? outbuf : *args, errno);
+	    zwarnnam(name, "%s: %e", OPT_ISSET(ops,'f') ? outbuf : *args,
+		     errno);
 	    ret = 1;
-	    if (ops['f'] || arrnam)
+	    if (OPT_ISSET(ops,'f') || arrnam)
 		break;
 	    else
 		continue;
@@ -558,7 +560,7 @@ bin_stat(char *name, char **args, char *ops, int func)
 	    if (arrnam)
 		*arrptr++ = ztrdup(outbuf);
 	    else if (hashnam) {
-		/* STF_NAME explicitly turned off for ops['H'] above */
+		/* STF_NAME explicitly turned off for ops.ind['H'] above */
 	    	*hashptr++ = ztrdup(statelts[iwhich]);
 		*hashptr++ = ztrdup(outbuf);
 	    } else
@@ -570,14 +572,14 @@ bin_stat(char *name, char **args, char *ops, int func)
 		if (arrnam)
 		    *arrptr++= ztrdup(outbuf);
 		else if (hashnam) {
-		    /* STF_NAME explicitly turned off for ops['H'] above */
+		    /* STF_NAME explicitly turned off for ops.ind['H'] above */
 		    *hashptr++ = ztrdup(statelts[i]);
 		    *hashptr++ = ztrdup(outbuf);
 		} else
 		    printf("%s\n", outbuf);
 	    }
 	}
-	if (ops['f'])
+	if (OPT_ISSET(ops,'f'))
 	    break;
 
 	if (!arrnam && !hashnam && args[1] && !(flags & STF_PICK))
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index 5dc00d0bc..96dde66e3 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -336,7 +336,7 @@ tcp_connect(Tcp_session sess, char *addrp, struct hostent *zhost, int d_port)
 }
 
 static int
-bin_ztcp(char *nam, char **args, char *ops, int func)
+bin_ztcp(char *nam, char **args, Options ops, int func)
 {
     int herrno, err=1, destport, force=0, verbose=0, test=0, targetfd=0;
     SOCKLEN_T  len;
@@ -345,16 +345,16 @@ bin_ztcp(char *nam, char **args, char *ops, int func)
     struct servent *srv;
     Tcp_session sess = NULL;
 
-    if (ops['f'])
+    if (OPT_ISSET(ops,'f'))
 	force = 1;
 
-    if (ops['v'])
+    if (OPT_ISSET(ops,'v'))
 	verbose = 1;
 
-    if (ops['t'])
+    if (OPT_ISSET(ops,'t'))
         test = 1;
 
-    if (ops['d']) {
+    if (OPT_ISSET(ops,'d')) {
 	targetfd = atoi(args[0]);
 	dargs = args + 1;
 	if (!targetfd) {
@@ -366,7 +366,7 @@ bin_ztcp(char *nam, char **args, char *ops, int func)
 	dargs = args;
 
 
-    if (ops['c']) {
+    if (OPT_ISSET(ops,'c')) {
 	if (!dargs[0]) {
 	    tcp_cleanup();
 	}
@@ -395,7 +395,7 @@ bin_ztcp(char *nam, char **args, char *ops, int func)
 	    }
 	}
     }
-    else if (ops['l']) {
+    else if (OPT_ISSET(ops,'l')) {
 	int lport = 0;
 
 	if (!dargs[0]) {
@@ -462,7 +462,7 @@ bin_ztcp(char *nam, char **args, char *ops, int func)
 	return 0;
 
     }
-    else if (ops['a'])
+    else if (OPT_ISSET(ops,'a'))
     {
 	int lfd, rfd;
 
@@ -571,7 +571,7 @@ bin_ztcp(char *nam, char **args, char *ops, int func)
 			remotename = ztpeer->h_name;
 		    else
 			remotename = ztrdup(inet_ntoa(sess->peer.in.sin_addr));
-		    if (ops['L']) {
+		    if (OPT_ISSET(ops,'L')) {
 			int schar;
 			if (sess->flags & ZTCP_ZFTP)
 			    schar = 'Z';
diff --git a/Src/Modules/termcap.c b/Src/Modules/termcap.c
index 8ec8919d9..177a0d5c7 100644
--- a/Src/Modules/termcap.c
+++ b/Src/Modules/termcap.c
@@ -103,7 +103,7 @@ ztgetflag(char *s)
 
 /**/
 static int
-bin_echotc(char *name, char **argv, char *ops, int func)
+bin_echotc(char *name, char **argv, Options ops, int func)
 {
     char *s, buf[2048], *t, *u;
     int num, argct;
diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c
index 3f62f0367..732495891 100644
--- a/Src/Modules/terminfo.c
+++ b/Src/Modules/terminfo.c
@@ -57,7 +57,7 @@ static Param terminfo_pm;
 
 /**/
 static int
-bin_echoti(char *name, char **argv, char *ops, int func)
+bin_echoti(char *name, char **argv, Options ops, int func)
 {
     char *s, *t;
     int num;
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index de762e883..918061e42 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -2951,7 +2951,7 @@ zftp_rmsession(char *name, char **args, int flags)
 
 /**/
 static int
-bin_zftp(char *name, char **args, char *ops, int func)
+bin_zftp(char *name, char **args, Options ops, int func)
 {
     char fullname[20] = "zftp ";
     char *cnam = *args++, *prefs, *ptr;
diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c
index 9c7acb334..3121efacf 100644
--- a/Src/Modules/zprof.c
+++ b/Src/Modules/zprof.c
@@ -136,9 +136,9 @@ cmpparcs(Parc *a, Parc *b)
 }
 
 static int
-bin_zprof(char *nam, char **args, char *ops, int func)
+bin_zprof(char *nam, char **args, Options ops, int func)
 {
-    if (ops['c']) {
+    if (OPT_ISSET(ops,'c')) {
 	freepfuncs(calls);
 	calls = NULL;
 	ncalls = 0;
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index ad5d2ab39..1be615610 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -603,20 +603,23 @@ ptywrite(Ptycmd cmd, char **args, int nonl)
 
 /**/
 static int
-bin_zpty(char *nam, char **args, char *ops, int func)
+bin_zpty(char *nam, char **args, Options ops, int func)
 {
-    if ((ops['r'] && ops['w']) ||
-	((ops['r'] || ops['w']) && (ops['d'] || ops['e'] ||
-				    ops['b'] || ops['L'])) ||
-	(ops['w'] && ops['t']) ||
-	(ops['n'] && (ops['b'] || ops['e'] || ops['r'] || ops['t'] ||
-		      ops['d'] || ops['L'])) ||
-	(ops['d'] && (ops['b'] || ops['e'] || ops['L'] || ops['t'])) ||
-	(ops['L'] && (ops['b'] || ops['e']))) {
+    if ((OPT_ISSET(ops,'r') && OPT_ISSET(ops,'w')) ||
+	((OPT_ISSET(ops,'r') || OPT_ISSET(ops,'w')) && 
+	 (OPT_ISSET(ops,'d') || OPT_ISSET(ops,'e') ||
+	  OPT_ISSET(ops,'b') || OPT_ISSET(ops,'L'))) ||
+	(OPT_ISSET(ops,'w') && OPT_ISSET(ops,'t')) ||
+	(OPT_ISSET(ops,'n') && (OPT_ISSET(ops,'b') || OPT_ISSET(ops,'e') ||
+				OPT_ISSET(ops,'r') || OPT_ISSET(ops,'t') ||
+				OPT_ISSET(ops,'d') || OPT_ISSET(ops,'L'))) ||
+	(OPT_ISSET(ops,'d') && (OPT_ISSET(ops,'b') || OPT_ISSET(ops,'e') ||
+				OPT_ISSET(ops,'L') || OPT_ISSET(ops,'t'))) ||
+	(OPT_ISSET(ops,'L') && (OPT_ISSET(ops,'b') || OPT_ISSET(ops,'e')))) {
 	zwarnnam(nam, "illegal option combination", NULL, 0);
 	return 1;
     }
-    if (ops['r'] || ops['w']) {
+    if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'w')) {
 	Ptycmd p;
 
 	if (!*args) {
@@ -628,13 +631,14 @@ bin_zpty(char *nam, char **args, char *ops, int func)
 	}
 	if (p->fin)
 	    return 2;
-	if (ops['t'] && p->read == -1 && !read_poll(p->fd, &p->read, 0))
+	if (OPT_ISSET(ops,'t') && p->read == -1 &&
+	    !read_poll(p->fd, &p->read, 0))
 	    return 1;
 
-	return (ops['r'] ?
+	return (OPT_ISSET(ops,'r') ?
 		ptyread(nam, p, args + 1) :
-		ptywrite(p, args + 1, ops['n']));
-    } else if (ops['d']) {
+		ptywrite(p, args + 1, OPT_ISSET(ops,'n')));
+    } else if (OPT_ISSET(ops,'d')) {
 	Ptycmd p;
 	int ret = 0;
 
@@ -650,7 +654,7 @@ bin_zpty(char *nam, char **args, char *ops, int func)
 	    deleteallptycmds();
 
 	return ret;
-    } else if (ops['t']) {
+    } else if (OPT_ISSET(ops,'t')) {
 	Ptycmd p;
 
 	if (!*args) {
@@ -671,14 +675,15 @@ bin_zpty(char *nam, char **args, char *ops, int func)
 	    zwarnnam(nam, "pty command name already used: %s", *args, 0);
 	    return 1;
 	}
-	return newptycmd(nam, *args, args + 1, ops['e'], ops['b']);
+	return newptycmd(nam, *args, args + 1, OPT_ISSET(ops,'e'), 
+			 OPT_ISSET(ops,'b'));
     } else {
 	Ptycmd p;
 	char **a;
 
 	for (p = ptycmds; p; p = p->next) {
 	    checkptycmd(p);
-	    if (ops['L'])
+	    if (OPT_ISSET(ops,'L'))
 		printf("%s %s%s%s ", nam, (p->echo ? "-e " : ""),
 		       (p->nblock ? "-b " : ""), p->name);
 	    else if (p->fin)
diff --git a/Src/Modules/zselect.c b/Src/Modules/zselect.c
index 2eee59d03..25954559b 100644
--- a/Src/Modules/zselect.c
+++ b/Src/Modules/zselect.c
@@ -62,7 +62,7 @@ handle_digits(char *nam, char *argptr, fd_set *fdset, int *fdmax)
 
 /**/
 static int
-bin_zselect(char *nam, char **args, char *ops, int func)
+bin_zselect(char *nam, char **args, Options ops, int func)
 {
 #ifdef HAVE_SELECT
     int i, fd, fdsetind = 0, fdmax = 0, fdcount;
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index cb099bbcc..4ef237d90 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -254,7 +254,7 @@ lookupstyle(char *ctxt, char *style)
 }
 
 static int
-bin_zstyle(char *nam, char **args, char *ops, int func)
+bin_zstyle(char *nam, char **args, Options ops, int func)
 {
     int min, max, n, add = 0, list = 0, eval = 0;
 
@@ -550,7 +550,7 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 /* Format stuff. */
 
 static int
-bin_zformat(char *nam, char **args, char *ops, int func)
+bin_zformat(char *nam, char **args, Options ops, int func)
 {
     char opt;
 
@@ -1161,7 +1161,7 @@ rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp)
 */
 
 static int
-bin_zregexparse(char *nam, char **args, char *ops, int func)
+bin_zregexparse(char *nam, char **args, Options ops, int func)
 {
     int oldextendedglob = opts[EXTENDEDGLOB];
     char *var1 = args[0];
@@ -1187,7 +1187,7 @@ bin_zregexparse(char *nam, char **args, char *ops, int func)
 	ret = 0;
 
     if (!ret)
-	ret = rmatch(&result, subj, var1, var2, ops['c']);
+	ret = rmatch(&result, subj, var1, var2, OPT_ISSET(ops,'c'));
     popheap();
 
     opts[EXTENDEDGLOB] = oldextendedglob;
@@ -1315,7 +1315,7 @@ add_opt_val(Zoptdesc d, char *arg)
 }
 
 static int
-bin_zparseopts(char *nam, char **args, char *ops, int func)
+bin_zparseopts(char *nam, char **args, Options ops, int func)
 {
     char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL, **cp, **np;
     int del = 0, f, extract = 0, keep = 0;
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index a84f604cc..69f742731 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -187,7 +187,7 @@ freecompcond(void *a)
 
 /**/
 int
-compctlread(char *name, char **args, char *ops, char *reply)
+compctlread(char *name, char **args, Options ops, char *reply)
 {
     char *buf, *bptr;
 
@@ -198,15 +198,15 @@ compctlread(char *name, char **args, char *ops, char *reply)
 	return 1;
     }
 
-    if (ops['l']) {
+    if (OPT_ISSET(ops,'l')) {
 	/* -ln gives the index of the word the cursor is currently on, which is
 	available in cs (but remember that Zsh counts from one, not zero!) */
-	if (ops['n']) {
+	if (OPT_ISSET(ops,'n')) {
 	    char nbuf[14];
 
-	    if (ops['e'] || ops['E'])
+	    if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E'))
 		printf("%d\n", cs + 1);
-	    if (!ops['e']) {
+	    if (!OPT_ISSET(ops,'e')) {
 		sprintf(nbuf, "%d", cs + 1);
 		setsparam(reply, ztrdup(nbuf));
 	    }
@@ -214,11 +214,11 @@ compctlread(char *name, char **args, char *ops, char *reply)
 	}
 	/* without -n, the current line is assigned to the given parameter as a
 	scalar */
-	if (ops['e'] || ops['E']) {
+	if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
 	    zputs((char *) line, stdout);
 	    putchar('\n');
 	}
-	if (!ops['e'])
+	if (!OPT_ISSET(ops,'e'))
 	    setsparam(reply, ztrdup((char *) line));
     } else {
 	int i;
@@ -226,12 +226,12 @@ compctlread(char *name, char **args, char *ops, char *reply)
 	/* -cn gives the current cursor position within the current word, which
 	is available in clwpos (but remember that Zsh counts from one, not
 	zero!) */
-	if (ops['n']) {
+	if (OPT_ISSET(ops,'n')) {
 	    char nbuf[14];
 
-	    if (ops['e'] || ops['E'])
+	    if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E'))
 		printf("%d\n", clwpos + 1);
-	    if (!ops['e']) {
+	    if (!OPT_ISSET(ops,'e')) {
 		sprintf(nbuf, "%d", clwpos + 1);
 		setsparam(reply, ztrdup(nbuf));
 	    }
@@ -239,7 +239,7 @@ compctlread(char *name, char **args, char *ops, char *reply)
 	}
 	/* without -n, the words of the current line are assigned to the given
 	parameters separately */
-	if (ops['A'] && !ops['e']) {
+	if (OPT_ISSET(ops,'A') && !OPT_ISSET(ops,'e')) {
 	    /* the -A option means that one array is specified, instead of
 	    many parameters */
 	    char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *));
@@ -250,13 +250,13 @@ compctlread(char *name, char **args, char *ops, char *reply)
 	    setaparam(reply, b);
 	    return 0;
 	}
-	if (ops['e'] || ops['E']) {
+	if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
 	    for (i = 0; i < clwnum; i++) {
 		zputs(clwords[i], stdout);
 		putchar('\n');
 	    }
 
-	    if (ops['e'])
+	    if (OPT_ISSET(ops,'e'))
 		return 0;
 	}
 
@@ -1572,7 +1572,7 @@ printcompctlp(HashNode hn, int printflags)
 
 /**/
 static int
-bin_compctl(char *name, char **argv, char *ops, int func)
+bin_compctl(char *name, char **argv, Options ops, int func)
 {
     Compctl cc = NULL;
     int ret = 0;
@@ -1678,14 +1678,14 @@ bin_compctl(char *name, char **argv, char *ops, int func)
 #define CFN_DEFAULT 2
 
 static int
-bin_compcall(char *name, char **argv, char *ops, int func)
+bin_compcall(char *name, char **argv, Options ops, int func)
 {
     if (incompfunc != 1) {
 	zwarnnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
-    return makecomplistctl((ops['T'] ? 0 : CFN_FIRST) |
-			   (ops['D'] ? 0 : CFN_DEFAULT));
+    return makecomplistctl((OPT_ISSET(ops,'T') ? 0 : CFN_FIRST) |
+			   (OPT_ISSET(ops,'D') ? 0 : CFN_DEFAULT));
 }
 
 /*
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index ef029ddcb..713ce7f9e 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -421,7 +421,7 @@ parse_class(Cpattern p, unsigned char *s, unsigned char e)
 
 /**/
 static int
-bin_compadd(char *name, char **argv, char *ops, int func)
+bin_compadd(char *name, char **argv, Options ops, int func)
 {
     struct cadata dat;
     char *p, **sp, *e, *m = NULL, *mstr = NULL;
@@ -866,7 +866,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 
 /**/
 static int
-bin_compset(char *name, char **argv, char *ops, int func)
+bin_compset(char *name, char **argv, Options ops, int func)
 {
     int test = 0, na = 0, nb = 0;
     char *sa = NULL, *sb = NULL;
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 412347ec9..7fa4364df 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -717,7 +717,7 @@ cd_get(char **params)
 
 /**/
 static int
-bin_compdescribe(char *nam, char **args, char *ops, int func)
+bin_compdescribe(char *nam, char **args, Options ops, int func)
 {
     int n = arrlen(args);
 
@@ -2259,7 +2259,7 @@ ca_set_data(LinkList descr, LinkList act, LinkList subc,
 }
 
 static int
-bin_comparguments(char *nam, char **args, char *ops, int func)
+bin_comparguments(char *nam, char **args, Options ops, int func)
 {
     int min, max, n;
     Castate lstate = &ca_laststate;
@@ -3136,7 +3136,7 @@ cv_parse_word(Cvdef d)
 }
 
 static int
-bin_compvalues(char *nam, char **args, char *ops, int func)
+bin_compvalues(char *nam, char **args, Options ops, int func)
 {
     int min, max, n;
 
@@ -3349,7 +3349,7 @@ comp_quote(char *str, int prefix)
 }
 
 static int
-bin_compquote(char *nam, char **args, char *ops, int func)
+bin_compquote(char *nam, char **args, Options ops, int func)
 {
     char *name;
     struct value vbuf;
@@ -3372,7 +3372,8 @@ bin_compquote(char *nam, char **args, char *ops, int func)
 	if ((v = getvalue(&vbuf, &name, 0))) {
 	    switch (PM_TYPE(v->pm->flags)) {
 	    case PM_SCALAR:
-		setstrvalue(v, ztrdup(comp_quote(getstrvalue(v), ops['p'])));
+		setstrvalue(v, ztrdup(comp_quote(getstrvalue(v), 
+						 OPT_ISSET(ops,'p'))));
 		break;
 	    case PM_ARRAY:
 		{
@@ -3382,7 +3383,7 @@ bin_compquote(char *nam, char **args, char *ops, int func)
 		    char **p = new;
 
 		    for (; *val; val++, p++)
-			*p = ztrdup(comp_quote(*val, ops['p']));
+			*p = ztrdup(comp_quote(*val, OPT_ISSET(ops,'p')));
 		    *p = NULL;
 
 		    setarrvalue(v, new);
@@ -3499,7 +3500,7 @@ arrcontains(char **a, char *s, int colon)
 }
 
 static int
-bin_comptags(char *nam, char **args, char *ops, int func)
+bin_comptags(char *nam, char **args, Options ops, int func)
 {
     int min, max, n, level;
 
@@ -3629,7 +3630,7 @@ bin_comptags(char *nam, char **args, char *ops, int func)
 }
 
 static int
-bin_comptry(char *nam, char **args, char *ops, int func)
+bin_comptry(char *nam, char **args, Options ops, int func)
 {
     if (incompfunc != 1) {
 	zwarnnam(nam, "can only be called from completion function", NULL, 0);
@@ -4321,7 +4322,7 @@ cf_remove_other(char **names, char *pre, int *amb)
 }
 
 static int
-bin_compfiles(char *nam, char **args, char *ops, int func)
+bin_compfiles(char *nam, char **args, Options ops, int func)
 {
     if (incompfunc != 1) {
 	zwarnnam(nam, "can only be called from completion function", NULL, 0);
@@ -4423,7 +4424,7 @@ bin_compfiles(char *nam, char **args, char *ops, int func)
 }
 
 static int
-bin_compgroups(char *nam, char **args, char *ops, int func)
+bin_compgroups(char *nam, char **args, Options ops, int func)
 {
     Heap oldheap;
     char *n;
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 9fd5a9197..c36657697 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -604,12 +604,12 @@ keyisprefix(Keymap km, char *seq)
 
 /**/
 int
-bin_bindkey(char *name, char **argv, char *ops, int func)
+bin_bindkey(char *name, char **argv, Options ops, int func)
 {
     static struct opn {
 	char o;
 	char selp;
-	int (*func) _((char *, char *, Keymap, char **, char *, char));
+	int (*func) _((char *, char *, Keymap, char **, Options, char));
 	int min, max;
     } const opns[] = {
 	{ 'l', 0, bin_bindkey_lsmaps, 0,  0 },
@@ -628,15 +628,16 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ;
+    for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(ops[STOUC(opp->o)]) {
+	    if(OPT_ISSET(ops,STOUC(opp->o))) {
 		zwarnnam(name, "incompatible operation selection options",
 		    NULL, 0);
 		return 1;
 	    }
-    n = ops['e'] + ops['v'] + ops['a'] + ops['M'];
+    n = OPT_ISSET(ops,'e') + OPT_ISSET(ops,'v') + 
+	OPT_ISSET(ops,'a') + OPT_ISSET(ops,'M');
     if(!op->selp && n) {
 	zwarnnam(name, "keymap cannot be selected with -%c", NULL, op->o);
 	return 1;
@@ -648,13 +649,13 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 
     /* keymap selection */
     if(op->selp) {
-	if(ops['e'])
+	if(OPT_ISSET(ops,'e'))
 	    kmname = "emacs";
-	else if(ops['v'])
+	else if(OPT_ISSET(ops,'v'))
 	    kmname = "viins";
-	else if(ops['a'])
+	else if(OPT_ISSET(ops,'a'))
 	    kmname = "vicmd";
-	else if(ops['M']) {
+	else if(OPT_ISSET(ops,'M')) {
 	    kmname = *argv++;
 	    if(!kmname) {
 		zwarnnam(name, "-M option requires a keymap argument", NULL, 0);
@@ -667,7 +668,7 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 	    zwarnnam(name, "no such keymap `%s'", kmname, 0);
 	    return 1;
 	}
-	if(ops['e'] || ops['v'])
+	if(OPT_ISSET(ops,'e') || OPT_ISSET(ops,'v'))
 	    linkkeymap(km, "main", 0);
     } else {
 	kmname = NULL;
@@ -676,7 +677,7 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 
     /* listing is a special case */
     if(!op->o && (!argv[0] || !argv[1])) {
-	if(ops['e'] || ops['v'])
+	if(OPT_ISSET(ops,'e') || OPT_ISSET(ops,'v'))
 	    return 0;
 	return bin_bindkey_list(name, kmname, km, argv, ops, op->o);
     }
@@ -699,9 +700,9 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 
 /**/
 static int
-bin_bindkey_lsmaps(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_lsmaps(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
-    scanhashtable(keymapnamtab, 1, 0, 0, scanlistmaps, ops['L']);
+    scanhashtable(keymapnamtab, 1, 0, 0, scanlistmaps, OPT_ISSET(ops,'L'));
     return 0;
 }
 
@@ -725,7 +726,7 @@ scanlistmaps(HashNode hn, int list)
 
 /**/
 static int
-bin_bindkey_delall(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_delall(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     keymapnamtab->emptytable(keymapnamtab);
     default_bindings();
@@ -736,7 +737,7 @@ bin_bindkey_delall(char *name, char *kmname, Keymap km, char **argv, char *ops,
 
 /**/
 static int
-bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     int ret = 0;
 
@@ -755,7 +756,7 @@ bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
 
 /**/
 static int
-bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     km = openkeymap(argv[0]);
     if(!km) {
@@ -772,7 +773,7 @@ bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 
 /**/
 static int
-bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     KeymapName kmn = (KeymapName) keymapnamtab->getnode(keymapnamtab, argv[0]);
 
@@ -800,7 +801,7 @@ bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
 
 /**/
 static int
-bin_bindkey_meta(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_meta(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     char m[3], *str;
     int i;
@@ -830,7 +831,7 @@ bin_bindkey_meta(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 
 /**/
 static int
-bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     int ret = 0;
 
@@ -847,7 +848,7 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 	zwarnnam(name, "keymap `%s' is protected", kmname, 0);
 	return 1;
     }
-    if (func == 'r' && ops['p']) {
+    if (func == 'r' && OPT_ISSET(ops,'p')) {
 	char *useq, *bseq;
 	int len;
 	struct remprefstate rps;
@@ -878,7 +879,7 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 	}
 	bseq = getkeystring(useq, &len, 2, NULL);
 	seq = metafy(bseq, len, META_USEHEAP);
-	if(ops['R']) {
+	if(OPT_ISSET(ops,'R')) {
 	    int first, last;
 	    char m[3];
 
@@ -924,13 +925,13 @@ scanremoveprefix(char *seq, Thingy bind, char *str, void *magic)
 
 /**/
 static int
-bin_bindkey_list(char *name, char *kmname, Keymap km, char **argv, char *ops, char func)
+bin_bindkey_list(char *name, char *kmname, Keymap km, char **argv, Options ops, char func)
 {
     struct bindstate bs;
 
-    bs.flags = ops['L'] ? BS_LIST : 0;
+    bs.flags = OPT_ISSET(ops,'L') ? BS_LIST : 0;
     bs.kmname = kmname;
-    if(argv[0] && !ops['p']) {
+    if(argv[0] && !OPT_ISSET(ops,'p')) {
 	int len;
 	char *seq;
 
@@ -944,7 +945,7 @@ bin_bindkey_list(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
 	bindlistout(&bs);
     } else {
 	/* empty prefix is equivalent to no prefix */
-	if (ops['p'] && (!argv[0] || argv[0][0])) {
+	if (OPT_ISSET(ops,'p') && (!argv[0] || argv[0][0])) {
 	    if (!argv[0]) {
 		zwarnnam(name, "option -p requires a prefix string", NULL, 0);
 		return 1;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index e1429a0d0..1c45d120c 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -951,7 +951,7 @@ mod_export char *varedarg;
 
 /**/
 static int
-bin_vared(char *name, char **args, char *ops, int func)
+bin_vared(char *name, char **args, Options ops, int func)
 {
     char *s, *t, *ova = varedarg;
     struct value vbuf;
@@ -1012,11 +1012,11 @@ bin_vared(char *name, char **args, char *ops, int func)
 		break;
 	    case 'h':
 		/* -h option -- enable history */
-		ops['h'] = 1;
+		ops->ind['h'] = 1;
 		break;
 	    case 'e':
 		/* -e option -- enable EOF */
-		ops['e'] = 1;
+		ops->ind['e'] = 1;
 		break;
 	    default:
 		/* unrecognised option character */
@@ -1113,14 +1113,14 @@ bin_vared(char *name, char **args, char *ops, int func)
 
     varedarg = *args;
     ifl = isfirstln;
-    if (ops['h'])
+    if (OPT_ISSET(ops,'h'))
 	hbegin(2);
-    isfirstln = ops['e'];
+    isfirstln = OPT_ISSET(ops,'e');
     ieof = opts[IGNOREEOF];
     opts[IGNOREEOF] = 0;
-    t = (char *) zleread(p1, p2, ops['h'] ? ZLRF_HISTORY : 0);
+    t = (char *) zleread(p1, p2, OPT_ISSET(ops,'h') ? ZLRF_HISTORY : 0);
     opts[IGNOREEOF] = ieof;
-    if (ops['h'])
+    if (OPT_ISSET(ops,'h'))
 	hend(NULL);
     isfirstln = ifl;
     varedarg = ova;
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 7d6c5103e..503060d7b 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -324,11 +324,11 @@ deletezlefunction(Widget w)
 
 /**/
 int
-bin_zle(char *name, char **args, char *ops, int func)
+bin_zle(char *name, char **args, Options ops, int func)
 {
     static struct opn {
 	char o;
-	int (*func) _((char *, char **, char *, char));
+	int (*func) _((char *, char **, Options, char));
 	int min, max;
     } const opns[] = {
 	{ 'l', bin_zle_list, 0, -1 },
@@ -348,10 +348,10 @@ bin_zle(char *name, char **args, char *ops, int func)
     int n;
 
     /* select operation and ensure no clashing arguments */
-    for(op = opns; op->o && !ops[STOUC(op->o)]; op++) ;
+    for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
     if(op->o)
 	for(opp = op; (++opp)->o; )
-	    if(ops[STOUC(opp->o)]) {
+	    if(OPT_ISSET(ops,STOUC(opp->o))) {
 		zwarnnam(name, "incompatible operation selection options",
 		    NULL, 0);
 		return 1;
@@ -373,11 +373,11 @@ bin_zle(char *name, char **args, char *ops, int func)
 
 /**/
 static int
-bin_zle_list(char *name, char **args, char *ops, char func)
+bin_zle_list(char *name, char **args, Options ops, char func)
 {
     if (!*args) {
 	scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets,
-		      (ops['a'] ? -1 : ops['L']));
+		      (OPT_ISSET(ops,'a') ? -1 : OPT_ISSET(ops,'L')));
 	return 0;
     } else {
 	int ret = 0;
@@ -385,7 +385,7 @@ bin_zle_list(char *name, char **args, char *ops, char func)
 
 	for (; *args && !ret; args++) {
 	    if (!(t = (Thingy) thingytab->getnode2(thingytab, *args)) ||
-		(!ops['a'] && (t->widget->flags & WIDGET_INT)))
+		(!OPT_ISSET(ops,'a') && (t->widget->flags & WIDGET_INT)))
 		ret = 1;
 	}
 	return ret;
@@ -394,7 +394,7 @@ bin_zle_list(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_refresh(char *name, char **args, char *ops, char func)
+bin_zle_refresh(char *name, char **args, Options ops, char func)
 {
     char *s = statusline;
     int sl = statusll, ocl = clearlist;
@@ -421,11 +421,11 @@ bin_zle_refresh(char *name, char **args, char *ops, char func)
 		lastlistlen++;
 	    showinglist = clearlist = 0;
 	    zmult = zmultsav;
-	} else if (ops['c']) {
+	} else if (OPT_ISSET(ops,'c')) {
 	    clearlist = 1;
 	    lastlistlen = 0;
 	}
-    } else if (ops['c']) {
+    } else if (OPT_ISSET(ops,'c')) {
 	clearlist = listshown = 1;
 	lastlistlen = 0;
     }
@@ -439,7 +439,7 @@ bin_zle_refresh(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_mesg(char *name, char **args, char *ops, char func)
+bin_zle_mesg(char *name, char **args, Options ops, char func)
 {
     if (!zleactive) {
 	zwarnnam(name, "can only be called from widget function", NULL, 0);
@@ -453,7 +453,7 @@ bin_zle_mesg(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_unget(char *name, char **args, char *ops, char func)
+bin_zle_unget(char *name, char **args, Options ops, char func)
 {
     char *b = *args, *p = b + strlen(b);
 
@@ -468,7 +468,7 @@ bin_zle_unget(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_keymap(char *name, char **args, char *ops, char func)
+bin_zle_keymap(char *name, char **args, Options ops, char func)
 {
     if (!zleactive) {
 	zwarnnam(name, "can only be called from widget function", NULL, 0);
@@ -522,7 +522,7 @@ scanlistwidgets(HashNode hn, int list)
 
 /**/
 static int
-bin_zle_del(char *name, char **args, char *ops, char func)
+bin_zle_del(char *name, char **args, Options ops, char func)
 {
     int ret = 0;
 
@@ -541,7 +541,7 @@ bin_zle_del(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_link(char *name, char **args, char *ops, char func)
+bin_zle_link(char *name, char **args, Options ops, char func)
 {
     Thingy t = (Thingy) thingytab->getnode(thingytab, args[0]);
 
@@ -558,7 +558,7 @@ bin_zle_link(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_new(char *name, char **args, char *ops, char func)
+bin_zle_new(char *name, char **args, Options ops, char func)
 {
     Widget w = zalloc(sizeof(*w));
 
@@ -574,7 +574,7 @@ bin_zle_new(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_complete(char *name, char **args, char *ops, char func)
+bin_zle_complete(char *name, char **args, Options ops, char func)
 {
     Thingy t;
     Widget w, cw;
@@ -608,7 +608,7 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_call(char *name, char **args, char *ops, char func)
+bin_zle_call(char *name, char **args, Options ops, char func)
 {
     Thingy t;
     struct modifier modsave;
@@ -672,7 +672,7 @@ bin_zle_call(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_invalidate(char *name, char **args, char *ops, char func)
+bin_zle_invalidate(char *name, char **args, Options ops, char func)
 {
     if (zleactive) {
 	if (!trashedzle)
@@ -684,7 +684,7 @@ bin_zle_invalidate(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_fd(char *name, char **args, char *ops, char func)
+bin_zle_fd(char *name, char **args, Options ops, char func)
 {
     int fd = 0, i, found = 0;
     char *endptr;
@@ -698,7 +698,7 @@ bin_zle_fd(char *name, char **args, char *ops, char func)
 	}
     }
 
-    if (ops['L'] || !*args) {
+    if (OPT_ISSET(ops,'L') || !*args) {
 	/* Listing handlers. */
 	if (*args && args[1]) {
 	    zwarnnam(name, "too many arguments for -FL", NULL, 0);
diff --git a/Src/builtin.c b/Src/builtin.c
index a8174a2bc..1bf26aeb8 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -46,14 +46,14 @@ static struct builtin builtins[] =
     BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
     BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmr", NULL),
-    BUILTIN("autoload", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "tUXwkz", "u"),
+    BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "tUXwkz", "u"),
     BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL),
     BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL),
     BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
     BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFHLRTUZafghilprtux", NULL),
+    BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lprtux", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -62,12 +62,18 @@ static struct builtin builtins[] =
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "EFHLRTUZafhilprtu", "xg"),
+    BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
-    BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
+    /*
+     * We used to behave as if the argument to -e was optional.
+     * But that's actually not useful, so it's more consistent to
+     * cause an error.
+     */
+    BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "nlre:IRWAdDfEim",
+	    NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
-    BUILTIN("float", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "EFHghlprtux", "E"),
-    BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtuU", NULL),
+    BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "E:%F:%Hghlprtux", "E"),
+    BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "mtuU", NULL),
     BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
     BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
     BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL),
@@ -77,11 +83,11 @@ static struct builtin builtins[] =
 #endif
 
     BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "nrdDfEim", "l"),
-    BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "Hghilprtux", "i"),
+    BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "Hghi:%lprtux", "i"),
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
-    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFHLRTUZahilprtux", NULL),
+    BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
     BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
     BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
 
@@ -94,14 +100,14 @@ static struct builtin builtins[] =
 #endif
 
     BUILTIN("popd", 0, bin_cd, 0, 2, BIN_POPD, NULL, NULL),
-    BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPbnrsflzNu0123456789pioOcm-", NULL),
+    BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPbnrsf:lzNu:pioOcm-", NULL),
     BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, NULL, NULL),
     BUILTIN("pushd", 0, bin_cd, 0, 2, BIN_PUSHD, NULL, NULL),
     BUILTIN("pushln", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"),
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
-    BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
-    BUILTIN("read", 0, bin_read, 0, -1, 0, "ceklnpqrstzuAE0123456789", NULL),
-    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFHLRTUZafghilptux", "r"),
+    BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "nrl", NULL),
+    BUILTIN("read", 0, bin_read, 0, -1, 0, "cek:%lnpqrstzu:AE", NULL),
+    BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@@ -115,7 +121,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
-    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFHLRTUZafghilprtuxm", NULL),
+    BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lprtuxm", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@@ -201,29 +207,49 @@ freebuiltinnode(HashNode hn)
     }
 }
 
-static char *auxdata;
-static int auxlen;
+/* Make sure we have space for a new option and increment. */
 
-/* execute a builtin handler function after parsing the arguments */
+#define OPT_ALLOC_CHUNK 16
 
-#define MAX_OPS 128
+/**/
+static int
+new_optarg(Options ops)
+{
+    /* Argument index must be a non-zero 6-bit number. */
+    if (ops->argscount == 63)
+	return 1;
+    if (ops->argsalloc == ops->argscount) {
+	char **newptr = 
+	    (char **)zhalloc((ops->argsalloc + OPT_ALLOC_CHUNK) *
+			     sizeof(char *));
+	if (ops->argsalloc)
+	    memcpy(newptr, ops->args, ops->argsalloc * sizeof(char *));
+	ops->args = newptr;
+	ops->argsalloc += OPT_ALLOC_CHUNK;
+    }
+    ops->argscount++;
+    return 0;
+}
+
+
+/* execute a builtin handler function after parsing the arguments */
 
 /**/
 int
 execbuiltin(LinkList args, Builtin bn)
 {
     LinkNode n;
-    char ops[MAX_OPS], *arg, *pp, *name, *optstr;
+    char *arg, *pp, *name, *optstr;
     char *oxarg, *xarg = NULL;
-    char typenumstr[] = TYPESET_OPTNUM;
     int flags, sense, argc = 0, execop, xtr = isset(XTRACE), lxarg = 0;
+    struct options ops;
 
-    /* initialise some static variables */
-    auxdata = NULL;
-    auxlen = 0;
+    /* initialise options structure */
+    memset(ops.ind, 0, MAX_OPS*sizeof(unsigned char));
+    ops.args = NULL;
+    ops.argscount = ops.argsalloc = 0;
 
     /* initialize some local variables */
-    memset(ops, 0, MAX_OPS);
     name = (char *) ugetnode(args);
 
     arg = (char *) ugetnode(args);
@@ -239,7 +265,7 @@ execbuiltin(LinkList args, Builtin bn)
 
     /* Sort out the options. */
     if ((flags & BINF_ECHOPTS) && isset(BSDECHO))
-	ops['E'] = 1;
+	ops.ind['E'] = 1;
     if (optstr)
 	/* while arguments look like options ... */
 	while (arg &&
@@ -269,26 +295,62 @@ execbuiltin(LinkList args, Builtin bn)
 		    lxarg = strlen(xarg);
 		}
 	    }
-	    /* handle -- or - (ops['-']), and + (ops['-'] and ops['+']) */
+	    /* handle -- or - (ops.ind['-']), and +
+	     * (ops.ind['-'] and ops.ind['+']) */
 	    if (arg[1] == '-')
 		arg++;
 	    if (!arg[1]) {
-		ops['-'] = 1;
+		ops.ind['-'] = 1;
 		if (!sense)
-		    ops['+'] = 1;
+		    ops.ind['+'] = 1;
 	    }
 	    /* save options in ops, as long as they are in bn->optstr */
 	    execop = -1;
-	    while (*++arg)
-		if (strchr(optstr, execop = (int)*arg))
-		    ops[(int)*arg] = (sense) ? 1 : 2;
-		else
+	    while (*++arg) {
+		char *optptr;
+		if ((optptr = strchr(optstr, execop = (int)*arg))) {
+		    ops.ind[(int)*arg] = (sense) ? 1 : 2;
+		    if (optptr[1] == ':') {
+			char *argptr = NULL;
+			if (optptr[2] == ':') {
+			    if (arg[1])
+				argptr = arg+1;
+			    /* Optional argument in same word*/
+			} else if (optptr[2] == '%') {
+			    /* Optional numeric argument in same
+			     * or next word. */
+			    if (arg[1] && idigit(arg[1]))
+				argptr = arg+1;
+			    else if (firstnode(args) &&
+				     idigit(*(char *)peekfirst(args)))
+				argptr = arg = (char *)ugetnode(args);
+			} else {
+			    /* Mandatory argument */
+			    if (arg[1])
+				argptr = arg+1;
+			    else if ((arg = (char *)ugetnode(args)))
+				argptr = arg;
+			    else {
+				zwarnnam(name, "argument expected: -%c", NULL,
+					 execop);
+				return 1;
+			    }
+			}
+			if (argptr) {
+			    if (new_optarg(&ops)) {
+				zwarnnam(name, 
+					 "too many option arguments", NULL, 0);
+				return 1;
+			    }
+			    ops.ind[execop] |= ops.argscount << 2;
+			    ops.args[ops.argscount-1] = argptr;
+			    while (arg[1])
+				arg++;
+			}
+		    }
+		} else
 		    break;
-	    /* "typeset" may take a numeric argument *
-	     * at the tail of the options            */
-	    if (idigit(*arg) && (flags & BINF_TYPEOPT) &&
-		strchr(typenumstr, arg[-1]))
-		auxlen = (int)zstrtol(arg, &arg, 10);
+	    }
 	    /* The above loop may have exited on an invalid option.  (We  *
 	     * assume that any option requiring metafication is invalid.) */
 	    if (*arg) {
@@ -299,41 +361,24 @@ execbuiltin(LinkList args, Builtin bn)
 	    }
 	    arg = (char *) ugetnode(args);
 	    /* for the "print" builtin, the options after -R are treated as
-	       options to "echo" and -f takes an extra argument */
-	    if (flags & BINF_PRINTOPTS) {
-		if (ops['R'] && !ops['f']) {
-		    optstr = "ne";
-		    flags |= BINF_ECHOPTS;
-		} else if (execop == 'f') {
-		    if (!arg) {
-			zwarnnam(name, "-f: format argument expected", NULL, 0);
-			return 1;
-		    }
-		    auxdata = arg;
-		    arg = (char *) ugetnode(args);
-		}
+	       options to "echo" */
+	    if ((flags & BINF_PRINTOPTS) && ops.ind['R'] && !ops.ind['f']) {
+		optstr = "ne";
+		flags |= BINF_ECHOPTS;
 	    }
 	    /* the option -- indicates the end of the options */
-	    if (ops['-'])
+	    if (ops.ind['-'])
 		break;
-	    /* for "fc", -e takes an extra argument */
-	    if ((flags & BINF_FCOPTS) && execop == 'e') {
-		auxdata = arg;
-		arg = (char *) ugetnode(args);
-	    }
-	    /* some "typeset" options take a numeric extra argument */
-	    if ((flags & BINF_TYPEOPT) && strchr(typenumstr, execop) &&
-		arg && idigit(*arg)) {
-		auxlen = atoi(arg);
-		arg = (char *) ugetnode(args);
-	    }
 	}
-    if (flags & BINF_R)
-	auxdata = "-";
     /* handle built-in options, for overloaded handler functions */
-    if ((pp = bn->defopts))
-	while (*pp)
-	    ops[(int)*pp++] = 1;
+    if ((pp = bn->defopts)) {
+	while (*pp) {
+	    /* only if not already set */
+	    if (!ops.ind[(int)*pp])
+		ops.ind[(int)*pp] = 1;
+	    pp++;
+	}
+    }
 
     /* Set up the argument list. */
     if (arg) {
@@ -381,7 +426,7 @@ execbuiltin(LinkList args, Builtin bn)
 	    fflush(xtrerr);
 	}
 	/* call the handler function, and return its return value */
-	return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid);
+	return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
     }
 }
 
@@ -391,7 +436,7 @@ execbuiltin(LinkList args, Builtin bn)
 
 /**/
 int
-bin_enable(char *name, char **argv, char *ops, int func)
+bin_enable(char *name, char **argv, Options ops, int func)
 {
     HashTable ht;
     HashNode hn;
@@ -401,11 +446,11 @@ bin_enable(char *name, char **argv, char *ops, int func)
     int match = 0, returnval = 0;
 
     /* Find out which hash table we are working with. */
-    if (ops['f'])
+    if (OPT_ISSET(ops,'f'))
 	ht = shfunctab;
-    else if (ops['r'])
+    else if (OPT_ISSET(ops,'r'))
 	ht = reswdtab;
-    else if (ops['a'])
+    else if (OPT_ISSET(ops,'a'))
 	ht = aliastab;
     else
 	ht = builtintab;
@@ -431,7 +476,7 @@ bin_enable(char *name, char **argv, char *ops, int func)
     }
 
     /* With -m option, treat arguments as glob patterns. */
-    if (ops['m']) {
+    if (OPT_ISSET(ops,'m')) {
 	for (; *argv; argv++) {
 	    /* parse pattern */
 	    tokenize(*argv);
@@ -471,7 +516,7 @@ bin_enable(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_set(char *nam, char **args, char *ops, int func)
+bin_set(char *nam, char **args, Options ops, int func)
 {
     int action, optno, array = 0, hadopt = 0,
 	hadplus = 0, hadend = 0, sort = 0;
@@ -590,9 +635,10 @@ int doprintdir = 0;		/* set in exec.c (for autocd) */
 
 /**/
 int
-bin_pwd(char *name, char **argv, char *ops, int func)
+bin_pwd(char *name, char **argv, Options ops, int func)
 {
-    if (ops['r'] || ops['P'] || (isset(CHASELINKS) && !ops['L']))
+    if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'P') ||
+	(isset(CHASELINKS) && !OPT_ISSET(ops,'L')))
 	printf("%s\n", zgetcwd());
     else {
 	zputs(pwd, stdout);
@@ -610,33 +656,34 @@ mod_export LinkList dirstack;
 
 /**/
 int
-bin_dirs(char *name, char **argv, char *ops, int func)
+bin_dirs(char *name, char **argv, Options ops, int func)
 {
     LinkList l;
 
     queue_signals();
     /* with -v, -p or no arguments display the directory stack */
-    if (!(*argv || ops['c']) || ops['v'] || ops ['p']) {
+    if (!(*argv || OPT_ISSET(ops,'c')) || OPT_ISSET(ops,'v') || 
+	OPT_ISSET(ops,'p')) {
 	LinkNode node;
 	char *fmt;
 	int pos = 1;
 
 	/* with the -v option, display a numbered list, starting at zero */
-	if (ops['v']) {
+	if (OPT_ISSET(ops,'v')) {
 	    printf("0\t");
 	    fmt = "\n%d\t";
 	/* with the -p option, display entries one per line */
-	} else if (ops['p'])
+	} else if (OPT_ISSET(ops,'p'))
 	    fmt = "\n";
 	else
 	    fmt = " ";
-	if (ops['l'])
+	if (OPT_ISSET(ops,'l'))
 	    fputs(pwd, stdout);
 	else
 	    fprintdir(pwd, stdout);
 	for (node = firstnode(dirstack); node; incnode(node)) {
 	    printf(fmt, pos++);
-	    if (ops['l'])
+	    if (OPT_ISSET(ops,'l'))
 		fputs(getdata(node), stdout);
 	    else
 		fprintdir(getdata(node), stdout);
@@ -704,7 +751,7 @@ static int chasinglinks;
 
 /**/
 int
-bin_cd(char *nam, char **argv, char *ops, int func)
+bin_cd(char *nam, char **argv, Options ops, int func)
 {
     LinkNode dir;
     struct stat st1, st2;
@@ -728,10 +775,11 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 		goto brk;
 	    }
 	} while (*++s);
-	for (s = *argv; *++s; ops[STOUC(*s)] = 1);
+	for (s = *argv; *++s; ops->ind[STOUC(*s)] = 1);
     }
  brk:
-    chasinglinks = ops['P'] || (isset(CHASELINKS) && !ops['L']);
+    chasinglinks = OPT_ISSET(ops,'P') || 
+	(isset(CHASELINKS) && !OPT_ISSET(ops,'L'));
     queue_signals();
     zpushnode(dirstack, ztrdup(pwd));
     if (!(dir = cd_get_dest(nam, argv, ops, func))) {
@@ -764,7 +812,7 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 
 /**/
 static LinkNode
-cd_get_dest(char *nam, char **argv, char *ops, int func)
+cd_get_dest(char *nam, char **argv, Options ops, int func)
 {
     LinkNode dir = NULL;
     LinkNode target;
@@ -834,7 +882,7 @@ cd_get_dest(char *nam, char **argv, char *ops, int func)
     if (!dir) {
 	dir = firstnode(dirstack);
     }
-    if (!(dest = cd_do_chdir(nam, getdata(dir), ops['s']))) {
+    if (!(dest = cd_do_chdir(nam, getdata(dir), OPT_ISSET(ops,'s')))) {
 	if (!target)
 	    zsfree(getlinknode(dirstack));
 	if (func == BIN_POPD)
@@ -1232,7 +1280,7 @@ printif(char *str, int c)
 
 /**/
 int
-bin_fc(char *nam, char **argv, char *ops, int func)
+bin_fc(char *nam, char **argv, Options ops, int func)
 {
     int first = -1, last = -1, retval;
     char *s;
@@ -1246,7 +1294,7 @@ bin_fc(char *nam, char **argv, char *ops, int func)
     }
     /* with the -m option, the first argument is taken *
      * as a pattern that history lines have to match   */
-    if (*argv && ops['m']) {
+    if (*argv && OPT_ISSET(ops,'m')) {
 	tokenize(*argv);
 	if (!(pprog = patcompile(*argv++, 0, NULL))) {
 	    zwarnnam(nam, "invalid match pattern", NULL, 0);
@@ -1254,21 +1302,22 @@ bin_fc(char *nam, char **argv, char *ops, int func)
 	}
     }
     queue_signals();
-    if (ops['R']) {
+    if (OPT_ISSET(ops,'R')) {
 	/* read history from a file */
-	readhistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0);
+	readhistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0);
 	unqueue_signals();
 	return 0;
     }
-    if (ops['W']) {
+    if (OPT_ISSET(ops,'W')) {
 	/* write history to a file */
-	savehistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0);
+	savehistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0);
 	unqueue_signals();
 	return 0;
     }
-    if (ops['A']) {
+    if (OPT_ISSET(ops,'A')) {
 	/* append history to a file */
-	savehistfile(*argv, 1, HFILE_APPEND | (ops['I'] ? HFILE_SKIPOLD : 0));
+	savehistfile(*argv, 1, HFILE_APPEND | 
+		     (OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0));
 	unqueue_signals();
 	return 0;
     }
@@ -1318,7 +1367,7 @@ bin_fc(char *nam, char **argv, char *ops, int func)
     }
     /* default values of first and last, and range checking */
     if (last == -1) {
-	if (ops['l'] && first < curhist) {
+	if (OPT_ISSET(ops,'l') && first < curhist) {
 	    last = addhistnum(curline.histnum,-1,0);
 	    if (last < firsthist())
 		last = firsthist();
@@ -1327,18 +1376,16 @@ bin_fc(char *nam, char **argv, char *ops, int func)
 	    last = first;
     }
     if (first == -1) {
-	first = ops['l']? addhistnum(curline.histnum,-16,0)
+	first = OPT_ISSET(ops,'l')? addhistnum(curline.histnum,-16,0)
 			: addhistnum(curline.histnum,-1,0);
 	if (first < 1)
 	    first = 1;
 	if (last < first)
 	    last = first;
     }
-    if (ops['l']) {
+    if (OPT_ISSET(ops,'l')) {
 	/* list the required part of the history */
-	retval = fclist(stdout, !ops['n'], ops['r'], ops['D'],
-			ops['d'] + ops['f'] * 2 + ops['E'] * 4 + ops['i'] * 8,
-			first, last, asgf, pprog);
+	retval = fclist(stdout, ops, first, last, asgf, pprog);
 	unqueue_signals();
     }
     else {
@@ -1355,10 +1402,16 @@ bin_fc(char *nam, char **argv, char *ops, int func)
 	    unqueue_signals();
 	    zwarnnam("fc", "can't open temp file: %e", NULL, errno);
 	} else {
-	    if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, pprog)) {
+	    ops->ind['n'] = 1;	/* No line numbers here. */
+	    if (!fclist(out, ops, first, last, asgf, pprog)) {
 		char *editor;
 
-		editor = auxdata ? auxdata : getsparam("FCEDIT");
+		if (func == BIN_R)
+		    editor = "-";
+		else if (OPT_HASARG(ops, 'e'))
+		    editor = OPT_ARG(ops, 'e');
+		else
+		    editor = getsparam("FCEDIT");
 		if (!editor)
 		    editor = DEFAULT_FCEDIT;
 
@@ -1456,17 +1509,17 @@ fcsubs(char **sp, struct asgment *sub)
 
 /**/
 static int
-fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Patprog pprog)
+fclist(FILE *f, Options ops, int first, int last, struct asgment *subs, Patprog pprog)
 {
-    int fclistdone = 0;
+    int fclistdone = 0, tmp;
     char *s;
     Histent ent;
 
     /* reverse range if required */
-    if (r) {
-	r = last;
+    if (OPT_ISSET(ops,'r')) {
+	tmp = last;
 	last = first;
-	first = r;
+	first = tmp;
     }
     /* suppress "no substitution" warning if no substitution is requested */
     if (!subs)
@@ -1489,34 +1542,33 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
 	    fclistdone |= fcsubs(&s, subs);
 
 	    /* do numbering */
-	    if (n) {
+	    if (!OPT_ISSET(ops,'n')) {
 		fprintf(f, "%5d%c ", ent->histnum,
 			ent->flags & HIST_FOREIGN? '*' : ' ');
 	    }
 	    /* output actual time (and possibly date) of execution of the
 	       command, if required */
-	    if (d) {
+	    if (OPT_ISSET(ops,'d') || OPT_ISSET(ops,'f') ||
+		OPT_ISSET(ops,'E') || OPT_ISSET(ops,'i')) {
 		struct tm *ltm;
 		ltm = localtime(&ent->stim);
-		if (d >= 2) {
-		    if (d >= 8) {
-			fprintf(f, "%d-%02d-%02d ",
-				ltm->tm_year + 1900,
-				ltm->tm_mon + 1, ltm->tm_mday);
-		    } else if (d >= 4) {
-			fprintf(f, "%d.%d.%d ",
-				ltm->tm_mday, ltm->tm_mon + 1,
-				ltm->tm_year + 1900);
-		    } else {
-			fprintf(f, "%d/%d/%d ",
-				ltm->tm_mon + 1, ltm->tm_mday,
-				ltm->tm_year + 1900);
-		    }
+		if (OPT_ISSET(ops,'i')) {
+		    fprintf(f, "%d-%02d-%02d ",
+			    ltm->tm_year + 1900,
+			    ltm->tm_mon + 1, ltm->tm_mday);
+		} else if (OPT_ISSET(ops,'E')) {
+		    fprintf(f, "%d.%d.%d ",
+			    ltm->tm_mday, ltm->tm_mon + 1,
+			    ltm->tm_year + 1900);
+		} else if (OPT_ISSET(ops,'f')) {
+		    fprintf(f, "%d/%d/%d ",
+			    ltm->tm_mon + 1, ltm->tm_mday,
+			    ltm->tm_year + 1900);
 		}
 		fprintf(f, "%02d:%02d  ", ltm->tm_hour, ltm->tm_min);
 	    }
 	    /* display the time taken by the command, if required */
-	    if (D) {
+	    if (OPT_ISSET(ops,'D')) {
 		long diff;
 		diff = (ent->ftim) ? ent->ftim - ent->stim : 0;
 		fprintf(f, "%ld:%02ld  ", diff / 60, diff % 60);
@@ -1609,10 +1661,10 @@ getasg(char *s)
 /* function to set a single parameter */
 
 /**/
-Param
+static Param
 typeset_single(char *cname, char *pname, Param pm, int func,
 	       int on, int off, int roff, char *value, Param altpm,
-	       char *ops)
+	       Options ops, int auxlen)
 {
     int usepm, tc, keeplocal = 0, newspecial = 0;
     char *subscript;
@@ -1693,9 +1745,9 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     if (usepm) {
 	on &= ~PM_LOCAL;
 	if (!on && !roff && !value) {
-	    if (ops['p'])
+	    if (OPT_ISSET(ops,'p'))
 		paramtab->printnode((HashNode)pm, PRINT_TYPESET);
-	    else if (unset(TYPESETSILENT) || ops['m'])
+	    else if (unset(TYPESETSILENT) || OPT_ISSET(ops,'m'))
 		paramtab->printnode((HashNode)pm, PRINT_INCLUDEVALUE);
 	    return pm;
 	}
@@ -1741,7 +1793,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	    return NULL;
 	}
 	pm->flags |= (on & PM_READONLY);
-	if (ops['p'])
+	if (OPT_ISSET(ops,'p'))
 	    paramtab->printnode((HashNode)pm, PRINT_TYPESET);
 	return pm;
     }
@@ -1931,7 +1983,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	return NULL;
     }
 
-    if (ops['p'])
+    if (OPT_ISSET(ops,'p'))
 	paramtab->printnode((HashNode)pm, PRINT_TYPESET);
 
     return pm;
@@ -1941,7 +1993,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 
 /**/
 int
-bin_typeset(char *name, char **argv, char *ops, int func)
+bin_typeset(char *name, char **argv, Options ops, int func)
 {
     Param pm;
     Asgment asg;
@@ -1949,20 +2001,38 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     char *optstr = TYPESET_OPTSTR;
     int on = 0, off = 0, roff, bit = PM_ARRAY;
     int i;
-    int returnval = 0, printflags = 0;
+    int returnval = 0, printflags = 0, auxlen = 0;
 
     /* hash -f is really the builtin `functions' */
-    if (ops['f'])
+    if (OPT_ISSET(ops,'f'))
 	return bin_functions(name, argv, ops, func);
 
     /* Translate the options into PM_* flags.   *
      * Unfortunately, this depends on the order *
      * these flags are defined in zsh.h         */
     for (; *optstr; optstr++, bit <<= 1)
-	if (ops[STOUC(*optstr)] == 1)
+    {
+	int optval = STOUC(*optstr);
+	if (OPT_MINUS(ops,optval))
 	    on |= bit;
-	else if (ops[STOUC(*optstr)] == 2)
+	else if (OPT_PLUS(ops,optval))
 	    off |= bit;
+	/*
+	 * There is only a single field in struct param for widths,
+	 * precisions and bases.  Until this gets fixed, we can therefore
+	 * bundle all optional arguments up into a single word.  You
+	 * may think this is very nasty, but then you should have seen the
+	 * code before option arguments were handled properly.
+	 */
+	if (OPT_HASARG(ops,optval)) {
+	    char *eptr, *arg = OPT_ARG(ops,optval);
+	    auxlen = (int)zstrtol(arg, &eptr, 10);
+	    if (*eptr) {
+		zwarnnam(name, "bad integer value: %s", arg, 0);
+		return 1;
+	    }
+	}
+    }
     roff = off;
 
     /* Sanity checks on the options.  Remove conflicting options. */
@@ -1998,13 +2068,13 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     queue_signals();
 
     /* Given no arguments, list whatever the options specify. */
-    if (ops['p'])
+    if (OPT_ISSET(ops,'p'))
 	printflags |= PRINT_TYPESET;
     if (!*argv) {
-	if (!ops['p']) {
+	if (!OPT_ISSET(ops,'p')) {
 	    if (!(on|roff))
 		printflags |= PRINT_TYPE;
-	    if (roff || ops['+'])
+	    if (roff || OPT_ISSET(ops,'+'))
 		printflags |= PRINT_NAMEONLY;
 	}
 	scanhashtable(paramtab, 1, on|roff, 0, paramtab->printnode, printflags);
@@ -2012,8 +2082,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	return 0;
     }
 
-    if (!(ops['g'] || ops['x'] || ops['m']) || ops['g'] == 2 || *name == 'l' ||
-	(!isset(GLOBALEXPORT) && !ops['g']))
+    if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) || 
+	OPT_ISSET(ops,'g') == 2 || *name == 'l' ||
+	(!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g')))
 	on |= PM_LOCAL;
 
     if (on & PM_TIED) {
@@ -2021,7 +2092,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	struct asgment asg0;
 	char *oldval = NULL;
 
-	if (ops['m']) {
+	if (OPT_ISSET(ops,'m')) {
 	    zwarnnam(name, "incompatible options for -T", NULL, 0);
 	    unqueue_signals();
 	    return 1;
@@ -2072,7 +2143,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 				 (Param)paramtab->getnode(paramtab,
 							  asg->name),
 				 func, (on | PM_ARRAY) & ~PM_EXPORTED,
-				 off, roff, asg->value, NULL, ops))) {
+				 off, roff, asg->value, NULL, ops, auxlen))) {
 	    unqueue_signals();
 	    return 1;
 	}
@@ -2084,7 +2155,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 				(Param)paramtab->getnode(paramtab,
 							 asg0.name),
 				func, on, off, roff, asg0.value, apm,
-				ops))) {
+				ops, auxlen))) {
 	    if (oldval)
 		zsfree(oldval);
 	    unsetparam_pm(apm, 1, 1);
@@ -2106,8 +2177,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     }
 
     /* With the -m option, treat arguments as glob patterns */
-    if (ops['m']) {
-	if (!ops['p']) {
+    if (OPT_ISSET(ops,'m')) {
+	if (!OPT_ISSET(ops,'p')) {
 	    if (!(on|roff))
 		printflags |= PRINT_TYPE;
 	    if (!on)
@@ -2125,7 +2196,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 		returnval = 1;
 		continue;
 	    }
-	    if (ops['m'] == 2 && !asg->value) {
+	    if (OPT_PLUS(ops,'m') == 2 && !asg->value) {
 		scanmatchtable(paramtab, pprog, on|roff, 0,
 			       paramtab->printnode, printflags);
 		continue;
@@ -2151,7 +2222,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	    for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
 		pm = (Param) getdata(pmnode);
 		if (!typeset_single(name, pm->nam, pm, func, on, off, roff,
-				    asg->value, NULL, ops))
+				    asg->value, NULL, ops, auxlen))
 		    returnval = 1;
 	    }
 	}
@@ -2166,7 +2237,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 				     gethashnode2(paramtab, asg->name) :
 				     paramtab->getnode(paramtab, asg->name)),
 			    func, on, off, roff, asg->value, NULL,
-			    ops))
+			    ops, auxlen))
 	    returnval = 1;
     }
     unqueue_signals();
@@ -2177,7 +2248,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-eval_autoload(Shfunc shf, char *name, char *ops, int func)
+eval_autoload(Shfunc shf, char *name, Options ops, int func)
 {
     if (!(shf->flags & PM_UNDEFINED))
 	return 1;
@@ -2186,7 +2257,7 @@ eval_autoload(Shfunc shf, char *name, char *ops, int func)
 	freeeprog(shf->funcdef);
 	shf->funcdef = &dummy_eprog;
     }
-    if (ops['X'] == 1) {
+    if (OPT_MINUS(ops,'X')) {
 	char *fargv[3];
 	fargv[0] = name;
 	fargv[1] = "\"$@\"";
@@ -2195,7 +2266,8 @@ eval_autoload(Shfunc shf, char *name, char *ops, int func)
 	return bin_eval(name, fargv, ops, func);
     }
 
-    return !loadautofn(shf, (ops['k'] ? 2 : (ops['z'] ? 0 : 1)), 1);
+    return !loadautofn(shf, (OPT_ISSET(ops,'k') ? 2 : 
+			     (OPT_ISSET(ops,'z') ? 0 : 1)), 1);
 }
 
 /* Display or change the attributes of shell functions.   *
@@ -2204,7 +2276,7 @@ eval_autoload(Shfunc shf, char *name, char *ops, int func)
 
 /**/
 int
-bin_functions(char *name, char **argv, char *ops, int func)
+bin_functions(char *name, char **argv, Options ops, int func)
 {
     Patprog pprog;
     Shfunc shf;
@@ -2212,27 +2284,27 @@ bin_functions(char *name, char **argv, char *ops, int func)
     int on = 0, off = 0, pflags = 0;
 
     /* Do we have any flags defined? */
-    if (ops['u'] == 2)
+    if (OPT_ISSET(ops,'u') == 2)
 	off |= PM_UNDEFINED;
-    else if (ops['u'] == 1 || ops['X'])
+    else if (OPT_MINUS(ops,'u') || OPT_ISSET(ops,'X'))
 	on |= PM_UNDEFINED;
-    if (ops['U'] == 1)
+    if (OPT_MINUS(ops,'U'))
 	on |= PM_UNALIASED|PM_UNDEFINED;
-    else if (ops['U'] == 2)
+    else if (OPT_PLUS(ops,'U'))
 	off |= PM_UNALIASED;
-    if (ops['t'] == 1)
+    if (OPT_MINUS(ops,'t'))
 	on |= PM_TAGGED;
-    else if (ops['t'] == 2)
+    else if (OPT_PLUS(ops,'t'))
 	off |= PM_TAGGED;
 
-    if ((off & PM_UNDEFINED) || (ops['k'] && ops['z']) ||
-	(ops['X'] != 2 && (ops['k'] || ops['z'])) ||
-	(ops['X'] == 1 && (ops['m'] || *argv || !scriptname))) {
+    if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
+	(!OPT_PLUS(ops,'X') && (OPT_ISSET(ops,'k') || OPT_ISSET(ops,'z'))) ||
+	(OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || *argv || !scriptname))) {
 	zwarnnam(name, "invalid option(s)", NULL, 0);
 	return 1;
     }
 
-    if (ops['f'] == 2 || ops['+'])
+    if (OPT_PLUS(ops,'f') || OPT_ISSET(ops,'+'))
 	pflags |= PRINT_NAMEONLY;
 
     /* If no arguments given, we will print functions.  If flags *
@@ -2242,7 +2314,7 @@ bin_functions(char *name, char **argv, char *ops, int func)
 	int ret = 0;
 
 	queue_signals();
-	if (ops['X'] == 1) {
+	if (OPT_MINUS(ops,'X')) {
 	    if ((shf = (Shfunc) shfunctab->getnode(shfunctab, scriptname))) {
 		DPUTS(!shf->funcdef,
 		      "BUG: Calling autoload from empty function");
@@ -2253,7 +2325,7 @@ bin_functions(char *name, char **argv, char *ops, int func)
 	    shf->flags = on;
 	    ret = eval_autoload(shf, scriptname, ops, func);
 	} else {
-	    if (ops['U'] && !ops['u'])
+	    if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
 		on &= ~PM_UNDEFINED;
 	    scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode,
 			  pflags);
@@ -2263,7 +2335,7 @@ bin_functions(char *name, char **argv, char *ops, int func)
     }
 
     /* With the -m option, treat arguments as glob patterns */
-    if (ops['m']) {
+    if (OPT_ISSET(ops,'m')) {
 	on &= ~PM_UNDEFINED;
 	for (; *argv; argv++) {
 	    /* expand argument */
@@ -2283,7 +2355,7 @@ bin_functions(char *name, char **argv, char *ops, int func)
 				!(shf->flags & DISABLED)) {
 				shf->flags = (shf->flags |
 					      (on & ~PM_UNDEFINED)) & ~off;
-				if (ops['X'] &&
+				if (OPT_ISSET(ops,'X') &&
 				    eval_autoload(shf, shf->nam, ops, func)) {
 				    returnval = 1;
 				}
@@ -2303,14 +2375,15 @@ bin_functions(char *name, char **argv, char *ops, int func)
     /* Take the arguments literally -- do not glob */
     queue_signals();
     for (; *argv; argv++) {
-	if (ops['w'])
+	if (OPT_ISSET(ops,'w'))
 	    returnval = dump_autoload(name, *argv, on, ops, func);
 	else if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) {
 	    /* if any flag was given */
 	    if (on|off) {
 		/* turn on/off the given flags */
 		shf->flags = (shf->flags | (on & ~PM_UNDEFINED)) & ~off;
-		if (ops['X'] && eval_autoload(shf, shf->nam, ops, func))
+		if (OPT_ISSET(ops,'X') && 
+		    eval_autoload(shf, shf->nam, ops, func))
 		    returnval = 1;
 	    } else
 		/* no flags, so just print */
@@ -2322,7 +2395,7 @@ bin_functions(char *name, char **argv, char *ops, int func)
 	    shf->flags = on;
 	    shf->funcdef = mkautofn(shf);
 	    shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
-	    if (ops['X'] && eval_autoload(shf, shf->nam, ops, func))
+	    if (OPT_ISSET(ops,'X') && eval_autoload(shf, shf->nam, ops, func))
 		returnval = 1;
 	} else
 	    returnval = 1;
@@ -2361,7 +2434,7 @@ mkautofn(Shfunc shf)
 
 /**/
 int
-bin_unset(char *name, char **argv, char *ops, int func)
+bin_unset(char *name, char **argv, Options ops, int func)
 {
     Param pm, next;
     Patprog pprog;
@@ -2370,11 +2443,11 @@ bin_unset(char *name, char **argv, char *ops, int func)
     int i;
 
     /* unset -f is the same as unfunction */
-    if (ops['f'])
+    if (OPT_ISSET(ops,'f'))
 	return bin_unhash(name, argv, ops, func);
 
     /* with -m option, treat arguments as glob patterns */
-    if (ops['m']) {
+    if (OPT_ISSET(ops,'m')) {
 	while ((s = *argv++)) {
 	    /* expand */
 	    tokenize(s);
@@ -2453,7 +2526,7 @@ bin_unset(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_whence(char *nam, char **argv, char *ops, int func)
+bin_whence(char *nam, char **argv, Options ops, int func)
 {
     HashNode hn;
     Patprog pprog;
@@ -2464,24 +2537,24 @@ bin_whence(char *nam, char **argv, char *ops, int func)
     char *cnam;
 
     /* Check some option information */
-    csh = ops['c'];
-    v   = ops['v'];
-    all = ops['a'];
-    wd  = ops['w'];
+    csh = OPT_ISSET(ops,'c');
+    v   = OPT_ISSET(ops,'v');
+    all = OPT_ISSET(ops,'a');
+    wd  = OPT_ISSET(ops,'w');
 
-    if (ops['w'])
+    if (OPT_ISSET(ops,'w'))
 	printflags |= PRINT_WHENCE_WORD;
-    else if (ops['c'])
+    else if (OPT_ISSET(ops,'c'))
 	printflags |= PRINT_WHENCE_CSH;
-    else if (ops['v'])
+    else if (OPT_ISSET(ops,'v'))
 	printflags |= PRINT_WHENCE_VERBOSE;
     else
 	printflags |= PRINT_WHENCE_SIMPLE;
-    if (ops['f'])
+    if (OPT_ISSET(ops,'f'))
 	printflags |= PRINT_WHENCE_FUNCDEF;
 
     /* With -m option -- treat arguments as a glob patterns */
-    if (ops['m']) {
+    if (OPT_ISSET(ops,'m')) {
 	for (; *argv; argv++) {
 	    /* parse the pattern */
 	    tokenize(*argv);
@@ -2492,7 +2565,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 		continue;
 	    }
 	    queue_signals();
-	    if (!ops['p']) {
+	    if (!OPT_ISSET(ops,'p')) {
 		/* -p option is for path search only.    *
 		 * We're not using it, so search for ... */
 
@@ -2528,7 +2601,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
     for (; *argv; argv++) {
 	informed = 0;
 
-	if (!ops['p']) {
+	if (!OPT_ISSET(ops,'p')) {
 	    /* Look for alias */
 	    if ((hn = aliastab->getnode(aliastab, *argv))) {
 		aliastab->printnode(hn, printflags);
@@ -2585,7 +2658,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 			if (v && !csh)
 			    zputs(*argv, stdout), fputs(" is ", stdout);
 			zputs(buf, stdout);
-			if (ops['s'])
+			if (OPT_ISSET(ops,'s'))
 			    print_if_link(buf);
 			fputc('\n', stdout);
 		    }
@@ -2606,7 +2679,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 		if (v && !csh)
 		    zputs(*argv, stdout), fputs(" is ", stdout);
 		zputs(cnam, stdout);
-		if (ops['s'])
+		if (OPT_ISSET(ops,'s'))
 		    print_if_link(cnam);
 		fputc('\n', stdout);
 	    }
@@ -2643,7 +2716,7 @@ bin_whence(char *nam, char **argv, char *ops, int func)
 
 /**/
 int
-bin_hash(char *name, char **argv, char *ops, int func)
+bin_hash(char *name, char **argv, Options ops, int func)
 {
     HashTable ht;
     Patprog pprog;
@@ -2651,12 +2724,12 @@ bin_hash(char *name, char **argv, char *ops, int func)
     int returnval = 0;
     int printflags = 0;
 
-    if (ops['d'])
+    if (OPT_ISSET(ops,'d'))
 	ht = nameddirtab;
     else
 	ht = cmdnamtab;
 
-    if (ops['r'] || ops['f']) {
+    if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'f')) {
 	/* -f and -r can't be used with any arguments */
 	if (*argv) {
 	    zwarnnam("hash", "too many arguments", NULL, 0);
@@ -2664,17 +2737,17 @@ bin_hash(char *name, char **argv, char *ops, int func)
 	}
 
 	/* empty the hash table */
-	if (ops['r'])
+	if (OPT_ISSET(ops,'r'))
 	    ht->emptytable(ht);
 
 	/* fill the hash table in a standard way */
-	if (ops['f'])
+	if (OPT_ISSET(ops,'f'))
 	    ht->filltable(ht);
 
 	return 0;
     }
 
-    if (ops['L']) printflags |= PRINT_LIST;
+    if (OPT_ISSET(ops,'L')) printflags |= PRINT_LIST;
 
     /* Given no arguments, display current hash table. */
     if (!*argv) {
@@ -2687,7 +2760,7 @@ bin_hash(char *name, char **argv, char *ops, int func)
     queue_signals();
     while (*argv) {
 	void *hn;
-	if (ops['m']) {
+	if (OPT_ISSET(ops,'m')) {
 	    /* with the -m option, treat the argument as a glob pattern */
 	    tokenize(*argv);  /* expand */
 	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
@@ -2705,7 +2778,7 @@ bin_hash(char *name, char **argv, char *ops, int func)
 	    } else {
 		/* The argument is of the form foo=bar, *
 		 * so define an entry for the table.    */
-		if(ops['d']) {
+		if(OPT_ISSET(ops,'d')) {
 		    Nameddir nd = hn = zcalloc(sizeof *nd);
 		    nd->flags = 0;
 		    nd->dir = ztrdup(asg->value);
@@ -2715,13 +2788,13 @@ bin_hash(char *name, char **argv, char *ops, int func)
 		    cn->u.cmd = ztrdup(asg->value);
 		}
 		ht->addnode(ht, ztrdup(asg->name), hn);
-		if(ops['v'])
+		if(OPT_ISSET(ops,'v'))
 		    ht->printnode(hn, 0);
 	    }
 	} else if (!(hn = ht->getnode2(ht, asg->name))) {
 	    /* With no `=value' part to the argument, *
 	     * work out what it ought to be.          */
-	    if(ops['d']) {
+	    if(OPT_ISSET(ops,'d')) {
 		if(!getnameddir(asg->name)) {
 		    zwarnnam(name, "no such directory name: %s", asg->name, 0);
 		    returnval = 1;
@@ -2732,9 +2805,9 @@ bin_hash(char *name, char **argv, char *ops, int func)
 		    returnval = 1;
 		}
 	    }
-	    if(ops['v'] && (hn = ht->getnode2(ht, asg->name)))
+	    if(OPT_ISSET(ops,'v') && (hn = ht->getnode2(ht, asg->name)))
 		ht->printnode(hn, 0);
-	} else if(ops['v'])
+	} else if(OPT_ISSET(ops,'v'))
 	    ht->printnode(hn, 0);
 	argv++;
     }
@@ -2746,7 +2819,7 @@ bin_hash(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_unhash(char *name, char **argv, char *ops, int func)
+bin_unhash(char *name, char **argv, Options ops, int func)
 {
     HashTable ht;
     HashNode hn, nhn;
@@ -2755,18 +2828,18 @@ bin_unhash(char *name, char **argv, char *ops, int func)
     int i;
 
     /* Check which hash table we are working with. */
-    if (ops['d'])
+    if (OPT_ISSET(ops,'d'))
 	ht = nameddirtab;	/* named directories */
-    else if (ops['f'])
+    else if (OPT_ISSET(ops,'f'))
 	ht = shfunctab;		/* shell functions   */
-    else if (ops['a'])
+    else if (OPT_ISSET(ops,'a'))
 	ht = aliastab;		/* aliases           */
     else
 	ht = cmdnamtab;		/* external commands */
 
     /* With -m option, treat arguments as glob patterns. *
      * "unhash -m '*'" is legal, but not recommended.    */
-    if (ops['m']) {
+    if (OPT_ISSET(ops,'m')) {
 	for (; *argv; argv++) {
 	    /* expand argument */
 	    tokenize(*argv);
@@ -2816,7 +2889,7 @@ bin_unhash(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_alias(char *name, char **argv, char *ops, int func)
+bin_alias(char *name, char **argv, Options ops, int func)
 {
     Alias a;
     Patprog pprog;
@@ -2826,21 +2899,22 @@ bin_alias(char *name, char **argv, char *ops, int func)
     int printflags = 0;
 
     /* Did we specify the type of alias? */
-    if (ops['r'] || ops['g']) {
-	if (ops['r'] && ops['g']) {
+    if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'g')) {
+	if (OPT_ISSET(ops,'r') && OPT_ISSET(ops,'g')) {
 	    zwarnnam(name, "illegal combination of options", NULL, 0);
 	    return 1;
 	}
 	haveflags = 1;
-	if (ops['g'])
+	if (OPT_ISSET(ops,'g'))
 	    flags1 |= ALIAS_GLOBAL;
 	else
 	    flags2 |= ALIAS_GLOBAL;
     }
 
-    if (ops['L'])
+    if (OPT_ISSET(ops,'L'))
 	printflags |= PRINT_LIST;
-    else if (ops['r'] == 2 || ops['g'] == 2 || ops['m'] == 2 || ops['+'])
+    else if (OPT_PLUS(ops,'r') || OPT_PLUS(ops,'g')|| OPT_PLUS(ops,'m') ||
+	     OPT_ISSET(ops,'+'))
 	printflags |= PRINT_NAMEONLY;
 
     /* In the absence of arguments, list all aliases.  If a command *
@@ -2854,7 +2928,7 @@ bin_alias(char *name, char **argv, char *ops, int func)
 
     /* With the -m option, treat the arguments as *
      * glob patterns of aliases to display.       */
-    if (ops['m']) {
+    if (OPT_ISSET(ops,'m')) {
 	for (; *argv; argv++) {
 	    tokenize(*argv);  /* expand argument */
 	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
@@ -2875,7 +2949,7 @@ bin_alias(char *name, char **argv, char *ops, int func)
     /* Take arguments literally.  Don't glob */
     queue_signals();
     while ((asg = getasg(*argv++))) {
-	if (asg->value && !ops['L']) {
+	if (asg->value && !OPT_ISSET(ops,'L')) {
 	    /* The argument is of the form foo=bar and we are not *
 	     * forcing a listing with -L, so define an alias      */
 	    aliastab->addnode(aliastab, ztrdup(asg->name),
@@ -2883,8 +2957,8 @@ bin_alias(char *name, char **argv, char *ops, int func)
 	} else if ((a = (Alias) aliastab->getnode(aliastab, asg->name))) {
 	    /* display alias if appropriate */
 	    if (!haveflags ||
-		(ops['r'] && !(a->flags & ALIAS_GLOBAL)) ||
-		(ops['g'] &&  (a->flags & ALIAS_GLOBAL)))
+		(OPT_ISSET(ops,'r') && !(a->flags & ALIAS_GLOBAL)) ||
+		(OPT_ISSET(ops,'g') &&  (a->flags & ALIAS_GLOBAL)))
 		aliastab->printnode((HashNode) a, printflags);
 	} else
 	    returnval = 1;
@@ -2900,7 +2974,7 @@ bin_alias(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_true(char *name, char **argv, char *ops, int func)
+bin_true(char *name, char **argv, Options ops, int func)
 {
     return 0;
 }
@@ -2909,7 +2983,7 @@ bin_true(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_false(char *name, char **argv, char *ops, int func)
+bin_false(char *name, char **argv, Options ops, int func)
 {
     return 1;
 }
@@ -2929,7 +3003,7 @@ mod_export LinkList bufstack;
 
 /**/
 int
-bin_print(char *name, char **args, char *ops, int func)
+bin_print(char *name, char **args, Options ops, int func)
 {
     int flen, width, prec, type, argc, n, narg;
     int nnl = 0, ret = 0, maxarg = 0;
@@ -2950,14 +3024,18 @@ bin_print(char *name, char **args, char *ops, int func)
     zulong zulongval;
     char *stringval;
     
-    if (func == BIN_PRINTF) auxdata = *args++;
-    if (auxdata)
-	fmt = getkeystring(auxdata, &flen, ops['b'] ? 2 : 0, &nnl);
+    if (func == BIN_PRINTF)
+	fmt = *args++;
+    else if (OPT_HASARG(ops,'f'))
+	fmt = OPT_ARG(ops,'f');
+    if (fmt)
+	fmt = getkeystring(fmt, &flen, OPT_ISSET(ops,'b') ? 2 : 0, &nnl);
+
     first = args;
     
     /* -m option -- treat the first argument as a pattern and remove
      * arguments not matching */
-    if (ops['m']) {
+    if (OPT_ISSET(ops,'m')) {
 	Patprog pprog;
 	char **t, **p;
 	
@@ -2979,13 +3057,16 @@ bin_print(char *name, char **args, char *ops, int func)
     len = (int *) hcalloc(argc * sizeof(int));
     for(n = 0; n < argc; n++) {
 	/* first \ sequences */
-	if (fmt || (!ops['e'] && (ops['R'] || ops['r'] || ops['E'])))
+	if (fmt || 
+	    (!OPT_ISSET(ops,'e') && 
+	     (OPT_ISSET(ops,'R') || OPT_ISSET(ops,'r') || OPT_ISSET(ops,'E'))))
 	    unmetafy(args[n], &len[n]);
 	else
-	    args[n] = getkeystring(args[n], &len[n], ops['b'] ? 2 :
-				   (func != BIN_ECHO && !ops['e']), &nnl);
+	    args[n] = getkeystring(args[n], &len[n], OPT_ISSET(ops,'b') ? 2 :
+				   (func != BIN_ECHO && !OPT_ISSET(ops,'e')),
+				   &nnl);
 	/* -P option -- interpret as a prompt sequence */
-	if(ops['P']) {
+	if(OPT_ISSET(ops,'P')) {
 	    /*
 	     * promptexpand uses permanent storage: to avoid
 	     * messy memory management, stick it on the heap
@@ -2997,7 +3078,7 @@ bin_print(char *name, char **args, char *ops, int func)
 	    free(str);
 	}
 	/* -D option -- interpret as a directory, and use ~ */
-	if(ops['D']) {
+	if(OPT_ISSET(ops,'D')) {
 	    Nameddir d;
 	    
 	    queue_signals();
@@ -3014,48 +3095,57 @@ bin_print(char *name, char **args, char *ops, int func)
     }
     
     /* -u and -p -- output to other than standard output */
-    if (ops['u'] || ops['p']) {
+    if (OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) {
 	int fd;
 
-	if (ops['u']) {
-	    for (fd = 0; fd < 10; fd++)
-		if (ops[fd + '0'])
-		    break;
-	    if (fd == 10)
-		fd = 0;
-	} else
+	if (OPT_ISSET(ops, 'p'))
 	    fd = coprocout;
+	else {
+	    char *argptr = OPT_ARG(ops,'u'), *eptr;
+	    /* Handle undocumented feature that -up worked */
+	    if (!strcmp(argptr, "p")) {
+		fd = coprocout;
+	    } else {
+		fd = (int)zstrtol(argptr, &eptr, 10);
+		if (*eptr) {
+		    zwarnnam(name, "number expected after -u: %s", argptr, 0);
+		    return 1;
+		}
+	    }
+	}
+
 	if ((fd = dup(fd)) < 0) {
-	    zwarnnam(name, "bad file number", NULL, 0);
+	    zwarnnam(name, "bad file number: %d", NULL, fd);
 	    return 1;
 	}
 	if ((fout = fdopen(fd, "w")) == 0) {
-	    zwarnnam(name, "bad mode on fd", NULL, 0);
+	    close(fd);
+	    zwarnnam(name, "bad mode on fd %d", NULL, fd);
 	    return 1;
 	}
     }
 
     /* -o and -O -- sort the arguments */
-    if (ops['o']) {
+    if (OPT_ISSET(ops,'o')) {
 	if (fmt && !*args) return 0;
-	if (ops['i'])
+	if (OPT_ISSET(ops,'i'))
 	    qsort(args, arrlen(args), sizeof(char *), cstrpcmp);
 	else
 	    qsort(args, arrlen(args), sizeof(char *), strpcmp);
-    } else if (ops['O']) {
+    } else if (OPT_ISSET(ops,'O')) {
 	if (fmt && !*args) return 0;
-	if (ops['i'])
+	if (OPT_ISSET(ops,'i'))
 	    qsort(args, arrlen(args), sizeof(char *), invcstrpcmp);
 	else
 	    qsort(args, arrlen(args), sizeof(char *), invstrpcmp);
     }
     /* after sorting arguments, recalculate lengths */
-    if(ops['o'] || ops['O'])
+    if(OPT_ISSET(ops,'o') || OPT_ISSET(ops,'O'))
 	for(n = 0; n < argc; n++)
 	    len[n] = strlen(args[n]);
 
     /* -c -- output in columns */
-    if (ops['c']) {
+    if (OPT_ISSET(ops,'c')) {
 	int l, nc, nr, sc, n, t, i;
 	char **ap;
 
@@ -3079,7 +3169,7 @@ bin_print(char *name, char **args, char *ops, int func)
 		    for (; l < sc; l++)
 			fputc(' ', fout);
 	    } while (*ap);
-	    fputc(ops['N'] ? '\0' : '\n', fout);
+	    fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
 	}
 	/* Testing EBADF special-cases >&- redirections */
 	if ((fout != stdout) ? (fclose(fout) != 0) :
@@ -3093,14 +3183,14 @@ bin_print(char *name, char **args, char *ops, int func)
     /* normal output */
     if (!fmt) {
 	/* -z option -- push the arguments onto the editing buffer stack */
-	if (ops['z']) {
+	if (OPT_ISSET(ops,'z')) {
 	    queue_signals();
 	    zpushnode(bufstack, sepjoin(args, NULL, 0));
 	    unqueue_signals();
 	    return 0;
 	}
 	/* -s option -- add the arguments to the history list */
-	if (ops['s']) {
+	if (OPT_ISSET(ops,'s')) {
 	    int nwords = 0, nlen, iwords;
 	    char **pargs = args;
 
@@ -3130,10 +3220,11 @@ bin_print(char *name, char **args, char *ops, int func)
 	for (; *args; args++, len++) {
 	    fwrite(*args, *len, 1, fout);
 	    if (args[1])
-		fputc(ops['l'] ? '\n' : ops['N'] ? '\0' : ' ', fout);
+		fputc(OPT_ISSET(ops,'l') ? '\n' : 
+		      OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
 	}
-	if (!(ops['n'] || nnl))
-	    fputc(ops['N'] ? '\0' : '\n', fout);
+	if (!(OPT_ISSET(ops,'n') || nnl))
+	    fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
 	/* Testing EBADF special-cases >&- redirections */
 	if ((fout != stdout) ? (fclose(fout) != 0) :
 	    (fflush(fout) != 0 && errno != EBADF)) {
@@ -3143,7 +3234,7 @@ bin_print(char *name, char **args, char *ops, int func)
 	return ret;
     }
     
-    if (ops['z'] || ops['s']) {
+    if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) {
 #ifdef HAVE_OPEN_MEMSTREAM
     	if ((fout = open_memstream(&buf, &mcount)) == NULL)
 	    zwarnnam(name, "open_memstream failed", NULL, 0);
@@ -3291,7 +3382,8 @@ bin_print(char *name, char **args, char *ops, int func)
 	    case 'b':
 		if (curarg) {
 		    int l;
-		    char *b = getkeystring(curarg, &l, ops['b'] ? 2 : 0, &nnl);
+		    char *b = getkeystring(curarg, &l, 
+					   OPT_ISSET(ops,'b') ? 2 : 0, &nnl);
 		    /* handle width/precision here and use fwrite so that
 		     * nul characters can be output */
 		    if (prec >= 0 && prec < l) l = prec;
@@ -3402,9 +3494,9 @@ bin_print(char *name, char **args, char *ops, int func)
 
     	if (maxarg) args = first + maxarg;
 	/* if there are remaining args, reuse format string */
-    } while (*args && args != first && !ops['r']);
+    } while (*args && args != first && !OPT_ISSET(ops,'r'));
 
-    if (ops['z'] || ops['s']) {
+    if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) {
 #ifdef HAVE_OPEN_MEMSTREAM
 	putc(0, fout);
 	fflush(fout);
@@ -3416,7 +3508,7 @@ bin_print(char *name, char **args, char *ops, int func)
 	buf[count] = '\0';
 #endif
 	queue_signals();
-	if (ops['z']) {
+	if (OPT_ISSET(ops,'z')) {
 	    zpushnode(bufstack, buf);
 	} else {
 	    ent = prepnexthistent();
@@ -3442,7 +3534,7 @@ bin_print(char *name, char **args, char *ops, int func)
 
 /**/
 int
-bin_shift(char *name, char **argv, char *ops, int func)
+bin_shift(char *name, char **argv, Options ops, int func)
 {
     int num = 1, l, ret = 0;
     char **s;
@@ -3493,7 +3585,7 @@ int optcind;
 
 /**/
 int
-bin_getopts(char *name, char **argv, char *ops, int func)
+bin_getopts(char *name, char **argv, Options ops, int func)
 {
     int lenstr, lenoptstr, quiet, lenoptbuf;
     char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++;
@@ -3600,7 +3692,7 @@ exit_pending;
 
 /**/
 int
-bin_break(char *name, char **argv, char *ops, int func)
+bin_break(char *name, char **argv, Options ops, int func)
 {
     int num = lastval, nump = 0;
 
@@ -3746,7 +3838,7 @@ zexit(int val, int from_where)
 
 /**/
 int
-bin_dot(char *name, char **argv, char *ops, int func)
+bin_dot(char *name, char **argv, Options ops, int func)
 {
     char **old, *old0 = NULL;
     int ret, diddot = 0, dotdot = 0;
@@ -3824,10 +3916,10 @@ bin_dot(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_emulate(char *nam, char **argv, char *ops, int func)
+bin_emulate(char *nam, char **argv, Options ops, int func)
 {
-    emulate(*argv, ops['R']);
-    if (ops['L'])
+    emulate(*argv, OPT_ISSET(ops,'R'));
+    if (OPT_ISSET(ops,'L'))
 	opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1;
     return 0;
 }
@@ -3836,7 +3928,7 @@ bin_emulate(char *nam, char **argv, char *ops, int func)
 
 /**/
 int
-bin_eval(char *nam, char **argv, char *ops, int func)
+bin_eval(char *nam, char **argv, Options ops, int func)
 {
     Eprog prog;
     char *oscriptname = scriptname;
@@ -3873,7 +3965,7 @@ file/buffer. */
 
 /**/
 int
-bin_read(char *name, char **args, char *ops, int func)
+bin_read(char *name, char **args, Options ops, int func)
 {
     char *reply, *readpmpt;
     int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash, keys = 0;
@@ -3887,26 +3979,30 @@ bin_read(char *name, char **args, char *ops, int func)
     char d;
 
 
-    if ((ops['k'] || ops['b']) && *args && idigit(**args)) {
-	if (!(nchars = atoi(*args)))
-	    nchars = 1;
-	args++;
+    if ((OPT_HASARG(ops,c='k') || OPT_HASARG(ops,c='b'))) {
+	char *eptr, *optarg = OPT_ARG(ops,c);
+	nchars = (int)zstrtol(optarg, &eptr, 10);
+	if (*eptr) {
+	    zwarnnam(name, "number expected after -%c: %s", optarg, c);
+	    return 1;
+	}
     }
     /* This `*args++ : *args' looks a bit weird, but it works around a bug
      * in gcc-2.8.1 under DU 4.0. */
     firstarg = (*args && **args == '?' ? *args++ : *args);
-    reply = *args ? *args++ : ops['A'] ? "reply" : "REPLY";
+    reply = *args ? *args++ : OPT_ISSET(ops,'A') ? "reply" : "REPLY";
 
-    if (ops['A'] && *args) {
+    if (OPT_ISSET(ops,'A') && *args) {
 	zwarnnam(name, "only one array argument allowed", NULL, 0);
 	return 1;
     }
 
     /* handle compctl case */
-    if(ops['l'] || ops['c'])
+    if(OPT_ISSET(ops,'l') || OPT_ISSET(ops,'c'))
 	return compctlread(name, args, ops, reply);
 
-    if ((ops['k'] && !ops['u'] && !ops['p']) || ops['q']) {
+    if ((OPT_ISSET(ops,'k') && !OPT_ISSET(ops,'u') && 
+	 !OPT_ISSET(ops,'p')) || OPT_ISSET(ops,'q')) {
 	if (!zleactive) {
 	    if (SHTTY == -1) {
 		/* need to open /dev/tty specially */
@@ -3930,24 +4026,38 @@ bin_read(char *name, char **args, char *ops, int func)
 		gettyinfo(&shttyinfo);
 	    /* attach to the tty */
 	    attachtty(mypgrp);
-	    if (!isem && ops['k'])
+	    if (!isem && OPT_ISSET(ops,'k'))
 		setcbreak();
 	    readfd = SHTTY;
 	}
 	keys = 1;
-    } else if (ops['u'] && !ops['p']) {
-	/* -u means take input from the specified file descriptor. *
-	 * -up means take input from the coprocess.                */
-	for (readfd = 9; readfd && !ops[readfd + '0']; --readfd);
+    } else if (OPT_HASARG(ops,'u') && !OPT_ISSET(ops,'p')) {
+	/* -u means take input from the specified file descriptor. */
+	char *eptr, *argptr = OPT_ARG(ops,'u');
+	/* The old code handled -up, but that was never documented. Still...*/
+	if (!strcmp(argptr, "p")) {
+	    readfd = coprocin;
+	} else {
+	    readfd = (int)zstrtol(argptr, &eptr, 10);
+	    if (*eptr) {
+		zwarnnam(name, "number expected after -u: %s", argptr, 0);
+		return 1;
+	    }
+	}
+#if 0
+	/* This code is left as a warning to future generations --- pws. */
+	for (readfd = 9; readfd && !OPT_ISSET(ops,readfd + '0'); --readfd);
+#endif
 	izle = 0;
-    } else if (ops['p']) {
+    } else if (OPT_ISSET(ops,'p')) {
 	readfd = coprocin;
 	izle = 0;
     } else
 	readfd = izle = 0;
 
-    if (ops['t'] && !read_poll(readfd, &readchar, keys && !zleactive)) {
-	if (ops['k'] && !zleactive && !isem)
+    if (OPT_ISSET(ops,'t') && 
+	!read_poll(readfd, &readchar, keys && !zleactive)) {
+	if (OPT_ISSET(ops,'k') && !zleactive && !isem)
 	    settyinfo(&shttyinfo);
 	if (haso) {
 	    fclose(shout);
@@ -3956,7 +4066,7 @@ bin_read(char *name, char **args, char *ops, int func)
 	}
 	return 1;
     }
-    if (ops['s'] && SHTTY != -1) {
+    if (OPT_ISSET(ops,'s') && SHTTY != -1) {
 	struct ttyinfo ti;
 	gettyinfo(&ti);
 	saveti = ti;
@@ -3983,7 +4093,7 @@ bin_read(char *name, char **args, char *ops, int func)
     }
 
     /* option -k means read only a given number of characters (default 1) */
-    if (ops['k']) {
+    if (OPT_ISSET(ops,'k')) {
 	/* allocate buffer space for result */
 	bptr = buf = (char *)zalloc(nchars+1);
 
@@ -4010,7 +4120,7 @@ bin_read(char *name, char **args, char *ops, int func)
 	    }
 	} while (nchars > 0);
 	
-	if (!izle && !ops['u'] && !ops['p']) {
+	if (!izle && !OPT_ISSET(ops,'u') && !OPT_ISSET(ops,'p')) {
 	    /* dispose of result appropriately, etc. */
 	    if (isem)
 		while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
@@ -4025,9 +4135,9 @@ bin_read(char *name, char **args, char *ops, int func)
 	    }
 	}
 
-	if (ops['e'] || ops['E'])
+	if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E'))
 	    fwrite(buf, bptr - buf, 1, stdout);
-	if (!ops['e'])
+	if (!OPT_ISSET(ops,'e'))
 	    setsparam(reply, metafy(buf, bptr - buf, META_REALLOC));
 	else
 	    zfree(buf, bptr - buf + 1);
@@ -4037,7 +4147,7 @@ bin_read(char *name, char **args, char *ops, int func)
     }
 
     /* option -q means get one character, and interpret it as a Y or N */
-    if (ops['q']) {
+    if (OPT_ISSET(ops,'q')) {
 	char readbuf[2];
 
 	/* set up the buffer */
@@ -4058,9 +4168,9 @@ bin_read(char *name, char **args, char *ops, int func)
 		SHTTY = -1;
 	    }
 	}
-	if (ops['e'] || ops['E'])
+	if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E'))
 	    printf("%s\n", readbuf);
-	if (!ops['e'])
+	if (!OPT_ISSET(ops,'e'))
 	    setsparam(reply, ztrdup(readbuf));
 
 	if (resettty && SHTTY != -1)
@@ -4073,11 +4183,11 @@ bin_read(char *name, char **args, char *ops, int func)
        onto the last parameter.  If an array is specified, all the words become
        separate elements of the array. */
 
-    zbuforig = zbuf = (!ops['z']) ? NULL :
+    zbuforig = zbuf = (!OPT_ISSET(ops,'z')) ? NULL :
 	(nonempty(bufstack)) ? (char *) getlinknode(bufstack) : ztrdup("");
     first = 1;
     bslash = 0;
-    while (*args || (ops['A'] && !gotnl)) {
+    while (*args || (OPT_ISSET(ops,'A') && !gotnl)) {
 	sigset_t s = child_unblock();
 	buf = bptr = (char *)zalloc(bsiz = 64);
 	/* get input, a character at a time */
@@ -4108,7 +4218,7 @@ bin_read(char *name, char **args, char *ops, int func)
 		first |= !iwsep(c);
 		continue;
 	    }
-	    bslash = c == '\\' && !bslash && !ops['r'];
+	    bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r');
 	    if (bslash)
 		continue;
 	    first = 0;
@@ -4130,19 +4240,19 @@ bin_read(char *name, char **args, char *ops, int func)
 	    gotnl = 1;
 	*bptr = '\0';
 	/* dispose of word appropriately */
-	if (ops['e'] || ops['E']) {
+	if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
 	    zputs(buf, stdout);
 	    putchar('\n');
 	}
-	if (!ops['e'] && (*buf || first)) {
-	    if (ops['A']) {
+	if (!OPT_ISSET(ops,'e') && (*buf || first)) {
+	    if (OPT_ISSET(ops,'A')) {
 		addlinknode(readll, buf);
 		al++;
 	    } else
 		setsparam(reply, buf);
 	} else
 	    free(buf);
-	if (!ops['A'])
+	if (!OPT_ISSET(ops,'A'))
 	    reply = *args++;
     }
     /* handle EOF */
@@ -4154,15 +4264,15 @@ bin_read(char *name, char **args, char *ops, int func)
 	}
     }
     /* final assignment (and display) of array parameter */
-    if (ops['A']) {
+    if (OPT_ISSET(ops,'A')) {
 	char **pp, **p = NULL;
 	LinkNode n;
 
-	p = (ops['e'] ? (char **)NULL
+	p = (OPT_ISSET(ops,'e') ? (char **)NULL
 	     : (char **)zalloc((al + 1) * sizeof(char *)));
 
 	for (pp = p, n = firstnode(readll); n; incnode(n)) {
-	    if (ops['e'] || ops['E']) {
+	    if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
 		zputs((char *) getdata(n), stdout);
 		putchar('\n');
 	    }
@@ -4202,7 +4312,7 @@ bin_read(char *name, char **args, char *ops, int func)
 		    continue;
 		}
 	    }
-	    bslash = c == '\\' && !bslash && !ops['r'];
+	    bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r');
 	    if (bslash)
 		continue;
 	    if (imeta(c)) {
@@ -4226,11 +4336,11 @@ bin_read(char *name, char **args, char *ops, int func)
     if (resettty && SHTTY != -1)
 	settyinfo(&saveti);
     /* final assignment of reply, etc. */
-    if (ops['e'] || ops['E']) {
+    if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
 	zputs(buf, stdout);
 	putchar('\n');
     }
-    if (!ops['e'])
+    if (!OPT_ISSET(ops,'e'))
 	setsparam(reply, buf);
     else
 	zsfree(buf);
@@ -4345,7 +4455,7 @@ testlex(void)
 
 /**/
 int
-bin_test(char *name, char **argv, char *ops, int func)
+bin_test(char *name, char **argv, Options ops, int func)
 {
     char **s;
     Eprog prog;
@@ -4400,7 +4510,7 @@ bin_test(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_times(char *name, char **argv, char *ops, int func)
+bin_times(char *name, char **argv, Options ops, int func)
 {
     struct tms buf;
 
@@ -4422,7 +4532,7 @@ bin_times(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_trap(char *name, char **argv, char *ops, int func)
+bin_trap(char *name, char **argv, Options ops, int func)
 {
     Eprog prog;
     char *arg, *s;
@@ -4498,11 +4608,11 @@ bin_trap(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_ttyctl(char *name, char **argv, char *ops, int func)
+bin_ttyctl(char *name, char **argv, Options ops, int func)
 {
-    if (ops['f'])
+    if (OPT_ISSET(ops,'f'))
 	ttyfrozen = 1;
-    else if (ops['u'])
+    else if (OPT_ISSET(ops,'u'))
 	ttyfrozen = 0;
     else
 	printf("tty is %sfrozen\n", ttyfrozen ? "" : "not ");
@@ -4513,7 +4623,7 @@ bin_ttyctl(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_let(char *name, char **argv, char *ops, int func)
+bin_let(char *name, char **argv, Options ops, int func)
 {
     zlong val = 0;
 
@@ -4531,7 +4641,7 @@ bin_let(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_umask(char *nam, char **args, char *ops, int func)
+bin_umask(char *nam, char **args, Options ops, int func)
 {
     mode_t um;
     char *s = *args;
@@ -4541,7 +4651,7 @@ bin_umask(char *nam, char **args, char *ops, int func)
     umask(um);
     /* No arguments means to display the current setting. */
     if (!s) {
-	if (ops['S']) {
+	if (OPT_ISSET(ops,'S')) {
 	    char *who = "ugo";
 
 	    while (*who) {
@@ -4642,7 +4752,7 @@ bin_umask(char *nam, char **args, char *ops, int func)
 
 /**/
 mod_export int
-bin_notavail(char *nam, char **argv, char *ops, int func)
+bin_notavail(char *nam, char **argv, Options ops, int func)
 {
     zwarnnam(nam, "not available on this system", NULL, 0);
     return 1;
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 01abbf380..9fc7a3232 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -557,7 +557,7 @@ printhashtabinfo(HashTable ht)
 
 /**/
 int
-bin_hashinfo(char *nam, char **args, char *ops, int func)
+bin_hashinfo(char *nam, char **args, Options ops, int func)
 {
     HashTable ht;
 
diff --git a/Src/hashtable.h b/Src/hashtable.h
index aa12ad3cb..fdb9ab4fc 100644
--- a/Src/hashtable.h
+++ b/Src/hashtable.h
@@ -48,15 +48,16 @@
 #define BIN_EVAL     14
 #define BIN_SCHED    15
 #define BIN_FC       16
-#define BIN_PUSHLINE 17
-#define BIN_LOGOUT   18
-#define BIN_TEST     19
-#define BIN_BRACKET  20
-#define BIN_EXPORT   21
-#define BIN_ECHO     22
-#define BIN_DISABLE  23
-#define BIN_ENABLE   24
-#define BIN_PRINTF   25
+#define BIN_R	     17
+#define BIN_PUSHLINE 18
+#define BIN_LOGOUT   19
+#define BIN_TEST     20
+#define BIN_BRACKET  21
+#define BIN_EXPORT   22
+#define BIN_ECHO     23
+#define BIN_DISABLE  24
+#define BIN_ENABLE   25
+#define BIN_PRINTF   26
 
 /* These currently depend on being 0 and 1. */
 #define BIN_SETOPT    0
diff --git a/Src/init.c b/Src/init.c
index 1d2d8828f..c1c16873a 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1175,7 +1175,7 @@ mod_export CompctlReadFn compctlreadptr = fallback_compctlread;
 
 /**/
 mod_export int
-fallback_compctlread(char *name, char **args, char *ops, char *reply)
+fallback_compctlread(char *name, char **args, Options ops, char *reply)
 {
     zwarnnam(name, "option valid only in functions called from completion",
 	    NULL, 0);
diff --git a/Src/jobs.c b/Src/jobs.c
index a2dbea983..eb8d24450 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -1252,11 +1252,11 @@ init_hackzero(char **argv, char **envp)
 
 /**/
 int
-bin_fg(char *name, char **argv, char *ops, int func)
+bin_fg(char *name, char **argv, Options ops, int func)
 {
     int job, lng, firstjob = -1, retval = 0, ofunc = func;
 
-    if (ops['Z']) {
+    if (OPT_ISSET(ops,'Z')) {
 	int len;
 
 	if(isset(RESTRICTED)) {
@@ -1277,8 +1277,8 @@ bin_fg(char *name, char **argv, char *ops, int func)
 	return 0;
     }
 
-    lng = (ops['l']) ? 1 : (ops['p']) ? 2 : 0;
-    if (ops['d'])
+    lng = (OPT_ISSET(ops,'l')) ? 1 : (OPT_ISSET(ops,'p')) ? 2 : 0;
+    if (OPT_ISSET(ops,'d'))
 	lng |= 4;
     
     if ((func == BIN_FG || func == BIN_BG) && !jobbing) {
@@ -1327,10 +1327,11 @@ bin_fg(char *name, char **argv, char *ops, int func)
 	    }
 	    for (job = 0; job != maxjob; job++, jobptr++)
 		if (job != ignorejob && jobptr->stat) {
-		    if ((!ops['r'] && !ops['s']) ||
-			(ops['r'] && ops['s']) ||
-			(ops['r'] && !(jobptr->stat & STAT_STOPPED)) ||
-			(ops['s'] && jobptr->stat & STAT_STOPPED))
+		    if ((!OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'s')) ||
+			(OPT_ISSET(ops,'r') && OPT_ISSET(ops,'s')) ||
+			(OPT_ISSET(ops,'r') && 
+			 !(jobptr->stat & STAT_STOPPED)) ||
+			(OPT_ISSET(ops,'s') && jobptr->stat & STAT_STOPPED))
 			printjob(jobptr, lng, 2);
 		}
 	    unqueue_signals();
@@ -1498,7 +1499,7 @@ bin_fg(char *name, char **argv, char *ops, int func)
 
 /**/
 int
-bin_kill(char *nam, char **argv, char *ops, int func)
+bin_kill(char *nam, char **argv, Options ops, int func)
 {
     int sig = SIGTERM;
     int returnval = 0;
@@ -1642,10 +1643,10 @@ bin_kill(char *nam, char **argv, char *ops, int func)
 
 /**/
 int
-bin_suspend(char *name, char **argv, char *ops, int func)
+bin_suspend(char *name, char **argv, Options ops, int func)
 {
     /* won't suspend a login shell, unless forced */
-    if (islogin && !ops['f']) {
+    if (islogin && !OPT_ISSET(ops,'f')) {
 	zwarnnam(name, "can't suspend login shell", NULL, 0);
 	return 1;
     }
diff --git a/Src/mem.c b/Src/mem.c
index 67b4624d9..1d60fc42a 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -1238,7 +1238,7 @@ calloc(MALLOC_ARG_T n, MALLOC_ARG_T size)
 
 /**/
 int
-bin_mem(char *name, char **argv, char *ops, int func)
+bin_mem(char *name, char **argv, Options ops, int func)
 {
     int i, ii, fi, ui, j;
     struct m_hdr *m, *mf, *ms;
@@ -1246,21 +1246,21 @@ bin_mem(char *name, char **argv, char *ops, int func)
     long u = 0, f = 0, to, cu;
 
     queue_signals();
-    if (ops['v']) {
+    if (OPT_ISSET(ops,'v')) {
 	printf("The lower and the upper addresses of the heap. Diff gives\n");
 	printf("the difference between them, i.e. the size of the heap.\n\n");
     }
     printf("low mem %ld\t high mem %ld\t diff %ld\n",
 	   (long)m_l, (long)m_high, (long)(m_high - ((char *)m_l)));
 
-    if (ops['v']) {
+    if (OPT_ISSET(ops,'v')) {
 	printf("\nThe number of bytes that were allocated using sbrk() and\n");
 	printf("the number of bytes that were given back to the system\n");
 	printf("via brk().\n");
     }
     printf("\nsbrk %d\tbrk %d\n", m_s, m_b);
 
-    if (ops['v']) {
+    if (OPT_ISSET(ops,'v')) {
 	printf("\nInformation about the sizes that were allocated or freed.\n");
 	printf("For each size that were used the number of mallocs and\n");
 	printf("frees is shown. Diff gives the difference between these\n");
@@ -1282,7 +1282,7 @@ bin_mem(char *name, char **argv, char *ops, int func)
     if (m_m[i] || m_f[i])
 	printf("big\t%d\t%d\t%d\n", m_m[i], m_f[i], m_m[i] - m_f[i]);
 
-    if (ops['v']) {
+    if (OPT_ISSET(ops,'v')) {
 	printf("\nThe list of memory blocks. For each block the following\n");
 	printf("information is shown:\n\n");
 	printf("num\tthe number of this block\n");
@@ -1334,7 +1334,7 @@ bin_mem(char *name, char **argv, char *ops, int func)
 	    mf = mf->next;
     }
 
-    if (ops['v']) {
+    if (OPT_ISSET(ops,'v')) {
 	printf("\nHere is some information about the small blocks used.\n");
 	printf("For each size the arrays with the number of free and the\n");
 	printf("number of used blocks are shown.\n");
@@ -1353,7 +1353,7 @@ bin_mem(char *name, char **argv, char *ops, int func)
 	    }
 	    putchar('\n');
 	}
-    if (ops['v']) {
+    if (OPT_ISSET(ops,'v')) {
 	printf("\n\nBelow is some information about the allocation\n");
 	printf("behaviour of the zsh heaps. First the number of times\n");
 	printf("pushheap(), popheap(), and freeheap() were called.\n");
@@ -1362,7 +1362,7 @@ bin_mem(char *name, char **argv, char *ops, int func)
 
     printf("push %d\tpop %d\tfree %d\n\n", h_push, h_pop, h_free);
 
-    if (ops['v']) {
+    if (OPT_ISSET(ops,'v')) {
 	printf("\nThe next list shows for several sizes the number of times\n");
 	printf("memory of this size were taken from heaps.\n\n");
     }
diff --git a/Src/module.c b/Src/module.c
index 9cecac826..2c2e9e0f0 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -92,9 +92,9 @@ register_module(char *n, Module_func setup, Module_func boot,
 
 /**/
 static void
-printmodalias(Module m, char *ops)
+printmodalias(Module m, Options ops)
 {
-    if (ops['L']) {
+    if (OPT_ISSET(ops,'L')) {
 	printf("zmodload -A ");
 	if (m->nam[0] == '-')
 	    fputs("-- ", stdout);
@@ -956,10 +956,11 @@ autoloadscan(HashNode hn, int printflags)
 
 /**/
 int
-bin_zmodload(char *nam, char **args, char *ops, int func)
+bin_zmodload(char *nam, char **args, Options ops, int func)
 {
-    int ops_bcpf = ops['b'] || ops['c'] || ops['p'] || ops['f'];
-    int ops_au = ops['a'] || ops['u'];
+    int ops_bcpf = OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c') || 
+	OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f');
+    int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u');
     int ret = 1;
 
     if (ops_bcpf && !ops_au) {
@@ -967,41 +968,45 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 		 NULL, 0);
 	return 1;
     }
-    if (ops['A'] || ops['R']) {
-	if (ops_bcpf || ops_au || ops['d'] || (ops['R'] && ops['e'])) {
+    if (OPT_ISSET(ops,'A') || OPT_ISSET(ops,'R')) {
+	if (ops_bcpf || ops_au || OPT_ISSET(ops,'d') || 
+	    (OPT_ISSET(ops,'R') && OPT_ISSET(ops,'e'))) {
 	    zwarnnam(nam, "illegal flags combined with -A or -R", NULL, 0);
 	    return 1;
 	}
-	if (!ops['e'])
+	if (!OPT_ISSET(ops,'e'))
 	    return bin_zmodload_alias(nam, args, ops);
     }
-    if (ops['d'] && ops['a']) {
+    if (OPT_ISSET(ops,'d') && OPT_ISSET(ops,'a')) {
 	zwarnnam(nam, "-d cannot be combined with -a", NULL, 0);
 	return 1;
     }
-    if (ops['u'] && !*args) {
+    if (OPT_ISSET(ops,'u') && !*args) {
 	zwarnnam(nam, "what do you want to unload?", NULL, 0);
 	return 1;
     }
-    if (ops['e'] && (ops['I'] || ops['L'] || ops['a'] || ops['d'] ||
-		     ops['i'] || ops['u'])) {
+    if (OPT_ISSET(ops,'e') && (OPT_ISSET(ops,'I') || OPT_ISSET(ops,'L') || 
+			       OPT_ISSET(ops,'a') || OPT_ISSET(ops,'d') ||
+			       OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) {
 	zwarnnam(nam, "-e cannot be combined with other options", NULL, 0);
 	return 1;
     }
     queue_signals();
-    if (ops['e'])
+    if (OPT_ISSET(ops,'e'))
 	ret = bin_zmodload_exist(nam, args, ops);
-    else if (ops['d'])
+    else if (OPT_ISSET(ops,'d'))
 	ret = bin_zmodload_dep(nam, args, ops);
-    else if ((ops['a'] || ops['b']) && !(ops['c'] || ops['p'] || ops['f']))
+    else if ((OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b')) && 
+	     !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f')))
 	ret = bin_zmodload_auto(nam, args, ops);
-    else if (ops['c'] && !(ops['b'] || ops['p']))
+    else if (OPT_ISSET(ops,'c') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p')))
 	ret = bin_zmodload_cond(nam, args, ops);
-    else if (ops['f'] && !(ops['b'] || ops['p']))
+    else if (OPT_ISSET(ops,'f') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'p')))
 	ret = bin_zmodload_math(nam, args, ops);
-    else if (ops['p'] && !(ops['b'] || ops['c']))
+    else if (OPT_ISSET(ops,'p') && !(OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c')))
 	ret = bin_zmodload_param(nam, args, ops);
-    else if (!(ops['a'] || ops['b'] || ops['c'] || ops['p']))
+    else if (!(OPT_ISSET(ops,'a') || OPT_ISSET(ops,'b') || 
+	       OPT_ISSET(ops,'c') || OPT_ISSET(ops,'p')))
 	ret = bin_zmodload_load(nam, args, ops);
     else
 	zwarnnam(nam, "use only one of -b, -c, or -p", NULL, 0);
@@ -1012,7 +1017,7 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
-bin_zmodload_alias(char *nam, char **args, char *ops)
+bin_zmodload_alias(char *nam, char **args, Options ops)
 {
     /*
      * TODO: while it would be too nasty to have aliases, as opposed
@@ -1032,7 +1037,7 @@ bin_zmodload_alias(char *nam, char **args, char *ops)
     int ret = 0;
 
     if (!*args) {
-	if (ops['R']) {
+	if (OPT_ISSET(ops,'R')) {
 	    zwarnnam(nam, "no module alias to remove", NULL, 0);
 	    return 1;
 	}
@@ -1053,7 +1058,7 @@ bin_zmodload_alias(char *nam, char **args, char *ops)
 	    zwarnnam(nam, "invalid module name `%s'", *args, 0);
 	    return 1;
 	}
-	if (ops['R']) {
+	if (OPT_ISSET(ops,'R')) {
 	    if (aliasname) {
 		zwarnnam(nam, "bad syntax for removing module alias: %s",
 			 *args, 0);
@@ -1123,7 +1128,7 @@ bin_zmodload_alias(char *nam, char **args, char *ops)
 
 /**/
 static int
-bin_zmodload_exist(char *nam, char **args, char *ops)
+bin_zmodload_exist(char *nam, char **args, Options ops)
 {
     LinkNode node;
     Module m;
@@ -1135,7 +1140,8 @@ bin_zmodload_exist(char *nam, char **args, char *ops)
 	    modname = m->nam;
 	    if (m->flags & MOD_ALIAS) {
 		LinkNode node2;
-		if (ops['A'] && (node2 = find_module(m->u.alias, 1, NULL)))
+		if (OPT_ISSET(ops,'A') && 
+		    (node2 = find_module(m->u.alias, 1, NULL)))
 		    m = (Module) getdata(node2);
 		else
 		    continue;
@@ -1161,11 +1167,11 @@ bin_zmodload_exist(char *nam, char **args, char *ops)
 
 /**/
 static int
-bin_zmodload_dep(char *nam, char **args, char *ops)
+bin_zmodload_dep(char *nam, char **args, Options ops)
 {
     LinkNode node;
     Module m;
-    if (ops['u']) {
+    if (OPT_ISSET(ops,'u')) {
 	/* remove dependencies, which can't pertain to aliases */
 	const char *tnam = *args++;
 	node = find_module(tnam, 1, &tnam);
@@ -1201,7 +1207,7 @@ bin_zmodload_dep(char *nam, char **args, char *ops)
 	    m = (Module) getdata(node);
 	    if (m->deps && (!args[0] || !strcmp(args[0], m->nam))) {
 		LinkNode n;
-		if (ops['L']) {
+		if (OPT_ISSET(ops,'L')) {
 		    printf("zmodload -d ");
 		    if(m->nam[0] == '-')
 			fputs("-- ", stdout);
@@ -1212,7 +1218,7 @@ bin_zmodload_dep(char *nam, char **args, char *ops)
 		}
 		for (n = firstnode(m->deps); n; incnode(n)) {
 		    putchar(' ');
-		    if(ops['L'])
+		    if(OPT_ISSET(ops,'L'))
 			quotedzputs((char *) getdata(n), stdout);
 		    else
 			nicezputs((char *) getdata(n), stdout);
@@ -1234,15 +1240,15 @@ bin_zmodload_dep(char *nam, char **args, char *ops)
 
 /**/
 static int
-bin_zmodload_auto(char *nam, char **args, char *ops)
+bin_zmodload_auto(char *nam, char **args, Options ops)
 {
     int ret = 0;
-    if(ops['u']) {
+    if(OPT_ISSET(ops,'u')) {
 	/* remove autoloaded builtins */
 	for (; *args; args++) {
 	    Builtin bn = (Builtin) builtintab->getnode2(builtintab, *args);
 	    if (!bn) {
-		if(!ops['i']) {
+		if(!OPT_ISSET(ops,'i')) {
 		    zwarnnam(nam, "%s: no such builtin", *args, 0);
 		    ret = 1;
 		}
@@ -1256,7 +1262,7 @@ bin_zmodload_auto(char *nam, char **args, char *ops)
     } else if(!*args) {
 	/* list autoloaded builtins */
 	scanhashtable(builtintab, 0, 0, 0,
-	    autoloadscan, ops['L'] ? PRINT_LIST : 0);
+	    autoloadscan, OPT_ISSET(ops,'L') ? PRINT_LIST : 0);
 	return 0;
     } else {
 	/* add autoloaded builtins */
@@ -1267,7 +1273,7 @@ bin_zmodload_auto(char *nam, char **args, char *ops)
 	    if (strchr(bnam, '/')) {
 		zwarnnam(nam, "%s: `/' is illegal in a builtin", bnam, 0);
 		ret = 1;
-	    } else if (add_autobin(bnam, modnam) && !ops['i']) {
+	    } else if (add_autobin(bnam, modnam) && !OPT_ISSET(ops,'i')) {
 		zwarnnam(nam, "failed to add builtin %s", bnam, 0);
 		ret = 1;
 	    }
@@ -1278,17 +1284,17 @@ bin_zmodload_auto(char *nam, char **args, char *ops)
 
 /**/
 static int
-bin_zmodload_cond(char *nam, char **args, char *ops)
+bin_zmodload_cond(char *nam, char **args, Options ops)
 {
     int ret = 0;
 
-    if (ops['u']) {
+    if (OPT_ISSET(ops,'u')) {
 	/* remove autoloaded conditions */
 	for (; *args; args++) {
-	    Conddef cd = getconddef(ops['I'], *args, 0);
+	    Conddef cd = getconddef(OPT_ISSET(ops,'I'), *args, 0);
 
 	    if (!cd) {
-		if (!ops['i']) {
+		if (!OPT_ISSET(ops,'i')) {
 		    zwarnnam(nam, "%s: no such condition", *args, 0);
 		    ret = 1;
 		}
@@ -1305,7 +1311,7 @@ bin_zmodload_cond(char *nam, char **args, char *ops)
 
 	for (p = condtab; p; p = p->next) {
 	    if (p->module) {
-		if (ops['L']) {
+		if (OPT_ISSET(ops,'L')) {
 		    fputs("zmodload -ac", stdout);
 		    if (p->flags & CONDF_INFIX)
 			putchar('I');
@@ -1330,7 +1336,8 @@ bin_zmodload_cond(char *nam, char **args, char *ops)
 	    if (strchr(cnam, '/')) {
 		zwarnnam(nam, "%s: `/' is illegal in a condition", cnam, 0);
 		ret = 1;
-	    } else if (add_autocond(cnam, ops['I'], modnam) && !ops['i']) {
+	    } else if (add_autocond(cnam, OPT_ISSET(ops,'I'), modnam) &&
+		       !OPT_ISSET(ops,'i')) {
 		zwarnnam(nam, "failed to add condition `%s'", cnam, 0);
 		ret = 1;
 	    }
@@ -1341,17 +1348,17 @@ bin_zmodload_cond(char *nam, char **args, char *ops)
 
 /**/
 static int
-bin_zmodload_math(char *nam, char **args, char *ops)
+bin_zmodload_math(char *nam, char **args, Options ops)
 {
     int ret = 0;
 
-    if (ops['u']) {
+    if (OPT_ISSET(ops,'u')) {
 	/* remove autoloaded math functions */
 	for (; *args; args++) {
 	    MathFunc f = getmathfunc(*args, 0);
 
 	    if (!f) {
-		if (!ops['i']) {
+		if (!OPT_ISSET(ops,'i')) {
 		    zwarnnam(nam, "%s: no such math function", *args, 0);
 		    ret = 1;
 		}
@@ -1368,7 +1375,7 @@ bin_zmodload_math(char *nam, char **args, char *ops)
 
 	for (p = mathfuncs; p; p = p->next) {
 	    if (p->module) {
-		if (ops['L']) {
+		if (OPT_ISSET(ops,'L')) {
 		    fputs("zmodload -af", stdout);
 		    printf(" %s %s\n", p->module, p->name);
 		} else
@@ -1387,7 +1394,7 @@ bin_zmodload_math(char *nam, char **args, char *ops)
 		zwarnnam(nam, "%s: `/' is illegal in a math function",
 			 fnam, 0);
 		ret = 1;
-	    } else if (add_automathfunc(fnam, modnam) && !ops['i']) {
+	    } else if (add_automathfunc(fnam, modnam) && !OPT_ISSET(ops,'i')) {
 		zwarnnam(nam, "failed to add math function `%s'", fnam, 0);
 		ret = 1;
 	    }
@@ -1411,17 +1418,17 @@ printautoparams(HashNode hn, int lon)
 
 /**/
 static int
-bin_zmodload_param(char *nam, char **args, char *ops)
+bin_zmodload_param(char *nam, char **args, Options ops)
 {
     int ret = 0;
 
-    if (ops['u']) {
+    if (OPT_ISSET(ops,'u')) {
 	/* remove autoloaded parameters */
 	for (; *args; args++) {
 	    Param pm = (Param) gethashnode2(paramtab, *args);
 
 	    if (!pm) {
-		if (!ops['i']) {
+		if (!OPT_ISSET(ops,'i')) {
 		    zwarnnam(nam, "%s: no such parameter", *args, 0);
 		    ret = 1;
 		}
@@ -1433,7 +1440,7 @@ bin_zmodload_param(char *nam, char **args, char *ops)
 	}
 	return ret;
     } else if (!*args) {
-	scanhashtable(paramtab, 1, 0, 0, printautoparams, ops['L']);
+	scanhashtable(paramtab, 1, 0, 0, printautoparams, OPT_ISSET(ops,'L'));
 	return 0;
     } else {
 	/* add autoloaded parameters */
@@ -1544,12 +1551,12 @@ unload_module(Module m, LinkNode node)
 
 /**/
 static int
-bin_zmodload_load(char *nam, char **args, char *ops)
+bin_zmodload_load(char *nam, char **args, Options ops)
 {
     LinkNode node;
     Module m;
     int ret = 0;
-    if(ops['u']) {
+    if(OPT_ISSET(ops,'u')) {
 	/* unload modules */
 	const char *mname = *args;
 	for(; *args; args++) {
@@ -1579,7 +1586,7 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 		    ret = 1;
 		if (del)
 		    m->wrapper--;
-	    } else if (!ops['i']) {
+	    } else if (!OPT_ISSET(ops,'i')) {
 		zwarnnam(nam, "no such module %s", *args, 0);
 		ret = 1;
 	    }
@@ -1591,7 +1598,7 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 	for (node = firstnode(modules); node; incnode(node)) {
 	    m = (Module) getdata(node);
 	    if (m->u.handle && !(m->flags & (MOD_UNLOAD|MOD_ALIAS))) {
-		if(ops['L']) {
+		if(OPT_ISSET(ops,'L')) {
 		    printf("zmodload ");
 		    if(m->nam[0] == '-')
 			fputs("-- ", stdout);
@@ -1605,7 +1612,7 @@ bin_zmodload_load(char *nam, char **args, char *ops)
     } else {
 	/* load modules */
 	for (; *args; args++)
-	    if (!require_module(nam, *args, 1, (!ops['i'])))
+	    if (!require_module(nam, *args, 1, (!OPT_ISSET(ops,'i'))))
 		ret = 1;
 
 	return ret;
diff --git a/Src/options.c b/Src/options.c
index 7555440f6..841a3cb82 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -481,7 +481,7 @@ setoption(HashNode hn, int value)
 
 /**/
 int
-bin_setopt(char *nam, char **args, char *ops, int isun)
+bin_setopt(char *nam, char **args, Options ops, int isun)
 {
     int action, optno, match = 0;
 
diff --git a/Src/parse.c b/Src/parse.c
index 4b105d868..b53b36a0e 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -2396,24 +2396,26 @@ dump_find_func(Wordcode h, char *name)
 
 /**/
 int
-bin_zcompile(char *nam, char **args, char *ops, int func)
+bin_zcompile(char *nam, char **args, Options ops, int func)
 {
     int map, flags, ret;
     char *dump;
 
-    if ((ops['k'] && ops['z']) || (ops['R'] && ops['M']) ||
-	(ops['c'] && (ops['U'] || ops['k'] || ops['z'])) ||
-	(!(ops['c'] || ops['a']) && ops['m'])) {
+    if ((OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
+	(OPT_ISSET(ops,'R') && OPT_ISSET(ops,'M')) ||
+	(OPT_ISSET(ops,'c') &&
+	 (OPT_ISSET(ops,'U') || OPT_ISSET(ops,'k') || OPT_ISSET(ops,'z'))) ||
+	(!(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) && OPT_ISSET(ops,'m'))) {
 	zwarnnam(nam, "illegal combination of options", NULL, 0);
 	return 1;
     }
-    if ((ops['c'] || ops['a']) && isset(KSHAUTOLOAD))
+    if ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) && isset(KSHAUTOLOAD))
 	zwarnnam(nam, "functions will use zsh style autoloading", NULL, 0);
 
-    flags = (ops['k'] ? FDHF_KSHLOAD :
-	     (ops['z'] ? FDHF_ZSHLOAD : 0));
+    flags = (OPT_ISSET(ops,'k') ? FDHF_KSHLOAD :
+	     (OPT_ISSET(ops,'z') ? FDHF_ZSHLOAD : 0));
 
-    if (ops['t']) {
+    if (OPT_ISSET(ops,'t')) {
 	Wordcode f;
 
 	if (!*args) {
@@ -2443,21 +2445,23 @@ bin_zcompile(char *nam, char **args, char *ops, int func)
 	zwarnnam(nam, "too few arguments", NULL, 0);
 	return 1;
     }
-    map = (ops['M'] ? 2 : (ops['R'] ? 0 : 1));
+    map = (OPT_ISSET(ops,'M') ? 2 : (OPT_ISSET(ops,'R') ? 0 : 1));
 
-    if (!args[1] && !(ops['c'] || ops['a'])) {
+    if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) {
 	queue_signals();
-	ret = build_dump(nam, dyncat(*args, FD_EXT), args, ops['U'], map, flags);
+	ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'),
+			 map, flags);
 	unqueue_signals();
 	return ret;
     }
     dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT));
 
     queue_signals();
-    ret = ((ops['c'] || ops['a']) ?
-	   build_cur_dump(nam, dump, args + 1, ops['m'], map,
-			  (ops['c'] ? 1 : 0) | (ops['a'] ? 2 : 0)) :
-	   build_dump(nam, dump, args + 1, ops['U'], map, flags));
+    ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ?
+	   build_cur_dump(nam, dump, args + 1, OPT_ISSET(ops,'m'), map,
+			  (OPT_ISSET(ops,'c') ? 1 : 0) | 
+			  (OPT_ISSET(ops,'a') ? 2 : 0)) :
+	   build_dump(nam, dump, args + 1, OPT_ISSET(ops,'U'), map, flags));
     unqueue_signals();
 
     return ret;
@@ -3217,7 +3221,7 @@ closedumps(void)
 
 /**/
 int
-dump_autoload(char *nam, char *file, int on, char *ops, int func)
+dump_autoload(char *nam, char *file, int on, Options ops, int func)
 {
     Wordcode h;
     FDHead n, e;
@@ -3236,7 +3240,7 @@ dump_autoload(char *nam, char *file, int on, char *ops, int func)
 	shf->flags = on;
 	shf->funcdef = mkautofn(shf);
 	shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf);
-	if (ops['X'] && eval_autoload(shf, shf->nam, ops, func))
+	if (OPT_ISSET(ops,'X') && eval_autoload(shf, shf->nam, ops, func))
 	    ret = 1;
     }
     return ret;
diff --git a/Src/watch.c b/Src/watch.c
index 4f63a5bd9..e2a65207c 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -559,7 +559,7 @@ dowatch(void)
 
 /**/
 int
-bin_log(char *nam, char **argv, char *ops, int func)
+bin_log(char *nam, char **argv, Options ops, int func)
 {
     if (!watch)
 	return 1;
@@ -581,7 +581,7 @@ void dowatch(void)
 
 /**/
 int
-bin_log(char *nam, char **argv, char *ops, int func)
+bin_log(char *nam, char **argv, Options ops, int func)
 {
     return bin_notavail(nam, argv, ops, func);
 }
diff --git a/Src/zsh.h b/Src/zsh.h
index 881dd05b4..4cb87e085 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -298,6 +298,7 @@ typedef struct cmdnam    *Cmdnam;
 typedef struct shfunc    *Shfunc;
 typedef struct funcstack *Funcstack;
 typedef struct funcwrap  *FuncWrap;
+typedef struct options	 *Options;
 typedef struct builtin   *Builtin;
 typedef struct nameddir  *Nameddir;
 typedef struct module    *Module;
@@ -934,7 +935,49 @@ struct funcwrap {
 
 /* node in builtin command hash table (builtintab) */
 
-typedef int (*HandlerFunc) _((char *, char **, char *, int));
+/*
+ * Handling of options.
+ *
+ * Option strings are standard in that a trailing `:' indicates
+ * a mandatory argument.  In addtion, `::' indicates an optional
+ * argument which must immediately follow the option letter if it is present.
+ * `:%' indicates an optional numeric argument which may follow
+ * the option letter or be in the next word; the only test is
+ * that the next character is a digit, and no actual conversion is done.
+ */
+
+#define MAX_OPS 128
+
+/* Macros taking struct option * and char argument */
+/* Option was set as -X */
+#define OPT_MINUS(ops,c)	((ops)->ind[c] & 1)
+/* Option was set as +X */
+#define OPT_PLUS(ops,c)		((ops)->ind[c] & 2)
+/*
+ * Option was set any old how, maybe including an argument 
+ * (cheap test when we don't care).
+ */
+#define OPT_ISSET(ops,c)	((ops)->ind[c])
+/* Option has an argument */
+#define OPT_HASARG(ops,c)	((ops)->ind[c] > 3)
+/* The argument for the option; not safe if it doesn't have one */
+#define OPT_ARG(ops,c)		((ops)->args[((ops)->ind[c] >> 2) - 1])
+/* Ditto, but safely returns NULL if there is no argument. */
+#define OPT_ARG_SAFE(ops,c)	(OPT_HASARG(ops,c) ? OPT_ARG(ops,c) : NULL)
+
+struct options {
+    unsigned char ind[MAX_OPS];
+    char **args;
+    int argscount, argsalloc;
+};
+
+/*
+ * Handler arguments are: builtin name, null-terminated argument
+ * list excluding command name, option structure, the funcid element from the
+ * builtin structure.
+ */
+
+typedef int (*HandlerFunc) _((char *, char **, Options, int));
 #define NULLBINCMD ((HandlerFunc) 0)
 
 struct builtin {
@@ -957,22 +1000,17 @@ struct builtin {
 /* builtin flags */
 /* DISABLE IS DEFINED AS (1<<0) */
 #define BINF_PLUSOPTS		(1<<1)	/* +xyz legal */
-#define BINF_R			(1<<2)	/* this is the builtin `r' (fc -e -) */
-#define BINF_PRINTOPTS		(1<<3)
-#define BINF_ADDED		(1<<4)	/* is in the builtins hash table */
-#define BINF_FCOPTS		(1<<5)
-#define BINF_TYPEOPT		(1<<6)
-#define BINF_ECHOPTS		(1<<7)
-#define BINF_MAGICEQUALS	(1<<8)  /* needs automatic MAGIC_EQUAL_SUBST substitution */
-#define BINF_PREFIX		(1<<9)
-#define BINF_DASH		(1<<10)
-#define BINF_BUILTIN		(1<<11)
-#define BINF_COMMAND		(1<<12)
-#define BINF_EXEC		(1<<13)
-#define BINF_NOGLOB		(1<<14)
-#define BINF_PSPECIAL		(1<<15)
-
-#define BINF_TYPEOPTS   (BINF_TYPEOPT|BINF_PLUSOPTS)
+#define BINF_PRINTOPTS		(1<<2)
+#define BINF_ADDED		(1<<3)	/* is in the builtins hash table */
+#define BINF_ECHOPTS		(1<<4)
+#define BINF_MAGICEQUALS	(1<<5)  /* needs automatic MAGIC_EQUAL_SUBST substitution */
+#define BINF_PREFIX		(1<<6)
+#define BINF_DASH		(1<<7)
+#define BINF_BUILTIN		(1<<8)
+#define BINF_COMMAND		(1<<9)
+#define BINF_EXEC		(1<<10)
+#define BINF_NOGLOB		(1<<11)
+#define BINF_PSPECIAL		(1<<12)
 
 struct module {
     char *nam;
@@ -1711,7 +1749,7 @@ struct heap {
 
 /* compctl entry point pointers */
 
-typedef int (*CompctlReadFn) _((char *, char **, char *, char *));
+typedef int (*CompctlReadFn) _((char *, char **, Options, char *));
 
 /* ZLE entry point pointers */