1/*
2 * zle_thingy.c - thingies
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad 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 Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zle.mdh"
31#include "zle_thingy.pro"
32
33/*
34 * Thingies:
35 *
36 * From the user's point of view, a thingy is just a string.  Internally,
37 * the thingy is a struct thingy; these structures are in a hash table
38 * indexed by the string the user sees.  This hash table contains all
39 * thingies currently referenced anywhere; each has a reference count,
40 * and is deleted when it becomes unused.  Being the name of a function
41 * counts as a reference.
42 *
43 * The DISABLED flag on a thingy indicates that it is not the name of a
44 * widget.  This makes it easy to generate completion lists;
45 * looking only at the `enabled' nodes makes the thingy table look like
46 * a table of widgets.
47 */
48
49/* Hashtable of thingies.  Enabled nodes are those that refer to widgets. */
50
51/**/
52mod_export HashTable thingytab;
53
54/**********************************/
55/* hashtable management functions */
56/**********************************/
57
58/**/
59static void
60createthingytab(void)
61{
62    thingytab = newhashtable(199, "thingytab", NULL);
63
64    thingytab->hash        = hasher;
65    thingytab->emptytable  = emptythingytab;
66    thingytab->filltable   = NULL;
67    thingytab->cmpnodes    = strcmp;
68    thingytab->addnode     = addhashnode;
69    thingytab->getnode     = gethashnode;
70    thingytab->getnode2    = gethashnode2;
71    thingytab->removenode  = removehashnode;
72    thingytab->disablenode = NULL;
73    thingytab->enablenode  = NULL;
74    thingytab->freenode    = freethingynode;
75    thingytab->printnode   = NULL;
76}
77
78/**/
79static void
80emptythingytab(UNUSED(HashTable ht))
81{
82    /* This will only be called when deleting the thingy table, which *
83     * is only done to unload the zle module.  A normal emptytable()  *
84     * function would free all the thingies, but we don't want to do  *
85     * that because some of them are the known thingies in the fixed  *
86     * `thingies' table.  As the module cleanup code deletes all the  *
87     * keymaps and so on before deleting the thingy table, we can     *
88     * just remove the user-defined widgets and then be sure that     *
89     * *all* the thingies left are the fixed ones.  This has the side *
90     * effect of freeing all resources used by user-defined widgets.  */
91    scanhashtable(thingytab, 0, 0, DISABLED, scanemptythingies, 0);
92}
93
94/**/
95static void
96scanemptythingies(HashNode hn, UNUSED(int flags))
97{
98    Thingy t = (Thingy) hn;
99
100    /* Mustn't unbind internal widgets -- we wouldn't want to free the *
101     * memory they use.                                                */
102    if(!(t->widget->flags & WIDGET_INT))
103	unbindwidget(t, 1);
104}
105
106/**/
107static Thingy
108makethingynode(void)
109{
110    Thingy t = (Thingy) zshcalloc(sizeof(*t));
111
112    t->flags = DISABLED;
113    return t;
114}
115
116/**/
117static void
118freethingynode(HashNode hn)
119{
120    Thingy th = (Thingy) hn;
121
122    zsfree(th->nam);
123    zfree(th, sizeof(*th));
124}
125
126/************************/
127/* referencing thingies */
128/************************/
129
130/* It is important to maintain the reference counts on thingies.  When *
131 * copying a reference to a thingy, wrap the copy in refthingy(), to   *
132 * increase its reference count.  When removing a reference,           *
133 * unrefthingy() it.  Both of these functions handle NULL arguments    *
134 * correctly.                                                          */
135
136/**/
137mod_export Thingy
138refthingy(Thingy th)
139{
140    if(th)
141	th->rc++;
142    return th;
143}
144
145/**/
146void
147unrefthingy(Thingy th)
148{
149    if(th && !--th->rc)
150	thingytab->freenode(thingytab->removenode(thingytab, th->nam));
151}
152
153/* Use rthingy() to turn a string into a thingy.  It increases the reference *
154 * count, after creating the thingy structure if necessary.                  */
155
156/**/
157Thingy
158rthingy(char *nam)
159{
160    Thingy t = (Thingy) thingytab->getnode2(thingytab, nam);
161
162    if(!t)
163	thingytab->addnode(thingytab, ztrdup(nam), t = makethingynode());
164    return refthingy(t);
165}
166
167/**/
168Thingy
169rthingy_nocreate(char *nam)
170{
171    Thingy t = (Thingy) thingytab->getnode2(thingytab, nam);
172
173    if(!t)
174	return NULL;
175    return refthingy(t);
176}
177
178/***********/
179/* widgets */
180/***********/
181
182/*
183 * Each widget is attached to one or more thingies.  Each thingy
184 * names either zero or one widgets.  Thingies that name a widget
185 * are treated as being referenced.  The widget type, flags and pointer
186 * are stored in a separate structure pointed to by the thingies.  Each
187 * thingy also has a pointer to the `next' thingy (in a circular list)
188 * that references the same widget.  The DISABLED flag is unset in these
189 * thingies.
190 */
191
192/* Bind a widget to a thingy.  The thingy's reference count must already *
193 * have been incremented.  The widget may already be bound to other      *
194 * thingies; if it is not, then its `first' member must be NULL.  Return *
195 * is 0 on success, or -1 if the thingy has the TH_IMMORTAL flag set.    */
196
197/**/
198static int
199bindwidget(Widget w, Thingy t)
200{
201    if(t->flags & TH_IMMORTAL) {
202	unrefthingy(t);
203	return -1;
204    }
205    if(!(t->flags & DISABLED)) {
206	if(t->widget == w)
207	    return 0;
208	unbindwidget(t, 1);
209    }
210    if(w->first) {
211	t->samew = w->first->samew;
212	w->first->samew = t;
213    } else {
214	w->first = t;
215	t->samew = t;
216    }
217    t->widget = w;
218    t->flags &= ~DISABLED;
219    return 0;
220}
221
222/* Unbind a widget from a thingy.  This decrements the thingy's reference *
223 * count.  The widget will be destroyed if this is its last name.         *
224 * TH_IMMORTAL thingies won't be touched, unless override is non-zero.    *
225 * Returns 0 on success, or -1 if the thingy is protected.  If the thingy *
226 * doesn't actually reference a widget, this is considered successful.    */
227
228/**/
229static int
230unbindwidget(Thingy t, int override)
231{
232    Widget w;
233
234    if(t->flags & DISABLED)
235	return 0;
236    if(!override && (t->flags & TH_IMMORTAL))
237	return -1;
238    w = t->widget;
239    if(t->samew == t)
240	freewidget(w);
241    else {
242	Thingy p;
243	for(p = w->first; p->samew != t; p = p->samew) ;
244	w->first = p;   /* optimised for deletezlefunction() */
245	p->samew = t->samew;
246    }
247    t->flags &= ~TH_IMMORTAL;
248    t->flags |= DISABLED;
249    unrefthingy(t);
250    return 0;
251}
252
253/* Free a widget. */
254
255/**/
256static void
257freewidget(Widget w)
258{
259    if (w->flags & WIDGET_NCOMP) {
260	zsfree(w->u.comp.wid);
261	zsfree(w->u.comp.func);
262    } else if(!(w->flags & WIDGET_INT))
263	zsfree(w->u.fnnam);
264    zfree(w, sizeof(*w));
265}
266
267/* Add am internal widget provided by a module.  The name given is the  *
268 * canonical one, which must not begin with a dot.  The widget is first *
269 * bound to the dotted canonical name; if that name is already taken by *
270 * an internal widget, failure is indicated.  The same widget is then   *
271 * bound to the canonical name, and a pointer to the widget structure   *
272 * returned.                                                            */
273
274/**/
275mod_export Widget
276addzlefunction(char *name, ZleIntFunc ifunc, int flags)
277{
278    VARARR(char, dotn, strlen(name) + 2);
279    Widget w;
280    Thingy t;
281
282    if(name[0] == '.')
283	return NULL;
284    dotn[0] = '.';
285    strcpy(dotn + 1, name);
286    t = (Thingy) thingytab->getnode(thingytab, dotn);
287    if(t && (t->flags & TH_IMMORTAL))
288	return NULL;
289    w = zalloc(sizeof(*w));
290    w->flags = WIDGET_INT | flags;
291    w->first = NULL;
292    w->u.fn = ifunc;
293    t = rthingy(dotn);
294    bindwidget(w, t);
295    t->flags |= TH_IMMORTAL;
296    bindwidget(w, rthingy(name));
297    return w;
298}
299
300/* Delete an internal widget provided by a module.  Don't try to delete *
301 * a widget from the fixed table -- it would be bad.  (Thanks, Egon.)   */
302
303/**/
304mod_export void
305deletezlefunction(Widget w)
306{
307    Thingy p, n;
308
309    p = w->first;
310    while(1) {
311	n = p->samew;
312	if(n == p) {
313	    unbindwidget(p, 1);
314	    return;
315	}
316	unbindwidget(p, 1);
317	p = n;
318    }
319}
320
321/***************/
322/* zle builtin */
323/***************/
324
325/*
326 * The available operations are:
327 *
328 *   -l   list widgets/test for existence
329 *   -D   delete widget names
330 *   -A   link the two named widgets (2 arguments)
331 *   -C   create completion widget (3 arguments)
332 *   -N   create new user-defined widget (1 or 2 arguments)
333 *        invoke a widget (1 argument)
334 */
335
336/**/
337int
338bin_zle(char *name, char **args, Options ops, UNUSED(int func))
339{
340    static struct opn {
341	char o;
342	int (*func) _((char *, char **, Options, char));
343	int min, max;
344    } const opns[] = {
345	{ 'l', bin_zle_list, 0, -1 },
346	{ 'D', bin_zle_del,  1, -1 },
347	{ 'A', bin_zle_link, 2,  2 },
348	{ 'N', bin_zle_new,  1,  2 },
349	{ 'C', bin_zle_complete, 3, 3 },
350	{ 'R', bin_zle_refresh, 0, -1 },
351	{ 'M', bin_zle_mesg, 1, 1 },
352	{ 'U', bin_zle_unget, 1, 1 },
353	{ 'K', bin_zle_keymap, 1, 1 },
354	{ 'I', bin_zle_invalidate, 0, 0 },
355	{ 'F', bin_zle_fd, 0, 2 },
356	{ 'T', bin_zle_transform, 0, 2},
357	{ 0,   bin_zle_call, 0, -1 },
358    };
359    struct opn const *op, *opp;
360    int n;
361
362    /* select operation and ensure no clashing arguments */
363    for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
364    if(op->o)
365	for(opp = op; (++opp)->o; )
366	    if(OPT_ISSET(ops,STOUC(opp->o))) {
367		zwarnnam(name, "incompatible operation selection options");
368		return 1;
369	    }
370
371    /* check number of arguments */
372    for(n = 0; args[n]; n++) ;
373    if(n < op->min) {
374	zwarnnam(name, "not enough arguments for -%c", op->o);
375	return 1;
376    } else if(op->max != -1 && n > op->max) {
377	zwarnnam(name, "too many arguments for -%c", op->o);
378	return 1;
379    }
380
381    /* pass on the work to the operation function */
382    return op->func(name, args, ops, op->o);
383}
384
385/**/
386static int
387bin_zle_list(UNUSED(char *name), char **args, Options ops, UNUSED(char func))
388{
389    if (!*args) {
390	scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets,
391		      (OPT_ISSET(ops,'a') ? -1 : OPT_ISSET(ops,'L')));
392	return 0;
393    } else {
394	int ret = 0;
395	Thingy t;
396
397	for (; *args && !ret; args++) {
398	    HashNode hn = thingytab->getnode2(thingytab, *args);
399	    if (!(t = (Thingy) hn) ||
400		(!OPT_ISSET(ops,'a') && (t->widget->flags & WIDGET_INT)))
401		ret = 1;
402	    else if (OPT_ISSET(ops,'L')) {
403		scanlistwidgets(hn, 1);
404	    }
405	}
406	return ret;
407    }
408}
409
410/**/
411static int
412bin_zle_refresh(UNUSED(char *name), char **args, Options ops, UNUSED(char func))
413{
414    char *s = statusline;
415    int ocl = clearlist;
416
417    if (!zleactive)
418	return 1;
419    statusline = NULL;
420    if (*args) {
421	if (**args)
422	    statusline = *args;
423	if (*++args) {
424	    LinkList l = newlinklist();
425	    int zmultsav = zmult;
426
427	    for (; *args; args++)
428		addlinknode(l, *args);
429
430	    zmult = 1;
431	    listlist(l);
432	    if (statusline)
433		lastlistlen++;
434	    showinglist = clearlist = 0;
435	    zmult = zmultsav;
436	} else if (OPT_ISSET(ops,'c')) {
437	    clearlist = 1;
438	    lastlistlen = 0;
439	}
440    } else if (OPT_ISSET(ops,'c')) {
441	clearlist = listshown = 1;
442	lastlistlen = 0;
443    }
444    zrefresh();
445
446    clearlist = ocl;
447    statusline = s;
448    return 0;
449}
450
451/**/
452static int
453bin_zle_mesg(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
454{
455    if (!zleactive) {
456	zwarnnam(name, "can only be called from widget function");
457	return 1;
458    }
459    showmsg(*args);
460    if (sfcontext != SFC_WIDGET)
461	zrefresh();
462    return 0;
463}
464
465/**/
466static int
467bin_zle_unget(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
468{
469    char *b = *args, *p = b + strlen(b);
470
471    if (!zleactive) {
472	zwarnnam(name, "can only be called from widget function");
473	return 1;
474    }
475    while (p > b)
476	ungetbyte((int) *--p);
477    return 0;
478}
479
480/**/
481static int
482bin_zle_keymap(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
483{
484    if (!zleactive) {
485	zwarnnam(name, "can only be called from widget function");
486	return 1;
487    }
488    return selectkeymap(*args, 0);
489}
490
491/*
492 * List a widget.
493 * If list is negative, just print the name.
494 * If list is 0, use abbreviated format.
495 * If list is positive, output as a command.
496 */
497/**/
498static void
499scanlistwidgets(HashNode hn, int list)
500{
501    Thingy t = (Thingy) hn;
502    Widget w = t->widget;
503
504    if(list < 0) {
505	printf("%s\n", hn->nam);
506	return;
507    }
508    if(w->flags & WIDGET_INT)
509	return;
510    if(list) {
511	printf("zle -%c ", (w->flags & WIDGET_NCOMP) ? 'C' : 'N');
512	if(t->nam[0] == '-')
513	    fputs("-- ", stdout);
514	quotedzputs(t->nam, stdout);
515	if (w->flags & WIDGET_NCOMP) {
516	    fputc(' ', stdout);
517	    quotedzputs(w->u.comp.wid, stdout);
518	    fputc(' ', stdout);
519	    quotedzputs(w->u.comp.func, stdout);
520	} else if(strcmp(t->nam, w->u.fnnam)) {
521	    fputc(' ', stdout);
522	    quotedzputs(w->u.fnnam, stdout);
523	}
524    } else {
525	nicezputs(t->nam, stdout);
526	if (w->flags & WIDGET_NCOMP) {
527	    fputs(" -C ", stdout);
528	    nicezputs(w->u.comp.wid, stdout);
529	    fputc(' ', stdout);
530	    nicezputs(w->u.comp.func, stdout);
531	} else if(strcmp(t->nam, w->u.fnnam)) {
532	    fputs(" (", stdout);
533	    nicezputs(w->u.fnnam, stdout);
534	    fputc(')', stdout);
535	}
536    }
537    putchar('\n');
538}
539
540/**/
541static int
542bin_zle_del(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
543{
544    int ret = 0;
545
546    do {
547	Thingy t = (Thingy) thingytab->getnode(thingytab, *args);
548	if(!t) {
549	    zwarnnam(name, "no such widget `%s'", *args);
550	    ret = 1;
551	} else if(unbindwidget(t, 0)) {
552	    zwarnnam(name, "widget name `%s' is protected", *args);
553	    ret = 1;
554	}
555    } while(*++args);
556    return ret;
557}
558
559/**/
560static int
561bin_zle_link(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
562{
563    Thingy t = (Thingy) thingytab->getnode(thingytab, args[0]);
564
565    if(!t) {
566	zwarnnam(name, "no such widget `%s'", args[0]);
567	return 1;
568    } else if(bindwidget(t->widget, rthingy(args[1]))) {
569	zwarnnam(name, "widget name `%s' is protected", args[1]);
570	return 1;
571    }
572    return 0;
573
574}
575
576/**/
577static int
578bin_zle_new(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
579{
580    Widget w = zalloc(sizeof(*w));
581
582    w->flags = 0;
583    w->first = NULL;
584    w->u.fnnam = ztrdup(args[1] ? args[1] : args[0]);
585    if(!bindwidget(w, rthingy(args[0])))
586	return 0;
587    freewidget(w);
588    zwarnnam(name, "widget name `%s' is protected", args[0]);
589    return 1;
590}
591
592/**/
593static int
594bin_zle_complete(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
595{
596    Thingy t;
597    Widget w, cw;
598
599    if (require_module("zsh/complete", NULL) == 1) {
600	zwarnnam(name, "can't load complete module");
601	return 1;
602    }
603    t = rthingy((args[1][0] == '.') ? args[1] : dyncat(".", args[1]));
604    cw = t->widget;
605    unrefthingy(t);
606    if (!cw || !(cw->flags & ZLE_ISCOMP)) {
607	zwarnnam(name, "invalid widget `%s'", args[1]);
608	return 1;
609    }
610    w = zalloc(sizeof(*w));
611    w->flags = WIDGET_NCOMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX;
612    w->first = NULL;
613    w->u.comp.fn = cw->u.fn;
614    w->u.comp.wid = ztrdup(args[1]);
615    w->u.comp.func = ztrdup(args[2]);
616    if (bindwidget(w, rthingy(args[0]))) {
617	freewidget(w);
618	zwarnnam(name, "widget name `%s' is protected", args[0]);
619	return 1;
620    }
621    hascompwidgets++;
622
623    return 0;
624}
625
626/**/
627static int
628zle_usable()
629{
630    return zleactive && !incompctlfunc && !incompfunc
631#if 0
632	/*
633	 * PWS experiment: commenting this out allows zle widgets
634	 * in signals, hooks etc.  I'm not sure if this has a down side;
635	 * it ought to be that zleactive is good enough to test whether
636	 * widgets are callable.
637	 */
638	&& sfcontext == SFC_WIDGET
639#endif
640	   ;
641}
642
643/**/
644static int
645bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
646{
647    Thingy t;
648    struct modifier modsave = zmod;
649    int ret, saveflag = 0, setbindk = 0;
650    char *wname = *args++, *keymap_restore = NULL, *keymap_tmp;
651
652    if (!wname)
653	return !zle_usable();
654
655    if(!zle_usable()) {
656	zwarnnam(name, "widgets can only be called when ZLE is active");
657	return 1;
658    }
659
660    UNMETACHECK();
661
662    while (*args && **args == '-') {
663	char *num;
664	if (!args[0][1] || args[0][1] == '-') {
665	    args++;
666	    break;
667	}
668	while (*++(*args)) {
669	    switch (**args) {
670	    case 'n':
671		num = args[0][1] ? args[0]+1 : args[1];
672		if (!num) {
673		    zwarnnam(name, "number expected after -%c", **args);
674		    return 1;
675		}
676		if (!args[0][1])
677		    *++args = "" - 1;
678		saveflag = 1;
679		zmod.mult = atoi(num);
680		zmod.flags |= MOD_MULT;
681		break;
682	    case 'N':
683		saveflag = 1;
684		zmod.mult = 1;
685		zmod.flags &= ~MOD_MULT;
686		break;
687	    case 'K':
688		keymap_tmp = args[0][1] ? args[0]+1 : args[1];
689		if (!keymap_tmp) {
690		    zwarnnam(name, "keymap expected after -%c", **args);
691		    return 1;
692		}
693		if (!args[0][1])
694		    *++args = "" - 1;
695		keymap_restore = dupstring(curkeymapname);
696		if (selectkeymap(keymap_tmp, 0))
697		    return 1;
698		break;
699	    case 'w':
700		setbindk = 1;
701		break;
702	    default:
703		zwarnnam(name, "unknown option: %s", *args);
704		return 1;
705	    }
706	}
707	args++;
708    }
709
710    t = rthingy(wname);
711    ret = execzlefunc(t, args, setbindk);
712    unrefthingy(t);
713    if (saveflag)
714	zmod = modsave;
715    if (keymap_restore)
716	selectkeymap(keymap_restore, 0);
717    return ret;
718}
719
720
721/*
722 * Flag that the user has requested the terminal be trashed
723 * for whatever use.  We attempt to keep the tty settings in
724 * this mode synced with the normal (non-zle) settings unless
725 * they are frozen.
726 */
727
728/**/
729int fetchttyinfo;
730
731/**/
732static int
733bin_zle_invalidate(UNUSED(char *name), UNUSED(char **args), UNUSED(Options ops), UNUSED(char func))
734{
735    /*
736     * Trash zle if trashable, but only indicate that zle is usable
737     * if it's possible to call a zle widget next.  This is not
738     * true if a completion widget is active.
739     */
740    if (zleactive) {
741	int wastrashed = trashedzle;
742	trashzle();
743	if (!wastrashed && (zlereadflags & ZLRF_NOSETTY)) {
744	    /*
745	     * We normally wouldn't have restored the terminal
746	     * in this case, but as it's at user request we do
747	     * so (hence the apparently illogical sense of the
748	     * second part of the test).
749	     */
750	    settyinfo(&shttyinfo);
751	}
752	fetchttyinfo = 1;
753	return 0;
754    } else
755	return 1;
756}
757
758/**/
759static int
760bin_zle_fd(char *name, char **args, Options ops, UNUSED(char func))
761{
762    int fd = 0, i, found = 0;
763    char *endptr;
764
765    if (*args) {
766	fd = (int)zstrtol(*args, &endptr, 10);
767
768	if (*endptr || fd < 0) {
769	    zwarnnam(name, "Bad file descriptor number for -F: %s", *args);
770	    return 1;
771	}
772    }
773
774    if (OPT_ISSET(ops,'L') || !*args) {
775	/* Listing handlers. */
776	if (*args && args[1]) {
777	    zwarnnam(name, "too many arguments for -FL");
778	    return 1;
779	}
780	for (i = 0; i < nwatch; i++) {
781	    Watch_fd watch_fd = watch_fds + i;
782	    if (*args && watch_fd->fd != fd)
783		continue;
784	    found = 1;
785	    printf("%s -F %s%d %s\n", name, watch_fd->widget ? "-w " : "",
786		   watch_fd->fd, watch_fd->func);
787	}
788	/* only return status 1 if fd given and not found */
789	return *args && !found;
790    }
791
792    if (args[1]) {
793	/* Adding or replacing a handler */
794	char *funcnam = ztrdup(args[1]);
795	if (nwatch) {
796	    for (i = 0; i < nwatch; i++) {
797		Watch_fd watch_fd = watch_fds + i;
798		if (watch_fd->fd == fd) {
799		    zsfree(watch_fd->func);
800		    watch_fd->func = funcnam;
801		    watch_fd->widget = OPT_ISSET(ops,'w') ? 1 : 0;
802		    found = 1;
803		    break;
804		}
805	    }
806	}
807	if (!found) {
808	    /* zrealloc handles NULL pointers, so OK for first time through */
809	    int newnwatch = nwatch+1;
810	    Watch_fd new_fd;
811	    watch_fds = (Watch_fd)zrealloc(watch_fds,
812					   newnwatch * sizeof(struct watch_fd));
813	    new_fd = watch_fds + nwatch;
814	    new_fd->fd = fd;
815	    new_fd->func = funcnam;
816	    new_fd->widget = OPT_ISSET(ops,'w') ? 1 : 0;
817	    nwatch = newnwatch;
818	}
819    } else {
820	/* Deleting a handler */
821	for (i = 0; i < nwatch; i++) {
822	    Watch_fd watch_fd = watch_fds + i;
823	    if (watch_fd->fd == fd) {
824		int newnwatch = nwatch-1;
825		Watch_fd new_fds;
826
827		zsfree(watch_fd->func);
828		if (newnwatch) {
829		    new_fds = zalloc(newnwatch*sizeof(struct watch_fd));
830		    if (i) {
831			memcpy(new_fds, watch_fds, i*sizeof(struct watch_fd));
832		    }
833		    if (i < newnwatch) {
834			memcpy(new_fds+i, watch_fds+i+1,
835			       (newnwatch-i)*sizeof(struct watch_fd));
836		    }
837		} else {
838		    new_fds = NULL;
839		}
840		zfree(watch_fds, nwatch*sizeof(struct watch_fd));
841		watch_fds = new_fds;
842		nwatch = newnwatch;
843		found = 1;
844		break;
845	    }
846	}
847	if (!found) {
848	    zwarnnam(name, "No handler installed for fd %d", fd);
849	    return 1;
850	}
851    }
852
853    return 0;
854}
855
856/**/
857static int
858bin_zle_transform(char *name, char **args, Options ops, UNUSED(char func))
859{
860    /*
861     * -1: too few arguments
862     * 0: just right
863     * 1: too many arguments
864     * 2: first argument not recognised
865     */
866    int badargs = 0;
867
868    if (OPT_ISSET(ops,'L')) {
869	if (args[0]) {
870	    if (args[1]) {
871		badargs = 1;
872	    } else if (strcmp(args[0], "tc")) {
873		badargs = 2;
874	    }
875	}
876	if (!badargs && tcout_func_name) {
877	    fputs("zle -T tc ", stdout);
878	    quotedzputs(tcout_func_name, stdout);
879	    putchar('\n');
880	}
881    } else if (OPT_ISSET(ops,'r')) {
882	if (!args[0]) {
883	    badargs = -1;
884	} else if (args[1]) {
885	    badargs = 1;
886	} else if (tcout_func_name) {
887	    zsfree(tcout_func_name);
888	    tcout_func_name = NULL;
889	}
890    } else {
891	if (!args[0] || !args[1]) {
892	    badargs = -1;
893	    /* we've already checked args <= 2 */
894	} else {
895	    if (!strcmp(args[0], "tc")) {
896		if (tcout_func_name) {
897		    zsfree(tcout_func_name);
898		}
899		tcout_func_name = ztrdup(args[1]);
900	    } else {
901		badargs = 2;
902	    }
903	}
904    }
905
906    if (badargs) {
907	if (badargs == 2) {
908	    zwarnnam(name, "-T: no such transformation '%s'", args[0]);
909	} else {
910	    char *way = (badargs > 0) ? "many" : "few";
911	    zwarnnam(name, "too %s arguments for option -T", way);
912	}
913	return 1;
914    }
915
916    return 0;
917}
918
919/*******************/
920/* initialiasation */
921/*******************/
922
923/**/
924void
925init_thingies(void)
926{
927    Thingy t;
928
929    createthingytab();
930    for(t = thingies; t->nam; t++)
931	thingytab->addnode(thingytab, t->nam, t);
932}
933