1/*
2 * zutil.c - misc utilities
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1999 Sven Wischnowsky
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Sven Wischnowsky and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zutil.mdh"
31#include "zutil.pro"
32
33/* Style stuff. */
34
35typedef struct stypat *Stypat;
36typedef struct style *Style;
37
38/* A pattern and the styles for it. */
39
40struct style {
41    struct hashnode node;
42    Stypat pats;		/* patterns */
43};
44
45struct stypat {
46    Stypat next;
47    char *pat;			/* pattern string */
48    Patprog prog;		/* compiled pattern */
49    int weight;			/* how specific is the pattern? */
50    Eprog eval;			/* eval-on-retrieve? */
51    char **vals;
52};
53
54/* Hash table of styles and associated functions. */
55
56static HashTable zstyletab;
57
58/* Memory stuff. */
59
60static void
61freestylepatnode(Stypat p)
62{
63    zsfree(p->pat);
64    freepatprog(p->prog);
65    if (p->vals)
66	freearray(p->vals);
67    if (p->eval)
68	freeeprog(p->eval);
69    zfree(p, sizeof(*p));
70}
71
72static void
73freestylenode(HashNode hn)
74{
75    Style s = (Style) hn;
76    Stypat p, pn;
77
78    p = s->pats;
79    while (p) {
80	pn = p->next;
81	freestylepatnode(p);
82	p = pn;
83    }
84
85    zsfree(s->node.nam);
86    zfree(s, sizeof(struct style));
87}
88
89/*
90 * Free the information for one of the patterns associated with
91 * a style.
92 *
93 * If the style s is passed, prev is the previous pattern in the list,
94 * found when scanning.  We use this to update the list of patterns.
95 * If this results in their being no remaining patterns, the style
96 * itself is removed from the list of styles.  This isn't optimised,
97 * since it's not a very frequent operation; we simply scan down the list
98 * to find the previous entry.
99 */
100static void
101freestypat(Stypat p, Style s, Stypat prev)
102{
103    if (s) {
104	if (prev)
105	    prev->next = p->next;
106	else
107	    s->pats = p->next;
108    }
109
110    freestylepatnode(p);
111
112    if (s && !s->pats) {
113	/* No patterns left, free style */
114	zstyletab->removenode(zstyletab, s->node.nam);
115	zsfree(s->node.nam);
116	zfree(s, sizeof(*s));
117    }
118}
119
120/* Pattern to match context when printing nodes */
121
122static Patprog zstyle_contprog;
123
124/*
125 * Print a node.  Print flags as shown.
126 */
127enum {
128    ZSLIST_NONE,
129    ZSLIST_BASIC,
130    ZSLIST_SYNTAX,
131};
132
133static void
134printstylenode(HashNode hn, int printflags)
135{
136    Style s = (Style)hn;
137    Stypat p;
138    char **v;
139
140    if (printflags == ZSLIST_BASIC) {
141	quotedzputs(s->node.nam, stdout);
142	putchar('\n');
143    }
144
145    for (p = s->pats; p; p = p->next) {
146	if (zstyle_contprog && !pattry(zstyle_contprog, p->pat))
147	    continue;
148	if (printflags == ZSLIST_BASIC)
149	    printf("%s  %s", (p->eval ? "(eval)" : "      "), p->pat);
150	else {
151	    printf("zstyle %s", (p->eval ? "-e " : ""));
152	    quotedzputs(p->pat, stdout);
153	    printf(" %s", s->node.nam);
154	}
155	for (v = p->vals; *v; v++) {
156	    putchar(' ');
157	    quotedzputs(*v, stdout);
158	}
159	putchar('\n');
160    }
161}
162
163/*
164 * Scan the list for a particular pattern, maybe adding matches to
165 * the link list (heap memory).  Value to be added as
166 * shown in enum
167 */
168static LinkList zstyle_list;
169static char *zstyle_patname;
170
171enum {
172    ZSPAT_NAME,		/* Add style names for matched pattern to list */
173    ZSPAT_PAT,		/* Add all patterns to list, doesn't use patname */
174    ZSPAT_REMOVE,	/* Remove matched pattern, doesn't use list */
175};
176
177static void
178scanpatstyles(HashNode hn, int spatflags)
179{
180    Style s = (Style)hn;
181    Stypat p, q;
182    LinkNode n;
183
184    for (q = NULL, p = s->pats; p; q = p, p = p->next) {
185	switch (spatflags) {
186	case ZSPAT_NAME:
187	    if (!strcmp(p->pat, zstyle_patname)) {
188		addlinknode(zstyle_list, s->node.nam);
189		return;
190	    }
191	    break;
192
193	case ZSPAT_PAT:
194	    /* Check pattern isn't already there */
195	    for (n = firstnode(zstyle_list); n; incnode(n))
196		if (!strcmp(p->pat, (char *) getdata(n)))
197		    break;
198	    if (!n)
199		addlinknode(zstyle_list, p->pat);
200	    break;
201
202	case ZSPAT_REMOVE:
203	    if (!strcmp(p->pat, zstyle_patname)) {
204		freestypat(p, s, q);
205		/*
206		 * May remove link node itself; that's OK
207		 * when scanning but we need to make sure
208		 * we don't look at it any more.
209		 */
210		return;
211	    }
212	    break;
213	}
214    }
215}
216
217
218static HashTable
219newzstyletable(int size, char const *name)
220{
221    HashTable ht;
222    ht = newhashtable(size, name, NULL);
223
224    ht->hash        = hasher;
225    ht->emptytable  = emptyhashtable;
226    ht->filltable   = NULL;
227    ht->cmpnodes    = strcmp;
228    ht->addnode     = addhashnode;
229    /* DISABLED is not supported */
230    ht->getnode     = gethashnode2;
231    ht->getnode2    = gethashnode2;
232    ht->removenode  = removehashnode;
233    ht->disablenode = NULL;
234    ht->enablenode  = NULL;
235    ht->freenode    = freestylenode;
236    ht->printnode   = printstylenode;
237
238    return ht;
239}
240
241/* Store a value for a style. */
242
243static int
244setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
245{
246    int weight, tmp, first;
247    char *str;
248    Stypat p, q, qq;
249    Eprog eprog = NULL;
250
251    if (eval) {
252	int ef = errflag;
253
254	eprog = parse_string(zjoin(vals, ' ', 1), 0);
255	errflag = ef;
256
257	if (!eprog)
258	{
259	    freepatprog(prog);
260	    return 1;
261	}
262
263	eprog = dupeprog(eprog, 0);
264    }
265    for (p = s->pats; p; p = p->next)
266	if (!strcmp(pat, p->pat)) {
267
268	    /* Exists -> replace. */
269
270	    if (p->vals)
271		freearray(p->vals);
272	    if (p->eval)
273		freeeprog(p->eval);
274	    p->vals = zarrdup(vals);
275	    p->eval = eprog;
276	    freepatprog(prog);
277
278	    return 0;
279	}
280
281    /* New pattern. */
282
283    p = (Stypat) zalloc(sizeof(*p));
284    p->pat = ztrdup(pat);
285    p->prog = prog;
286    p->vals = zarrdup(vals);
287    p->eval = eprog;
288    p->next = NULL;
289
290    /* Calculate the weight. */
291
292    for (weight = 0, tmp = 2, first = 1, str = pat; *str; str++) {
293	if (first && *str == '*' && (!str[1] || str[1] == ':')) {
294	    /* Only `*' in this component. */
295	    tmp = 0;
296	    continue;
297	}
298	first = 0;
299
300	if (*str == '(' || *str == '|' || *str == '*' || *str == '[' ||
301	    *str == '<' ||  *str == '?' || *str == '#' || *str == '^')
302	    /* Is pattern. */
303	    tmp = 1;
304
305	if (*str == ':') {
306	    /* Yet another component. */
307
308	    first = 1;
309	    weight += tmp;
310	    tmp = 2;
311	}
312    }
313    p->weight = (weight += tmp);
314
315    for (qq = NULL, q = s->pats; q && q->weight >= weight;
316	 qq = q, q = q->next);
317
318    p->next = q;
319    if (qq)
320	qq->next = p;
321    else
322	s->pats = p;
323
324    return 0;
325}
326
327/* Add a new style. */
328
329static Style
330addstyle(char *name)
331{
332    Style s = (Style) zshcalloc(sizeof(*s));
333
334    zstyletab->addnode(zstyletab, ztrdup(name), s);
335
336    return s;
337}
338
339static char **
340evalstyle(Stypat p)
341{
342    int ef = errflag;
343    char **ret, *str;
344
345    unsetparam("reply");
346    execode(p->eval, 1, 0, "style");
347    if (errflag) {
348	errflag = ef;
349	return NULL;
350    }
351    errflag = ef;
352
353    queue_signals();
354    if ((ret = getaparam("reply")))
355	ret = arrdup(ret);
356    else if ((str = getsparam("reply"))) {
357	ret = (char **) hcalloc(2 * sizeof(char *));
358	ret[0] = dupstring(str);
359    }
360    unqueue_signals();
361    unsetparam("reply");
362
363    return ret;
364}
365
366/* Look up a style for a context pattern. This does the matching. */
367
368static char **
369lookupstyle(char *ctxt, char *style)
370{
371    Style s;
372    Stypat p;
373
374    s = (Style)zstyletab->getnode2(zstyletab, style);
375    if (!s)
376	return NULL;
377    for (p = s->pats; p; p = p->next)
378	if (pattry(p->prog, ctxt))
379	    return (p->eval ? evalstyle(p) : p->vals);
380
381    return NULL;
382}
383
384static int
385bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
386{
387    int min, max, n, add = 0, list = ZSLIST_NONE, eval = 0;
388
389    if (!args[0])
390	list = ZSLIST_BASIC;
391    else if (args[0][0] == '-') {
392	char oc;
393
394	if ((oc = args[0][1]) && oc != '-') {
395	    if (args[0][2]) {
396		zwarnnam(nam, "invalid argument: %s", args[0]);
397		return 1;
398	    }
399	    if (oc == 'L') {
400		list = ZSLIST_SYNTAX;
401		args++;
402	    } else if (oc == 'e') {
403		eval = add = 1;
404		args++;
405	    }
406	} else {
407	    add = 1;
408	    args++;
409	}
410    } else
411	add = 1;
412
413    if (add) {
414	Style s;
415	Patprog prog;
416	char *pat;
417
418	if (arrlen(args) < 2) {
419	    zwarnnam(nam, "not enough arguments");
420	    return 1;
421	}
422	pat = dupstring(args[0]);
423	tokenize(pat);
424
425	if (!(prog = patcompile(pat, PAT_ZDUP, NULL))) {
426	    zwarnnam(nam, "invalid pattern: %s", args[0]);
427	    return 1;
428	}
429	if (!(s = (Style)zstyletab->getnode2(zstyletab, args[1])))
430	    s = addstyle(args[1]);
431	return setstypat(s, args[0], prog, args + 2, eval);
432    }
433    if (list) {
434	Style s;
435	char *context, *stylename;
436
437	switch (arrlen(args)) {
438	case 2:
439	    context = args[0];
440	    stylename = args[1];
441	    break;
442
443	case 1:
444	    context = args[0];
445	    stylename = NULL;
446	    break;
447
448	case 0:
449	    context = stylename = NULL;
450	    break;
451
452	default:
453	    zwarnnam(nam, "too many arguments");
454	    return 1;
455	}
456	if (context) {
457	    tokenize(context);
458	    zstyle_contprog = patcompile(context, PAT_STATIC, NULL);
459
460	    if (!zstyle_contprog)
461		return 1;
462	} else
463	    zstyle_contprog = NULL;
464
465	if (stylename) {
466	    s = (Style)zstyletab->getnode2(zstyletab, stylename);
467	    if (!s)
468		return 1;
469	    zstyletab->printnode(&s->node, list);
470	} else {
471	    scanhashtable(zstyletab, 1, 0, 0,
472			  zstyletab->printnode, list);
473	}
474
475	return 0;
476    }
477    switch (args[0][1]) {
478    case 'd': min = 0; max = -1; break;
479    case 's': min = 3; max =  4; break;
480    case 'b': min = 3; max =  3; break;
481    case 'a': min = 3; max =  3; break;
482    case 't': min = 2; max = -1; break;
483    case 'T': min = 2; max = -1; break;
484    case 'm': min = 3; max =  3; break;
485    case 'g': min = 1; max =  3; break;
486    default:
487	zwarnnam(nam, "invalid option: %s", args[0]);
488	return 1;
489    }
490    n = arrlen(args) - 1;
491    if (n < min) {
492	zwarnnam(nam, "not enough arguments");
493	return 1;
494    } else if (max >= 0 && n > max) {
495	zwarnnam(nam, "too many arguments");
496	return 1;
497    }
498    switch (args[0][1]) {
499    case 'd':
500	{
501	    Style s;
502
503	    if (args[1]) {
504		if (args[2]) {
505		    char *pat = args[1];
506
507		    for (args += 2; *args; args++) {
508			if ((s = (Style)zstyletab->getnode2(zstyletab,
509							    *args))) {
510			    Stypat p, q;
511
512			    for (q = NULL, p = s->pats; p;
513				 q = p, p = p->next) {
514				if (!strcmp(p->pat, pat)) {
515				    freestypat(p, s, q);
516				    break;
517				}
518			    }
519			}
520		    }
521		} else {
522		    zstyle_patname = args[1];
523
524		    /* sorting not needed for deletion */
525		    scanhashtable(zstyletab, 0, 0, 0, scanpatstyles,
526				  ZSPAT_REMOVE);
527		}
528	    } else
529		zstyletab->emptytable(zstyletab);
530	}
531	break;
532    case 's':
533	{
534	    char **vals, *ret;
535	    int val;
536
537	    if ((vals = lookupstyle(args[1], args[2])) && vals[0]) {
538		ret = sepjoin(vals, (args[4] ? args[4] : " "), 0);
539		val = 0;
540	    } else {
541		ret = ztrdup("");
542		val = 1;
543	    }
544	    setsparam(args[3], ret);
545
546	    return val;
547	}
548	break;
549    case 'b':
550	{
551	    char **vals, *ret;
552	    int val;
553
554	    if ((vals = lookupstyle(args[1], args[2])) &&
555		vals[0] && !vals[1] &&
556		(!strcmp(vals[0], "yes") ||
557		 !strcmp(vals[0], "true") ||
558		 !strcmp(vals[0], "on") ||
559		 !strcmp(vals[0], "1"))) {
560		ret = "yes";
561		val = 0;
562	    } else {
563		ret = "no";
564		val = 1;
565	    }
566	    setsparam(args[3], ztrdup(ret));
567
568	    return val;
569	}
570	break;
571    case 'a':
572	{
573	    char **vals, **ret;
574	    int val;
575
576	    if ((vals = lookupstyle(args[1], args[2]))) {
577		ret = zarrdup(vals);
578		val = 0;
579	    } else {
580		char *dummy = NULL;
581
582		ret = zarrdup(&dummy);
583		val = 1;
584	    }
585	    setaparam(args[3], ret);
586
587	    return val;
588	}
589	break;
590    case 't':
591    case 'T':
592	{
593	    char **vals;
594
595	    if ((vals = lookupstyle(args[1], args[2])) && vals[0]) {
596		if (args[3]) {
597		    char **ap = args + 3, **p;
598
599		    while (*ap) {
600			p = vals;
601			while (*p)
602			    if (!strcmp(*ap, *p++))
603				return 0;
604			ap++;
605		    }
606		    return 1;
607		} else
608		    return !(!strcmp(vals[0], "true") ||
609			     !strcmp(vals[0], "yes") ||
610			     !strcmp(vals[0], "on") ||
611			     !strcmp(vals[0], "1"));
612	    }
613	    return (args[0][1] == 't' ? (vals ? 1 : 2) : 0);
614	}
615	break;
616    case 'm':
617	{
618	    char **vals;
619	    Patprog prog;
620
621	    tokenize(args[3]);
622
623	    if ((vals = lookupstyle(args[1], args[2])) &&
624		(prog = patcompile(args[3], PAT_STATIC, NULL))) {
625		while (*vals)
626		    if (pattry(prog, *vals++))
627			return 0;
628	    }
629	    return 1;
630	}
631	break;
632    case 'g':
633	{
634	    int ret = 1;
635	    Style s;
636	    Stypat p;
637
638	    zstyle_list = newlinklist();
639
640	    if (args[2]) {
641		if (args[3]) {
642		    if ((s = (Style)zstyletab->getnode2(zstyletab, args[3]))) {
643			for (p = s->pats; p; p = p->next) {
644			    if (!strcmp(args[2], p->pat)) {
645				char **v = p->vals;
646
647				while (*v)
648				    addlinknode(zstyle_list, *v++);
649
650				ret = 0;
651				break;
652			    }
653			}
654		    }
655		} else {
656		    zstyle_patname = args[2];
657		    scanhashtable(zstyletab, 1, 0, 0, scanpatstyles,
658				  ZSPAT_NAME);
659		    ret = 0;
660		}
661	    } else {
662		scanhashtable(zstyletab, 1, 0, 0, scanpatstyles,
663			      ZSPAT_PAT);
664		ret = 0;
665	    }
666	    set_list_array(args[1], zstyle_list);
667
668	    return ret;
669	}
670    }
671    return 0;
672}
673
674/* Format stuff. */
675
676/*
677 * One chunk of text, to allow recursive handling of ternary
678 * expressions in zformat -f output.
679 *   instr	The input string.
680 *   specs	The format specifiers, specs[c] is the string from c:string
681 *   outp	*outp is the start of the output string
682 *   ousedp	(*outp)[*ousedp] is where to write next
683 *   olenp	*olenp is the size allocated for *outp
684 *   endchar    Terminator character in addition to `\0' (may be '\0')
685 *   skip	If 1, don't output, just parse.
686 */
687static char *zformat_substring(char* instr, char **specs, char **outp,
688			       int *ousedp, int *olenp, int endchar, int skip)
689{
690    char *s;
691
692    for (s = instr; *s && *s != endchar; s++) {
693	if (*s == '%') {
694	    int right, min = -1, max = -1, outl, testit;
695	    char *spec, *start = s;
696
697	    if ((right = (*++s == '-')))
698		s++;
699
700	    if (idigit(*s)) {
701		for (min = 0; idigit(*s); s++)
702		    min = (min * 10) + (int) STOUC(*s) - '0';
703	    }
704
705	    /* Ternary expressions */
706	    testit = (STOUC(*s) == '(');
707	    if (testit && s[1] == '-')
708	    {
709		/* Allow %(-1... etc. */
710		right = 1;
711		s++;
712	    }
713	    if ((*s == '.' || testit) && idigit(s[1])) {
714		for (max = 0, s++; idigit(*s); s++)
715		    max = (max * 10) + (int) STOUC(*s) - '0';
716	    }
717	    else if (testit)
718		s++;
719
720	    if (testit && STOUC(*s)) {
721		int actval, testval, endcharl;
722
723		/*
724		 * One one number is useful for ternary expressions.
725		 * Remember to put the sign back.
726		 */
727		testval = (min >= 0) ? min : (max >= 0) ? max : 0;
728		if (right)
729		    testval *= -1;
730
731		if (specs[STOUC(*s)])
732		    actval = (int)mathevali(specs[STOUC(*s)]);
733		else
734		    actval = 0;
735		/* zero means values are equal, i.e. true */
736		actval -= testval;
737
738		/* careful about premature end of string */
739		if (!(endcharl = *++s))
740		    return NULL;
741
742		/*
743		 * Either skip true text and output false text, or
744		 * vice versa... unless we are already skipping.
745		 */
746		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
747					    olenp, endcharl, skip || actval)))
748		    return NULL;
749		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
750					    olenp, ')', skip || !actval)))
751		    return NULL;
752	    } else if (skip) {
753		continue;
754	    } else if ((spec = specs[STOUC(*s)])) {
755		int len;
756
757		if ((len = strlen(spec)) > max && max >= 0)
758		    len = max;
759		outl = (min >= 0 ? (min > len ? min : len) : len);
760
761		if (*ousedp + outl >= *olenp) {
762		    int nlen = *olenp + outl + 128;
763		    char *tmp = (char *) zhalloc(nlen);
764
765		    memcpy(tmp, *outp, *olenp);
766		    *olenp = nlen;
767		    *outp = tmp;
768		}
769		if (len >= outl) {
770		    memcpy(*outp + *ousedp, spec, outl);
771		    *ousedp += outl;
772		} else {
773		    int diff = outl - len;
774
775		    if (right) {
776			while (diff--)
777			    (*outp)[(*ousedp)++] = ' ';
778			memcpy(*outp + *ousedp, spec, len);
779			*ousedp += len;
780		    } else {
781			memcpy(*outp + *ousedp, spec, len);
782			*ousedp += len;
783			while (diff--)
784			    (*outp)[(*ousedp)++] = ' ';
785		    }
786		}
787	    } else {
788		int len = s - start + 1;
789
790		if (*ousedp + len >= *olenp) {
791		    int nlen = *olenp + len + 128;
792		    char *tmp = (char *) zhalloc(nlen);
793
794		    memcpy(tmp, *outp, *olenp);
795		    *olenp = nlen;
796		    *outp = tmp;
797		}
798		memcpy(*outp + *ousedp, start, len);
799		*ousedp += len;
800	    }
801	} else {
802	    if (skip)
803		continue;
804	    if (*ousedp + 1 >= *olenp) {
805		char *tmp = (char *) zhalloc((*olenp) << 1);
806
807		memcpy(tmp, *outp, *olenp);
808		*olenp <<= 1;
809		*outp = tmp;
810	    }
811	    (*outp)[(*ousedp)++] = *s;
812	}
813    }
814
815    return s;
816}
817
818static int
819bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
820{
821    char opt;
822
823    if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
824	zwarnnam(nam, "invalid argument: %s", args[0]);
825	return 1;
826    }
827    args++;
828
829    switch (opt) {
830    case 'f':
831	{
832	    char **ap, *specs[256], *out;
833	    int olen, oused = 0;
834
835	    memset(specs, 0, 256 * sizeof(char *));
836
837	    specs['%'] = "%";
838	    specs[')'] = ")";
839	    for (ap = args + 2; *ap; ap++) {
840		if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' ||
841		    idigit(ap[0][0]) || ap[0][1] != ':') {
842		    zwarnnam(nam, "invalid argument: %s", *ap);
843		    return 1;
844		}
845		specs[STOUC(ap[0][0])] = ap[0] + 2;
846	    }
847	    out = (char *) zhalloc(olen = 128);
848
849	    zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0);
850	    out[oused] = '\0';
851
852	    setsparam(args[0], ztrdup(out));
853	    return 0;
854	}
855	break;
856    case 'a':
857	{
858	    char **ap, *cp;
859	    int nbc = 0, colon = 0, pre = 0, suf = 0;
860
861	    for (ap = args + 2; *ap; ap++) {
862		for (nbc = 0, cp = *ap; *cp && *cp != ':'; cp++)
863		    if (*cp == '\\' && cp[1])
864			cp++, nbc++;
865		if (*cp == ':' && cp[1]) {
866		    int d;
867
868		    colon++;
869		    if ((d = cp - *ap - nbc) > pre)
870			pre = d;
871		    if ((d = strlen(cp + 1)) > suf)
872			suf = d;
873		}
874	    }
875	    {
876		int sl = strlen(args[1]);
877		VARARR(char, buf, pre + suf + sl + 1);
878		char **ret, **rp, *copy, *cpp, oldc;
879
880		ret = (char **) zalloc((arrlen(args + 2) + 1) *
881				       sizeof(char *));
882
883		memcpy(buf + pre, args[1], sl);
884		suf = pre + sl;
885
886		for (rp = ret, ap = args + 2; *ap; ap++) {
887		    copy = dupstring(*ap);
888		    for (cp = cpp = copy; *cp && *cp != ':'; cp++) {
889			if (*cp == '\\' && cp[1])
890			    cp++;
891			*cpp++ = *cp;
892		    }
893		    oldc = *cpp;
894		    *cpp = '\0';
895		    if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1]) {
896			memset(buf, ' ', pre);
897			memcpy(buf, copy, (cpp - copy));
898			strcpy(buf + suf, cp + 1);
899			*rp++ = ztrdup(buf);
900		    } else
901			*rp++ = ztrdup(copy);
902		}
903		*rp = NULL;
904
905		setaparam(args[0], ret);
906		return 0;
907	    }
908	}
909	break;
910    }
911    zwarnnam(nam, "invalid option: -%c", opt);
912    return 1;
913}
914
915/* Zregexparse stuff. */
916
917typedef struct {
918    char **match;
919    char **mbegin;
920    char **mend;
921} MatchData;
922
923static void
924savematch(MatchData *m)
925{
926    char **a;
927
928    queue_signals();
929    a = getaparam("match");
930    m->match = a ? zarrdup(a) : NULL;
931    a = getaparam("mbegin");
932    m->mbegin = a ? zarrdup(a) : NULL;
933    a = getaparam("mend");
934    m->mend = a ? zarrdup(a) : NULL;
935    unqueue_signals();
936}
937
938static void
939restorematch(MatchData *m)
940{
941    if (m->match)
942	setaparam("match", m->match);
943    else
944	unsetparam("match");
945    if (m->mbegin)
946	setaparam("mbegin", m->mbegin);
947    else
948	unsetparam("mbegin");
949    if (m->mend)
950	setaparam("mend", m->mend);
951    else
952	unsetparam("mend");
953}
954
955static void
956freematch(MatchData *m)
957{
958    if (m->match)
959	freearray(m->match);
960    if (m->mbegin)
961	freearray(m->mbegin);
962    if (m->mend)
963	freearray(m->mend);
964}
965
966typedef struct {
967    int cutoff;
968    char *pattern;
969    Patprog patprog;
970    char *guard;
971    char *action;
972    LinkList branches;
973} RParseState;
974
975typedef struct {
976    RParseState *state;
977    LinkList actions;
978} RParseBranch;
979
980typedef struct {
981    LinkList nullacts;
982    LinkList in;
983    LinkList out;
984} RParseResult;
985
986static char **rparseargs;
987static LinkList rparsestates;
988
989static int rparsealt(RParseResult *result, jmp_buf *perr);
990
991static void
992connectstates(LinkList out, LinkList in)
993{
994    LinkNode outnode, innode, ln;
995
996    for (outnode = firstnode(out); outnode; outnode = nextnode(outnode)) {
997	RParseBranch *outbranch = getdata(outnode);
998
999	for (innode = firstnode(in); innode; innode = nextnode(innode)) {
1000	    RParseBranch *inbranch = getdata(innode);
1001	    RParseBranch *br = hcalloc(sizeof(*br));
1002
1003	    br->state = inbranch->state;
1004	    br->actions = newlinklist();
1005	    for (ln = firstnode(outbranch->actions); ln; ln = nextnode(ln))
1006		addlinknode(br->actions, getdata(ln));
1007	    for (ln = firstnode(inbranch->actions); ln; ln = nextnode(ln))
1008		addlinknode(br->actions, getdata(ln));
1009	    addlinknode(outbranch->state->branches, br);
1010	}
1011    }
1012}
1013
1014static int
1015rparseelt(RParseResult *result, jmp_buf *perr)
1016{
1017    int l;
1018    char *s = *rparseargs;
1019
1020    if (!s)
1021        return 1;
1022
1023    switch (s[0]) {
1024    case '/': {
1025	RParseState *st;
1026	RParseBranch *br;
1027	char *pattern, *lookahead;
1028	int patternlen, lookaheadlen = 0;
1029
1030	l = strlen(s);
1031	if (!((2 <= l && s[l - 1] == '/') ||
1032	      (3 <= l && s[l - 2] == '/' && (s[l - 1] == '+' ||
1033					     s[l - 1] == '-'))))
1034	    return 1;
1035	st = hcalloc(sizeof(*st));
1036	st->branches = newlinklist();
1037	st->cutoff = s[l - 1];
1038	if (s[l - 1] == '/') {
1039	    pattern = s + 1;
1040	    patternlen = l - 2;
1041	} else {
1042	    pattern = s + 1;
1043	    patternlen = l - 3;
1044	}
1045	rparseargs++;
1046	if ((s = *rparseargs) && s[0] == '%' &&
1047	   2 <= (l = strlen(s)) && s[l - 1] == '%') {
1048	    rparseargs++;
1049	    lookahead = s + 1;
1050	    lookaheadlen = l - 2;
1051	} else {
1052	    lookahead = NULL;
1053	}
1054	if (patternlen == 2 && !strncmp(pattern, "[]", 2))
1055	    st->pattern = NULL;
1056	else {
1057	    char *cp;
1058	    int l = patternlen + 12; /* (#b)((#B)...)...* */
1059	    if(lookahead)
1060	        l += lookaheadlen + 4; /* (#B)... */
1061	    cp = st->pattern = hcalloc(l);
1062	    strcpy(cp, "(#b)((#B)");
1063	    cp += 9;
1064	    strcpy(cp, pattern);
1065	    cp += patternlen;
1066	    strcpy(cp, ")");
1067	    cp += 1;
1068	    if (lookahead) {
1069		strcpy(cp, "(#B)");
1070		cp += 4;
1071		strcpy(cp, lookahead);
1072		cp += lookaheadlen;
1073	    }
1074	    strcpy(cp, "*");
1075	}
1076	st->patprog = NULL;
1077	if ((s = *rparseargs) && *s == '-') {
1078	    rparseargs++;
1079	    l = strlen(s);
1080	    st->guard = hcalloc(l);
1081	    memcpy(st->guard, s + 1, l - 1);
1082	    st->guard[l - 1] = '\0';
1083	} else
1084	    st->guard = NULL;
1085	if ((s = *rparseargs) && *s == ':') {
1086	    rparseargs++;
1087	    l = strlen(s);
1088	    st->action = hcalloc(l);
1089	    memcpy(st->action, s + 1, l - 1);
1090	    st->action[l - 1] = '\0';
1091	} else
1092	    st->action = NULL;
1093	result->nullacts = NULL;
1094	result->in = newlinklist();
1095	br = hcalloc(sizeof(*br));
1096	br->state = st;
1097	br->actions = newlinklist();
1098	addlinknode(result->in, br);
1099	result->out = newlinklist();
1100	br = hcalloc(sizeof(*br));
1101	br->state = st;
1102	br->actions = newlinklist();
1103	addlinknode(result->out, br);
1104	break;
1105    }
1106    case '(':
1107	if (s[1])
1108	    return 1;
1109	rparseargs++;
1110	if (rparsealt(result, perr))
1111	    longjmp(*perr, 2);
1112	s = *rparseargs;
1113	if (!s || s[0] != ')' || s[1] != '\0')
1114	    longjmp(*perr, 2);
1115	rparseargs++;
1116        break;
1117    default:
1118        return 1;
1119    }
1120
1121    return 0;
1122}
1123
1124static int
1125rparseclo(RParseResult *result, jmp_buf *perr)
1126{
1127    if (rparseelt(result, perr))
1128	return 1;
1129
1130    if (*rparseargs && !strcmp(*rparseargs, "#")) {
1131	rparseargs++;
1132	while (*rparseargs && !strcmp(*rparseargs, "#"))
1133	    rparseargs++;
1134
1135	connectstates(result->out, result->in);
1136	result->nullacts = newlinklist();
1137    }
1138    return 0;
1139}
1140
1141static void
1142prependactions(LinkList acts, LinkList branches)
1143{
1144    LinkNode aln, bln;
1145
1146    for (bln = firstnode(branches); bln; bln = nextnode(bln)) {
1147	RParseBranch *br = getdata(bln);
1148
1149	for (aln = lastnode(acts); aln != (LinkNode)acts; aln = prevnode(aln))
1150	    pushnode(br->actions, getdata(aln));
1151    }
1152}
1153
1154static void
1155appendactions(LinkList acts, LinkList branches)
1156{
1157    LinkNode aln, bln;
1158    for (bln = firstnode(branches); bln; bln = nextnode(bln)) {
1159	RParseBranch *br = getdata(bln);
1160
1161	for (aln = firstnode(acts); aln; aln = nextnode(aln))
1162	    addlinknode(br->actions, getdata(aln));
1163    }
1164}
1165
1166static int
1167rparseseq(RParseResult *result, jmp_buf *perr)
1168{
1169    int l;
1170    char *s;
1171    RParseResult sub;
1172
1173    result->nullacts = newlinklist();
1174    result->in = newlinklist();
1175    result->out = newlinklist();
1176
1177    while (1) {
1178	if ((s = *rparseargs) && s[0] == '{' && s[(l = strlen(s)) - 1] == '}') {
1179	    char *action = hcalloc(l - 1);
1180	    LinkNode ln;
1181
1182	    rparseargs++;
1183	    memcpy(action, s + 1, l - 2);
1184	    action[l - 2] = '\0';
1185	    if (result->nullacts)
1186		addlinknode(result->nullacts, action);
1187	    for (ln = firstnode(result->out); ln; ln = nextnode(ln)) {
1188		RParseBranch *br = getdata(ln);
1189		addlinknode(br->actions, action);
1190	    }
1191	}
1192        else if (!rparseclo(&sub, perr)) {
1193	    connectstates(result->out, sub.in);
1194
1195	    if (result->nullacts) {
1196		prependactions(result->nullacts, sub.in);
1197		insertlinklist(sub.in, lastnode(result->in), result->in);
1198	    }
1199	    if (sub.nullacts) {
1200		appendactions(sub.nullacts, result->out);
1201		insertlinklist(sub.out, lastnode(result->out), result->out);
1202	    } else
1203		result->out = sub.out;
1204
1205	    if (result->nullacts && sub.nullacts)
1206		insertlinklist(sub.nullacts, lastnode(result->nullacts),
1207			       result->nullacts);
1208	    else
1209		result->nullacts = NULL;
1210	}
1211	else
1212	    break;
1213    }
1214    return 0;
1215}
1216
1217static int
1218rparsealt(RParseResult *result, jmp_buf *perr)
1219{
1220    RParseResult sub;
1221
1222    if (rparseseq(result, perr))
1223	return 1;
1224
1225    while (*rparseargs && !strcmp(*rparseargs, "|")) {
1226	rparseargs++;
1227	if (rparseseq(&sub, perr))
1228	    longjmp(*perr, 2);
1229	if (!result->nullacts && sub.nullacts)
1230	    result->nullacts = sub.nullacts;
1231
1232	insertlinklist(sub.in, lastnode(result->in), result->in);
1233	insertlinklist(sub.out, lastnode(result->out), result->out);
1234    }
1235    return 0;
1236}
1237
1238static int
1239rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp)
1240{
1241    LinkNode ln, lnn;
1242    LinkList nexts;
1243    LinkList nextslist;
1244    RParseBranch *br;
1245    RParseState *st = NULL;
1246    int point1 = 0, point2 = 0;
1247
1248    setiparam(var1, point1);
1249    setiparam(var2, point2);
1250
1251    if (!comp && !*subj && sm->nullacts) {
1252	for (ln = firstnode(sm->nullacts); ln; ln = nextnode(ln)) {
1253	    char *action = getdata(ln);
1254
1255	    if (action)
1256		execstring(action, 1, 0, "zregexparse-action");
1257	}
1258	return 0;
1259    }
1260
1261    nextslist = newlinklist();
1262    nexts = sm->in;
1263    addlinknode(nextslist, nexts);
1264    do {
1265	MatchData match1, match2;
1266
1267	savematch(&match1);
1268
1269	for (ln = firstnode(nexts); ln; ln = nextnode(ln)) {
1270	    int i;
1271	    RParseState *next;
1272
1273	    br = getdata(ln);
1274	    next = br->state;
1275	    if (next->pattern && !next->patprog) {
1276	        tokenize(next->pattern);
1277		if (!(next->patprog = patcompile(next->pattern, 0, NULL)))
1278		    return 3;
1279	    }
1280	    if (next->pattern && pattry(next->patprog, subj) &&
1281		(!next->guard || (execstring(next->guard, 1, 0,
1282					     "zregexparse-guard"), !lastval))) {
1283		LinkNode aln;
1284		char **mend;
1285		int len;
1286
1287		queue_signals();
1288		mend = getaparam("mend");
1289		len = atoi(mend[0]);
1290		unqueue_signals();
1291
1292		for (i = len; i; i--)
1293		  if (*subj++ == Meta)
1294		    subj++;
1295
1296		savematch(&match2);
1297		restorematch(&match1);
1298
1299		for (aln = firstnode(br->actions); aln; aln = nextnode(aln)) {
1300		    char *action = getdata(aln);
1301
1302		    if (action)
1303			execstring(action, 1, 0, "zregexparse-action");
1304		}
1305		restorematch(&match2);
1306
1307		point2 += len;
1308		setiparam(var2, point2);
1309		st = br->state;
1310		nexts = st->branches;
1311		if (next->cutoff == '-' || (next->cutoff == '/' && len)) {
1312		    nextslist = newlinklist();
1313		    point1 = point2;
1314		    setiparam(var1, point1);
1315		}
1316		addlinknode(nextslist, nexts);
1317		break;
1318	    }
1319	}
1320	if (!ln)
1321	    freematch(&match1);
1322    } while (ln);
1323
1324    if (!comp && !*subj)
1325	for (ln = firstnode(sm->out); ln; ln = nextnode(ln)) {
1326	    br = getdata(ln);
1327	    if (br->state == st) {
1328		for (ln = firstnode(br->actions); ln; ln = nextnode(ln)) {
1329		    char *action = getdata(ln);
1330
1331		    if (action)
1332			execstring(action, 1, 0, "zregexparse-action");
1333		}
1334		return 0;
1335	    }
1336	}
1337
1338    for (lnn = firstnode(nextslist); lnn; lnn = nextnode(lnn)) {
1339	nexts = getdata(lnn);
1340	for (ln = firstnode(nexts); ln; ln = nextnode(ln)) {
1341	    br = getdata(ln);
1342	    if (br->state->action)
1343		execstring(br->state->action, 1, 0, "zregexparse-action");
1344	}
1345    }
1346    return empty(nexts) ? 2 : 1;
1347}
1348
1349/*
1350  usage: zregexparse [-c] var1 var2 string regex...
1351  status:
1352    0: matched
1353    1: unmatched (all next state candidates are failed)
1354    2: unmatched (there is no next state candidates)
1355    3: regex parse error
1356*/
1357
1358static int
1359bin_zregexparse(char *nam, char **args, Options ops, UNUSED(int func))
1360{
1361    int oldextendedglob = opts[EXTENDEDGLOB];
1362    char *var1 = args[0];
1363    char *var2 = args[1];
1364    char *subj = args[2];
1365    int ret;
1366    jmp_buf rparseerr;
1367    RParseResult result;
1368
1369    opts[EXTENDEDGLOB] = 1;
1370
1371    rparseargs = args + 3;
1372
1373    pushheap();
1374    rparsestates = newlinklist();
1375    if (setjmp(rparseerr) || rparsealt(&result, &rparseerr) || *rparseargs) {
1376	if (*rparseargs)
1377	    zwarnnam(nam, "invalid regex : %s", *rparseargs);
1378	else
1379	    zwarnnam(nam, "not enough regex arguments");
1380	ret = 3;
1381    } else
1382	ret = 0;
1383
1384    if (!ret)
1385	ret = rmatch(&result, subj, var1, var2, OPT_ISSET(ops,'c'));
1386    popheap();
1387
1388    opts[EXTENDEDGLOB] = oldextendedglob;
1389    return ret;
1390}
1391
1392typedef struct zoptdesc *Zoptdesc;
1393typedef struct zoptarr *Zoptarr;
1394typedef struct zoptval *Zoptval;
1395
1396struct zoptdesc {
1397    Zoptdesc next;
1398    char *name;
1399    int flags;
1400    Zoptarr arr;
1401    Zoptval vals, last;
1402};
1403
1404#define ZOF_ARG  1
1405#define ZOF_OPT  2
1406#define ZOF_MULT 4
1407#define ZOF_SAME 8
1408#define ZOF_MAP 16
1409#define ZOF_CYC 32
1410
1411struct zoptarr {
1412    Zoptarr next;
1413    char *name;
1414    Zoptval vals, last;
1415    int num;
1416};
1417
1418struct zoptval {
1419    Zoptval next, onext;
1420    char *name;
1421    char *arg;
1422    char *str;
1423};
1424
1425static Zoptdesc opt_descs;
1426static Zoptarr opt_arrs;
1427
1428static Zoptdesc
1429get_opt_desc(char *name)
1430{
1431    Zoptdesc p;
1432
1433    for (p = opt_descs; p; p = p->next)
1434	if (!strcmp(name, p->name))
1435	    return p;
1436
1437    return NULL;
1438}
1439
1440static Zoptdesc
1441lookup_opt(char *str)
1442{
1443    Zoptdesc p;
1444
1445    for (p = opt_descs; p; p = p->next) {
1446	if ((p->flags & ZOF_ARG) ? strpfx(p->name, str) : !strcmp(p->name, str))
1447	    return p;
1448    }
1449    return NULL;
1450}
1451
1452static Zoptarr
1453get_opt_arr(char *name)
1454{
1455    Zoptarr p;
1456
1457    for (p = opt_arrs; p; p = p->next)
1458	if (!strcmp(name, p->name))
1459	    return p;
1460
1461    return NULL;
1462}
1463
1464static Zoptdesc
1465map_opt_desc(Zoptdesc start)
1466{
1467    Zoptdesc map = NULL;
1468
1469    if (!start || !(start->flags & ZOF_MAP))
1470	return start;
1471
1472    map = get_opt_desc(start->arr->name);
1473
1474    if (!map)
1475	return start;
1476
1477    if (map == start) {
1478	start->flags &= ~ZOF_MAP;	/* optimize */
1479	return start;
1480    }
1481
1482    if (map->flags & ZOF_CYC)
1483	return NULL;
1484
1485    start->flags |= ZOF_CYC;
1486    map = map_opt_desc(map);
1487    start->flags &= ~ZOF_CYC;
1488
1489    return map;
1490}
1491
1492static void
1493add_opt_val(Zoptdesc d, char *arg)
1494{
1495    Zoptval v = NULL;
1496    char *n = dyncat("-", d->name);
1497    int new = 0;
1498
1499    Zoptdesc map = map_opt_desc(d);
1500    if (map)
1501	d = map;
1502
1503    if (!(d->flags & ZOF_MULT))
1504	v = d->vals;
1505    if (!v) {
1506	v = (Zoptval) zhalloc(sizeof(*v));
1507	v->next = v->onext = NULL;
1508	v->name = n;
1509	new = 1;
1510    }
1511    v->arg = arg;
1512    if ((d->flags & ZOF_ARG) && !(d->flags & (ZOF_OPT | ZOF_SAME))) {
1513	v->str = NULL;
1514	if (d->arr)
1515	    d->arr->num += (arg ? 2 : 1);
1516    } else if (arg) {
1517	char *s = (char *) zhalloc(strlen(d->name) + strlen(arg) + 2);
1518
1519	*s = '-';
1520	strcpy(s + 1, d->name);
1521	strcat(s, arg);
1522	v->str = s;
1523	if (d->arr)
1524	    d->arr->num += 1;
1525    } else {
1526	v->str = NULL;
1527	if (d->arr)
1528	    d->arr->num += 1;
1529    }
1530    if (new) {
1531	if (d->arr) {
1532	    if (d->arr->last)
1533		d->arr->last->next = v;
1534	    else
1535		d->arr->vals = v;
1536	    d->arr->last = v;
1537	}
1538	if (d->last)
1539	    d->last->onext = v;
1540	else
1541	    d->vals = v;
1542	d->last = v;
1543    }
1544}
1545
1546static int
1547bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
1548{
1549    char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL, **cp, **np;
1550    int del = 0, flags = 0, extract = 0, keep = 0;
1551    Zoptdesc sopts[256], d;
1552    Zoptarr a, defarr = NULL;
1553    Zoptval v;
1554
1555    opt_descs = NULL;
1556    opt_arrs = NULL;
1557    memset(sopts, 0, 256 * sizeof(Zoptdesc));
1558
1559    while ((o = *args++)) {
1560	if (*o == '-') {
1561	    switch (o[1]) {
1562	    case '\0':
1563		o = NULL;
1564		break;
1565	    case '-':
1566		if (o[2])
1567		    args--;
1568		/* else unreachable, default parsing removes "--" */
1569		o = NULL;
1570		break;
1571	    case 'D':
1572		if (o[2]) {
1573		    args--;
1574		    o = NULL;
1575		    break;
1576		}
1577		del = 1;
1578		break;
1579	    case 'E':
1580		if (o[2]) {
1581		    args--;
1582		    o = NULL;
1583		    break;
1584		}
1585		extract = 1;
1586		break;
1587	    case 'K':
1588		if (o[2]) {
1589		    args--;
1590		    o = NULL;
1591		    break;
1592		}
1593		keep = 1;
1594		break;
1595	    case 'M':
1596		if (o[2]) {
1597		    args--;
1598		    o = NULL;
1599		    break;
1600		}
1601		flags |= ZOF_MAP;
1602		break;
1603	    case 'a':
1604		if (defarr) {
1605		    zwarnnam(nam, "default array given more than once");
1606		    return 1;
1607		}
1608		if (o[2])
1609		    n = o + 2;
1610		else if (*args)
1611		    n = *args++;
1612		else {
1613		    zwarnnam(nam, "missing array name");
1614		    return 1;
1615		}
1616		defarr = (Zoptarr) zhalloc(sizeof(*defarr));
1617		defarr->name = n;
1618		defarr->num = 0;
1619		defarr->vals = defarr->last = NULL;
1620		defarr->next = NULL;
1621		opt_arrs = defarr;
1622		break;
1623	    case 'A':
1624		if (assoc) {
1625		    zwarnnam(nam, "associative array given more than once");
1626		    return 1;
1627		}
1628		if (o[2])
1629		    assoc = o + 2;
1630		else if (*args)
1631		    assoc = *args++;
1632		else {
1633		    zwarnnam(nam, "missing array name");
1634		    return 1;
1635		}
1636		break;
1637	    default:
1638		/* Anything else is an option description */
1639		args--;
1640		o = NULL;
1641		break;
1642	    }
1643	    if (!o) {
1644		o = "";
1645		break;
1646	    }
1647	} else {
1648	    args--;
1649	    break;
1650	}
1651    }
1652    if (!o) {
1653	zwarnnam(nam, "missing option descriptions");
1654	return 1;
1655    }
1656    while ((o = dupstring(*args++))) {
1657	int f = 0;
1658	if (!*o) {
1659	    zwarnnam(nam, "invalid option description: %s", o);
1660	    return 1;
1661	}
1662	for (p = o; *p; p++) {
1663	    if (*p == '\\' && p[1])
1664		p++;
1665	    else if (*p == '+') {
1666		f |= ZOF_MULT;
1667		*p = '\0';
1668		p++;
1669		break;
1670	    } else if (*p == ':' || *p == '=')
1671		break;
1672	}
1673	if (*p == ':') {
1674	    f |= ZOF_ARG;
1675	    *p = '\0';
1676	    if (*++p == ':') {
1677		p++;
1678		f |= ZOF_OPT;
1679	    }
1680	    if (*p == '-') {
1681		p++;
1682		f |= ZOF_SAME;
1683	    }
1684	}
1685	a = NULL;
1686	if (*p == '=') {
1687	    *p++ = '\0';
1688	    f |= flags;
1689	    if (!(a = get_opt_arr(p))) {
1690		a = (Zoptarr) zhalloc(sizeof(*a));
1691		a->name = p;
1692		a->num = 0;
1693		a->vals = a->last = NULL;
1694		a->next = opt_arrs;
1695		opt_arrs = a;
1696	    }
1697	} else if (*p) {
1698	    zwarnnam(nam, "invalid option description: %s", args[-1]);
1699	    return 1;
1700	} else if (!(a = defarr) && !assoc) {
1701	    zwarnnam(nam, "no default array defined: %s", args[-1]);
1702	    return 1;
1703	}
1704	for (p = n = o; *p; p++) {
1705	    if (*p == '\\' && p[1])
1706		p++;
1707	    *n++ = *p;
1708	}
1709	if (get_opt_desc(o)) {
1710	    zwarnnam(nam, "option defined more than once: %s", o);
1711	    return 1;
1712	}
1713	d = (Zoptdesc) zhalloc(sizeof(*d));
1714	d->name = o;
1715	d->flags = f;
1716	d->arr = a;
1717	d->next = opt_descs;
1718	d->vals = d->last = NULL;
1719	opt_descs = d;
1720	if (!o[1])
1721	    sopts[STOUC(*o)] = d;
1722	if ((flags & ZOF_MAP) && !map_opt_desc(d)) {
1723	    zwarnnam(nam, "cyclic option mapping: %s", args[-1]);
1724	    return 1;
1725	}
1726    }
1727    np = cp = pp = ((extract && del) ? arrdup(pparams) : pparams);
1728    for (; (o = *pp); pp++) {
1729	if (*o != '-') {
1730	    if (extract) {
1731		if (del)
1732		    *cp++ = o;
1733		continue;
1734	    } else
1735		break;
1736	}
1737	if (!o[1] || (o[1] == '-' && !o[2])) {
1738	    if (del && extract)
1739		*cp++ = o;
1740	    pp++;
1741	    break;
1742	}
1743	if (!(d = lookup_opt(o + 1))) {
1744	    while (*++o) {
1745		if (!(d = sopts[STOUC(*o)])) {
1746		    o = NULL;
1747		    break;
1748		}
1749		if (d->flags & ZOF_ARG) {
1750		    if (o[1]) {
1751			add_opt_val(d, o + 1);
1752			break;
1753		    } else if (!(d->flags & ZOF_OPT)) {
1754			if (!pp[1]) {
1755			    zwarnnam(nam, "missing argument for option: %s",
1756				    d->name);
1757			    return 1;
1758			}
1759			add_opt_val(d, *++pp);
1760		    } else
1761			add_opt_val(d, NULL);
1762		} else
1763		    add_opt_val(d, NULL);
1764	    }
1765	    if (!o) {
1766		if (extract) {
1767		    if (del)
1768			*cp++ = *pp;
1769		    continue;
1770		} else
1771		    break;
1772	    }
1773	} else {
1774	    if (d->flags & ZOF_ARG) {
1775		char *e = o + strlen(d->name) + 1;
1776
1777		if (*e)
1778		    add_opt_val(d, e);
1779		else if (!(d->flags & ZOF_OPT)) {
1780		    if (!pp[1]) {
1781			zwarnnam(nam, "missing argument for option: %s",
1782				d->name);
1783			return 1;
1784		    }
1785		    add_opt_val(d, *++pp);
1786		} else
1787		    add_opt_val(d, NULL);
1788	    } else
1789		add_opt_val(d, NULL);
1790	}
1791    }
1792
1793    if (flags & ZOF_MAP) {
1794	for (d = opt_descs; d; d = d->next)
1795	    if (d->arr && !d->vals && (d->flags & ZOF_MAP)) {
1796		if (d->arr->num == 0 && get_opt_desc(d->arr->name))
1797		    d->arr->num = -1;	/* this is not a real array */
1798	    }
1799    }
1800    if (extract && del)
1801	while (*pp)
1802	    *cp++ = *pp++;
1803
1804    for (a = opt_arrs; a; a = a->next) {
1805	if (a->num >= 0 && (!keep || a->num)) {
1806	    aval = (char **) zalloc((a->num + 1) * sizeof(char *));
1807	    for (ap = aval, v = a->vals; v; ap++, v = v->next) {
1808		if (v->str)
1809		    *ap = ztrdup(v->str);
1810		else {
1811		    *ap = ztrdup(v->name);
1812		    if (v->arg)
1813			*++ap = ztrdup(v->arg);
1814		}
1815	    }
1816	    *ap = NULL;
1817	    setaparam(a->name, aval);
1818	}
1819    }
1820    if (assoc) {
1821	int num;
1822
1823	for (num = 0, d = opt_descs; d; d = d->next)
1824	    if (d->vals)
1825		num++;
1826
1827	if (!keep || num) {
1828	    aval = (char **) zalloc(((num * 2) + 1) * sizeof(char *));
1829	    for (ap = aval, d = opt_descs; d; d = d->next) {
1830		if (d->vals) {
1831		    *ap++ = n = (char *) zalloc(strlen(d->name) + 2);
1832		    *n = '-';
1833		    strcpy(n + 1, d->name);
1834
1835		    for (num = 1, v = d->vals; v; v = v->onext) {
1836			num += (v->arg ? strlen(v->arg) : 0);
1837			if (v->next)
1838			    num++;
1839		    }
1840		    *ap++ = n = (char *) zalloc(num);
1841		    for (v = d->vals; v; v = v->onext) {
1842			if (v->arg) {
1843			    strcpy(n, v->arg);
1844			    n += strlen(v->arg);
1845			}
1846			*n = ' ';
1847		    }
1848		    *n = '\0';
1849		}
1850	    }
1851	    *ap = NULL;
1852	    sethparam(assoc, aval);
1853	}
1854    }
1855    if (del) {
1856	if (extract) {
1857	    *cp = NULL;
1858	    freearray(pparams);
1859	    pparams = zarrdup(np);
1860	} else {
1861	    pp = zarrdup(pp);
1862	    freearray(pparams);
1863	    pparams = pp;
1864	}
1865    }
1866    return 0;
1867}
1868
1869static struct builtin bintab[] = {
1870    BUILTIN("zformat", 0, bin_zformat, 3, -1, 0, NULL, NULL),
1871    BUILTIN("zparseopts", 0, bin_zparseopts, 1, -1, 0, NULL, NULL),
1872    BUILTIN("zregexparse", 0, bin_zregexparse, 3, -1, 0, "c", NULL),
1873    BUILTIN("zstyle", 0, bin_zstyle, 0, -1, 0, NULL, NULL),
1874};
1875
1876static struct features module_features = {
1877    bintab, sizeof(bintab)/sizeof(*bintab),
1878    NULL, 0,
1879    NULL, 0,
1880    NULL, 0,
1881    0
1882};
1883
1884/**/
1885int
1886setup_(UNUSED(Module m))
1887{
1888    zstyletab = newzstyletable(17, "zstyletab");
1889
1890    return 0;
1891}
1892
1893/**/
1894int
1895features_(Module m, char ***features)
1896{
1897    *features = featuresarray(m, &module_features);
1898    return 0;
1899}
1900
1901/**/
1902int
1903enables_(Module m, int **enables)
1904{
1905    return handlefeatures(m, &module_features, enables);
1906}
1907
1908/**/
1909int
1910boot_(Module m)
1911{
1912    return 0;
1913}
1914
1915/**/
1916int
1917cleanup_(Module m)
1918{
1919    return setfeatureenables(m, &module_features, NULL);
1920}
1921
1922/**/
1923int
1924finish_(UNUSED(Module m))
1925{
1926    deletehashtable(zstyletab);
1927
1928    return 0;
1929}
1930