mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			980 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			980 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * zle_thingy.c - thingies
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 1992-1997 Paul Falstad
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Paul Falstad or the Zsh Development Group be liable
 | |
|  * to any party for direct, indirect, special, incidental, or consequential
 | |
|  * damages arising out of the use of this software and its documentation,
 | |
|  * even if Paul Falstad and the Zsh Development Group have been advised of
 | |
|  * the possibility of such damage.
 | |
|  *
 | |
|  * Paul Falstad and the Zsh Development Group specifically disclaim any
 | |
|  * warranties, including, but not limited to, the implied warranties of
 | |
|  * merchantability and fitness for a particular purpose.  The software
 | |
|  * provided hereunder is on an "as is" basis, and Paul Falstad and the
 | |
|  * Zsh Development Group have no obligation to provide maintenance,
 | |
|  * support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "zle.mdh"
 | |
| #include "zle_thingy.pro"
 | |
| 
 | |
| /*
 | |
|  * Thingies:
 | |
|  *
 | |
|  * From the user's point of view, a thingy is just a string.  Internally,
 | |
|  * the thingy is a struct thingy; these structures are in a hash table
 | |
|  * indexed by the string the user sees.  This hash table contains all
 | |
|  * thingies currently referenced anywhere; each has a reference count,
 | |
|  * and is deleted when it becomes unused.  Being the name of a function
 | |
|  * counts as a reference.
 | |
|  *
 | |
|  * The DISABLED flag on a thingy indicates that it is not the name of a
 | |
|  * widget.  This makes it easy to generate completion lists;
 | |
|  * looking only at the `enabled' nodes makes the thingy table look like
 | |
|  * a table of widgets.
 | |
|  */
 | |
| 
 | |
| /* Hashtable of thingies.  Enabled nodes are those that refer to widgets. */
 | |
| 
 | |
| /**/
 | |
| mod_export HashTable thingytab;
 | |
| 
 | |
| /**********************************/
 | |
| /* hashtable management functions */
 | |
| /**********************************/
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| createthingytab(void)
 | |
| {
 | |
|     thingytab = newhashtable(199, "thingytab", NULL);
 | |
| 
 | |
|     thingytab->hash        = hasher;
 | |
|     thingytab->emptytable  = emptythingytab;
 | |
|     thingytab->filltable   = NULL;
 | |
|     thingytab->cmpnodes    = strcmp;
 | |
|     thingytab->addnode     = addhashnode;
 | |
|     thingytab->getnode     = gethashnode;
 | |
|     thingytab->getnode2    = gethashnode2;
 | |
|     thingytab->removenode  = removehashnode;
 | |
|     thingytab->disablenode = NULL;
 | |
|     thingytab->enablenode  = NULL;
 | |
|     thingytab->freenode    = freethingynode;
 | |
|     thingytab->printnode   = NULL;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| emptythingytab(UNUSED(HashTable ht))
 | |
| {
 | |
|     /* This will only be called when deleting the thingy table, which *
 | |
|      * is only done to unload the zle module.  A normal emptytable()  *
 | |
|      * function would free all the thingies, but we don't want to do  *
 | |
|      * that because some of them are the known thingies in the fixed  *
 | |
|      * `thingies' table.  As the module cleanup code deletes all the  *
 | |
|      * keymaps and so on before deleting the thingy table, we can     *
 | |
|      * just remove the user-defined widgets and then be sure that     *
 | |
|      * *all* the thingies left are the fixed ones.  This has the side *
 | |
|      * effect of freeing all resources used by user-defined widgets.  */
 | |
|     scanhashtable(thingytab, 0, 0, DISABLED, scanemptythingies, 0);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| scanemptythingies(HashNode hn, UNUSED(int flags))
 | |
| {
 | |
|     Thingy t = (Thingy) hn;
 | |
| 
 | |
|     /* Mustn't unbind internal widgets -- we wouldn't want to free the *
 | |
|      * memory they use.                                                */
 | |
|     if(!(t->widget->flags & WIDGET_INT))
 | |
| 	unbindwidget(t, 1);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static Thingy
 | |
| makethingynode(void)
 | |
| {
 | |
|     Thingy t = (Thingy) zshcalloc(sizeof(*t));
 | |
| 
 | |
|     t->flags = DISABLED;
 | |
|     return t;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static void
 | |
| freethingynode(HashNode hn)
 | |
| {
 | |
|     Thingy th = (Thingy) hn;
 | |
| 
 | |
|     zsfree(th->nam);
 | |
|     zfree(th, sizeof(*th));
 | |
| }
 | |
| 
 | |
| /************************/
 | |
| /* referencing thingies */
 | |
| /************************/
 | |
| 
 | |
| /* It is important to maintain the reference counts on thingies.  When *
 | |
|  * copying a reference to a thingy, wrap the copy in refthingy(), to   *
 | |
|  * increase its reference count.  When removing a reference,           *
 | |
|  * unrefthingy() it.  Both of these functions handle NULL arguments    *
 | |
|  * correctly.                                                          */
 | |
| 
 | |
| /**/
 | |
| mod_export Thingy
 | |
| refthingy(Thingy th)
 | |
| {
 | |
|     if(th)
 | |
| 	th->rc++;
 | |
|     return th;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| void
 | |
| unrefthingy(Thingy th)
 | |
| {
 | |
|     if(th && !--th->rc)
 | |
| 	thingytab->freenode(thingytab->removenode(thingytab, th->nam));
 | |
| }
 | |
| 
 | |
| /* Use rthingy() to turn a string into a thingy.  It increases the reference *
 | |
|  * count, after creating the thingy structure if necessary.                  */
 | |
| 
 | |
| /**/
 | |
| Thingy
 | |
| rthingy(char *nam)
 | |
| {
 | |
|     Thingy t = (Thingy) thingytab->getnode2(thingytab, nam);
 | |
| 
 | |
|     if(!t)
 | |
| 	thingytab->addnode(thingytab, ztrdup(nam), t = makethingynode());
 | |
|     return refthingy(t);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| Thingy
 | |
| rthingy_nocreate(char *nam)
 | |
| {
 | |
|     Thingy t = (Thingy) thingytab->getnode2(thingytab, nam);
 | |
| 
 | |
|     if(!t)
 | |
| 	return NULL;
 | |
|     return refthingy(t);
 | |
| }
 | |
| 
 | |
| /***********/
 | |
| /* widgets */
 | |
| /***********/
 | |
| 
 | |
| /*
 | |
|  * Each widget is attached to one or more thingies.  Each thingy
 | |
|  * names either zero or one widgets.  Thingies that name a widget
 | |
|  * are treated as being referenced.  The widget type, flags and pointer
 | |
|  * are stored in a separate structure pointed to by the thingies.  Each
 | |
|  * thingy also has a pointer to the `next' thingy (in a circular list)
 | |
|  * that references the same widget.  The DISABLED flag is unset in these
 | |
|  * thingies.
 | |
|  */
 | |
| 
 | |
| /* Bind a widget to a thingy.  The thingy's reference count must already *
 | |
|  * have been incremented.  The widget may already be bound to other      *
 | |
|  * thingies; if it is not, then its `first' member must be NULL.  Return *
 | |
|  * is 0 on success, or -1 if the thingy has the TH_IMMORTAL flag set.    */
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bindwidget(Widget w, Thingy t)
 | |
| {
 | |
|     if(t->flags & TH_IMMORTAL) {
 | |
| 	unrefthingy(t);
 | |
| 	return -1;
 | |
|     }
 | |
|     if(!(t->flags & DISABLED)) {
 | |
| 	if(t->widget == w)
 | |
| 	    return 0;
 | |
| 	unbindwidget(t, 1);
 | |
|     }
 | |
|     if(w->first) {
 | |
| 	t->samew = w->first->samew;
 | |
| 	w->first->samew = t;
 | |
|     } else {
 | |
| 	w->first = t;
 | |
| 	t->samew = t;
 | |
|     }
 | |
|     t->widget = w;
 | |
|     t->flags &= ~DISABLED;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Unbind a widget from a thingy.  This decrements the thingy's reference *
 | |
|  * count.  The widget will be destroyed if this is its last name.         *
 | |
|  * TH_IMMORTAL thingies won't be touched, unless override is non-zero.    *
 | |
|  * Returns 0 on success, or -1 if the thingy is protected.  If the thingy *
 | |
|  * doesn't actually reference a widget, this is considered successful.    */
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| unbindwidget(Thingy t, int override)
 | |
| {
 | |
|     Widget w;
 | |
| 
 | |
|     if(t->flags & DISABLED)
 | |
| 	return 0;
 | |
|     if(!override && (t->flags & TH_IMMORTAL))
 | |
| 	return -1;
 | |
|     w = t->widget;
 | |
|     if(t->samew == t)
 | |
| 	freewidget(w);
 | |
|     else {
 | |
| 	Thingy p;
 | |
| 	for(p = w->first; p->samew != t; p = p->samew) ;
 | |
| 	w->first = p;   /* optimised for deletezlefunction() */
 | |
| 	p->samew = t->samew;
 | |
|     }
 | |
|     t->flags &= ~TH_IMMORTAL;
 | |
|     t->flags |= DISABLED;
 | |
|     unrefthingy(t);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Free a widget. */
 | |
| 
 | |
| /**/
 | |
| void
 | |
| freewidget(Widget w)
 | |
| {
 | |
|     if (w->flags & WIDGET_INUSE) {
 | |
| 	w->flags |= WIDGET_FREE;
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|     if (w->flags & WIDGET_NCOMP) {
 | |
| 	zsfree(w->u.comp.wid);
 | |
| 	zsfree(w->u.comp.func);
 | |
|     } else if(!(w->flags & WIDGET_INT))
 | |
| 	zsfree(w->u.fnnam);
 | |
|     zfree(w, sizeof(*w));
 | |
| }
 | |
| 
 | |
| /* Add am internal widget provided by a module.  The name given is the  *
 | |
|  * canonical one, which must not begin with a dot.  The widget is first *
 | |
|  * bound to the dotted canonical name; if that name is already taken by *
 | |
|  * an internal widget, failure is indicated.  The same widget is then   *
 | |
|  * bound to the canonical name, and a pointer to the widget structure   *
 | |
|  * returned.                                                            */
 | |
| 
 | |
| /**/
 | |
| mod_export Widget
 | |
| addzlefunction(char *name, ZleIntFunc ifunc, int flags)
 | |
| {
 | |
|     VARARR(char, dotn, strlen(name) + 2);
 | |
|     Widget w;
 | |
|     Thingy t;
 | |
| 
 | |
|     if(name[0] == '.')
 | |
| 	return NULL;
 | |
|     dotn[0] = '.';
 | |
|     strcpy(dotn + 1, name);
 | |
|     t = (Thingy) thingytab->getnode(thingytab, dotn);
 | |
|     if(t && (t->flags & TH_IMMORTAL))
 | |
| 	return NULL;
 | |
|     w = zalloc(sizeof(*w));
 | |
|     w->flags = WIDGET_INT | flags;
 | |
|     w->first = NULL;
 | |
|     w->u.fn = ifunc;
 | |
|     t = rthingy(dotn);
 | |
|     bindwidget(w, t);
 | |
|     t->flags |= TH_IMMORTAL;
 | |
|     bindwidget(w, rthingy(name));
 | |
|     return w;
 | |
| }
 | |
| 
 | |
| /* Delete an internal widget provided by a module.  Don't try to delete *
 | |
|  * a widget from the fixed table -- it would be bad.  (Thanks, Egon.)   */
 | |
| 
 | |
| /**/
 | |
| mod_export void
 | |
| deletezlefunction(Widget w)
 | |
| {
 | |
|     Thingy p, n;
 | |
| 
 | |
|     p = w->first;
 | |
|     while(1) {
 | |
| 	n = p->samew;
 | |
| 	if(n == p) {
 | |
| 	    unbindwidget(p, 1);
 | |
| 	    return;
 | |
| 	}
 | |
| 	unbindwidget(p, 1);
 | |
| 	p = n;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /***************/
 | |
| /* zle builtin */
 | |
| /***************/
 | |
| 
 | |
| /*
 | |
|  * The available operations are:
 | |
|  *
 | |
|  *   -l   list widgets/test for existence
 | |
|  *   -D   delete widget names
 | |
|  *   -A   link the two named widgets (2 arguments)
 | |
|  *   -C   create completion widget (3 arguments)
 | |
|  *   -N   create new user-defined widget (1 or 2 arguments)
 | |
|  *        invoke a widget (1 argument)
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| int
 | |
| bin_zle(char *name, char **args, Options ops, UNUSED(int func))
 | |
| {
 | |
|     static struct opn {
 | |
| 	char o;
 | |
| 	int (*func) _((char *, char **, Options, char));
 | |
| 	int min, max;
 | |
|     } const opns[] = {
 | |
| 	{ 'l', bin_zle_list, 0, -1 },
 | |
| 	{ 'D', bin_zle_del,  1, -1 },
 | |
| 	{ 'A', bin_zle_link, 2,  2 },
 | |
| 	{ 'N', bin_zle_new,  1,  2 },
 | |
| 	{ 'C', bin_zle_complete, 3, 3 },
 | |
| 	{ 'R', bin_zle_refresh, 0, -1 },
 | |
| 	{ 'M', bin_zle_mesg, 1, 1 },
 | |
| 	{ 'U', bin_zle_unget, 1, 1 },
 | |
| 	{ 'K', bin_zle_keymap, 1, 1 },
 | |
| 	{ 'I', bin_zle_invalidate, 0, 0 },
 | |
| 	{ 'f', bin_zle_flags, 1, -1 },
 | |
| 	{ 'F', bin_zle_fd, 0, 2 },
 | |
| 	{ 'T', bin_zle_transform, 0, 2},
 | |
| 	{ 0,   bin_zle_call, 0, -1 },
 | |
|     };
 | |
|     struct opn const *op, *opp;
 | |
|     int n;
 | |
| 
 | |
|     /* select operation and ensure no clashing arguments */
 | |
|     for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ;
 | |
|     if(op->o)
 | |
| 	for(opp = op; (++opp)->o; )
 | |
| 	    if(OPT_ISSET(ops,STOUC(opp->o))) {
 | |
| 		zwarnnam(name, "incompatible operation selection options");
 | |
| 		return 1;
 | |
| 	    }
 | |
| 
 | |
|     /* check number of arguments */
 | |
|     for(n = 0; args[n]; n++) ;
 | |
|     if(n < op->min) {
 | |
| 	zwarnnam(name, "not enough arguments for -%c", op->o);
 | |
| 	return 1;
 | |
|     } else if(op->max != -1 && n > op->max) {
 | |
| 	zwarnnam(name, "too many arguments for -%c", op->o);
 | |
| 	return 1;
 | |
|     }
 | |
| 
 | |
|     /* pass on the work to the operation function */
 | |
|     return op->func(name, args, ops, op->o);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_list(UNUSED(char *name), char **args, Options ops, UNUSED(char func))
 | |
| {
 | |
|     if (!*args) {
 | |
| 	scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets,
 | |
| 		      (OPT_ISSET(ops,'a') ? -1 : OPT_ISSET(ops,'L')));
 | |
| 	return 0;
 | |
|     } else {
 | |
| 	int ret = 0;
 | |
| 	Thingy t;
 | |
| 
 | |
| 	for (; *args && !ret; args++) {
 | |
| 	    HashNode hn = thingytab->getnode2(thingytab, *args);
 | |
| 	    if (!(t = (Thingy) hn) ||
 | |
| 		(!OPT_ISSET(ops,'a') && (t->widget->flags & WIDGET_INT)))
 | |
| 		ret = 1;
 | |
| 	    else if (OPT_ISSET(ops,'L')) {
 | |
| 		scanlistwidgets(hn, 1);
 | |
| 	    }
 | |
| 	}
 | |
| 	return ret;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_refresh(UNUSED(char *name), char **args, Options ops, UNUSED(char func))
 | |
| {
 | |
|     char *s = statusline;
 | |
|     int ocl = clearlist;
 | |
| 
 | |
|     if (!zleactive)
 | |
| 	return 1;
 | |
|     statusline = NULL;
 | |
|     if (*args) {
 | |
| 	if (**args)
 | |
| 	    statusline = *args;
 | |
| 	if (*++args) {
 | |
| 	    LinkList l = newlinklist();
 | |
| 	    int zmultsav = zmult;
 | |
| 
 | |
| 	    for (; *args; args++)
 | |
| 		addlinknode(l, *args);
 | |
| 
 | |
| 	    zmult = 1;
 | |
| 	    listlist(l);
 | |
| 	    if (statusline)
 | |
| 		lastlistlen++;
 | |
| 	    showinglist = clearlist = 0;
 | |
| 	    zmult = zmultsav;
 | |
| 	} else if (OPT_ISSET(ops,'c')) {
 | |
| 	    clearlist = 1;
 | |
| 	    lastlistlen = 0;
 | |
| 	}
 | |
|     } else if (OPT_ISSET(ops,'c')) {
 | |
| 	clearlist = listshown = 1;
 | |
| 	lastlistlen = 0;
 | |
|     }
 | |
|     zrefresh();
 | |
| 
 | |
|     clearlist = ocl;
 | |
|     statusline = s;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_mesg(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     if (!zleactive) {
 | |
| 	zwarnnam(name, "can only be called from widget function");
 | |
| 	return 1;
 | |
|     }
 | |
|     showmsg(*args);
 | |
|     if (sfcontext != SFC_WIDGET)
 | |
| 	zrefresh();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_unget(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     char *b = unmeta(*args), *p = b + strlen(b);
 | |
| 
 | |
|     if (!zleactive) {
 | |
| 	zwarnnam(name, "can only be called from widget function");
 | |
| 	return 1;
 | |
|     }
 | |
|     while (p > b)
 | |
| 	ungetbyte((int) *--p);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_keymap(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     if (!zleactive) {
 | |
| 	zwarnnam(name, "can only be called from widget function");
 | |
| 	return 1;
 | |
|     }
 | |
|     return selectkeymap(*args, 0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * List a widget.
 | |
|  * If list is negative, just print the name.
 | |
|  * If list is 0, use abbreviated format.
 | |
|  * If list is positive, output as a command.
 | |
|  */
 | |
| /**/
 | |
| static void
 | |
| scanlistwidgets(HashNode hn, int list)
 | |
| {
 | |
|     Thingy t = (Thingy) hn;
 | |
|     Widget w = t->widget;
 | |
| 
 | |
|     if(list < 0) {
 | |
| 	printf("%s\n", hn->nam);
 | |
| 	return;
 | |
|     }
 | |
|     if(w->flags & WIDGET_INT)
 | |
| 	return;
 | |
|     if(list) {
 | |
| 	printf("zle -%c ", (w->flags & WIDGET_NCOMP) ? 'C' : 'N');
 | |
| 	if(t->nam[0] == '-')
 | |
| 	    fputs("-- ", stdout);
 | |
| 	quotedzputs(t->nam, stdout);
 | |
| 	if (w->flags & WIDGET_NCOMP) {
 | |
| 	    fputc(' ', stdout);
 | |
| 	    quotedzputs(w->u.comp.wid, stdout);
 | |
| 	    fputc(' ', stdout);
 | |
| 	    quotedzputs(w->u.comp.func, stdout);
 | |
| 	} else if(strcmp(t->nam, w->u.fnnam)) {
 | |
| 	    fputc(' ', stdout);
 | |
| 	    quotedzputs(w->u.fnnam, stdout);
 | |
| 	}
 | |
|     } else {
 | |
| 	nicezputs(t->nam, stdout);
 | |
| 	if (w->flags & WIDGET_NCOMP) {
 | |
| 	    fputs(" -C ", stdout);
 | |
| 	    nicezputs(w->u.comp.wid, stdout);
 | |
| 	    fputc(' ', stdout);
 | |
| 	    nicezputs(w->u.comp.func, stdout);
 | |
| 	} else if(strcmp(t->nam, w->u.fnnam)) {
 | |
| 	    fputs(" (", stdout);
 | |
| 	    nicezputs(w->u.fnnam, stdout);
 | |
| 	    fputc(')', stdout);
 | |
| 	}
 | |
|     }
 | |
|     putchar('\n');
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_del(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     int ret = 0;
 | |
| 
 | |
|     do {
 | |
| 	Thingy t = (Thingy) thingytab->getnode(thingytab, *args);
 | |
| 	if(!t) {
 | |
| 	    zwarnnam(name, "no such widget `%s'", *args);
 | |
| 	    ret = 1;
 | |
| 	} else if(unbindwidget(t, 0)) {
 | |
| 	    zwarnnam(name, "widget name `%s' is protected", *args);
 | |
| 	    ret = 1;
 | |
| 	}
 | |
|     } while(*++args);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_link(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     Thingy t = (Thingy) thingytab->getnode(thingytab, args[0]);
 | |
| 
 | |
|     if(!t) {
 | |
| 	zwarnnam(name, "no such widget `%s'", args[0]);
 | |
| 	return 1;
 | |
|     } else if(bindwidget(t->widget, rthingy(args[1]))) {
 | |
| 	zwarnnam(name, "widget name `%s' is protected", args[1]);
 | |
| 	return 1;
 | |
|     }
 | |
|     return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_new(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     Widget w = zalloc(sizeof(*w));
 | |
| 
 | |
|     w->flags = 0;
 | |
|     w->first = NULL;
 | |
|     w->u.fnnam = ztrdup(args[1] ? args[1] : args[0]);
 | |
|     if(!bindwidget(w, rthingy(args[0])))
 | |
| 	return 0;
 | |
|     freewidget(w);
 | |
|     zwarnnam(name, "widget name `%s' is protected", args[0]);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_complete(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     Thingy t;
 | |
|     Widget w, cw;
 | |
| 
 | |
|     if (require_module("zsh/complete", NULL) == 1) {
 | |
| 	zwarnnam(name, "can't load complete module");
 | |
| 	return 1;
 | |
|     }
 | |
|     t = rthingy((args[1][0] == '.') ? args[1] : dyncat(".", args[1]));
 | |
|     cw = t->widget;
 | |
|     unrefthingy(t);
 | |
|     if (!cw || !(cw->flags & ZLE_ISCOMP)) {
 | |
| 	zwarnnam(name, "invalid widget `%s'", args[1]);
 | |
| 	return 1;
 | |
|     }
 | |
|     w = zalloc(sizeof(*w));
 | |
|     w->flags = WIDGET_NCOMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX;
 | |
|     w->first = NULL;
 | |
|     w->u.comp.fn = cw->u.fn;
 | |
|     w->u.comp.wid = ztrdup(args[1]);
 | |
|     w->u.comp.func = ztrdup(args[2]);
 | |
|     if (bindwidget(w, rthingy(args[0]))) {
 | |
| 	freewidget(w);
 | |
| 	zwarnnam(name, "widget name `%s' is protected", args[0]);
 | |
| 	return 1;
 | |
|     }
 | |
|     hascompwidgets++;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| zle_usable(void)
 | |
| {
 | |
|     return zleactive && !incompctlfunc && !incompfunc
 | |
| #if 0
 | |
| 	/*
 | |
| 	 * PWS experiment: commenting this out allows zle widgets
 | |
| 	 * in signals, hooks etc.  I'm not sure if this has a down side;
 | |
| 	 * it ought to be that zleactive is good enough to test whether
 | |
| 	 * widgets are callable.
 | |
| 	 */
 | |
| 	&& sfcontext == SFC_WIDGET
 | |
| #endif
 | |
| 	   ;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     int ret = 0;
 | |
|     char **flag;
 | |
| 
 | |
|     if (!zle_usable()) {
 | |
| 	zwarnnam(name, "can only set flags from a widget");
 | |
| 	return 1;
 | |
|     }
 | |
| 
 | |
|     if (bindk) {
 | |
| 	Widget w = bindk->widget;
 | |
| 	if (w) {
 | |
| 	    for (flag = args; *flag; flag++) {
 | |
| 		if (!strcmp(*flag, "yank")) {
 | |
| 		    w->flags |= ZLE_YANKAFTER;
 | |
| 		} else if (!strcmp(*flag, "yankbefore"))
 | |
| 		    w->flags |= ZLE_YANKBEFORE;
 | |
| 		else if (!strcmp(*flag, "kill"))
 | |
| 		    w->flags |= ZLE_KILL;
 | |
| 		/*
 | |
| 		 * These won't do anything yet, because of how execzlefunc
 | |
| 		 * handles user widgets
 | |
| 		} else if (!strcmp(*flag, "menucmp"))
 | |
| 		    w->flags |= ZLE_MENUCMP;
 | |
| 		else if (!strcmp(*flag, "linemove"))
 | |
| 		    w->flags |= ZLE_LINEMOVE;
 | |
| 		else if (!strcmp(*flag, "keepsuffix"))
 | |
| 		    w->flags |= ZLE_KEEPSUFFIX;
 | |
| 		*/
 | |
| 		else {
 | |
| 		    zwarnnam(name, "invalid flag `%s' given to zle -f", *flag);
 | |
| 		    ret = 1;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     Thingy t;
 | |
|     struct modifier modsave = zmod;
 | |
|     int ret, saveflag = 0, setbindk = 0;
 | |
|     char *wname = *args++, *keymap_restore = NULL, *keymap_tmp;
 | |
| 
 | |
|     if (!wname)
 | |
| 	return !zle_usable();
 | |
| 
 | |
|     if(!zle_usable()) {
 | |
| 	zwarnnam(name, "widgets can only be called when ZLE is active");
 | |
| 	return 1;
 | |
|     }
 | |
| 
 | |
|     UNMETACHECK();
 | |
| 
 | |
|     while (*args && **args == '-') {
 | |
| 	char *num;
 | |
| 	if (!args[0][1] || args[0][1] == '-') {
 | |
| 	    args++;
 | |
| 	    break;
 | |
| 	}
 | |
| 	while (*++(*args)) {
 | |
| 	    switch (**args) {
 | |
| 	    case 'n':
 | |
| 		num = args[0][1] ? args[0]+1 : args[1];
 | |
| 		if (!num) {
 | |
| 		    zwarnnam(name, "number expected after -%c", **args);
 | |
| 		    return 1;
 | |
| 		}
 | |
| 		if (!args[0][1])
 | |
| 		    *++args = "" - 1;
 | |
| 		saveflag = 1;
 | |
| 		zmod.mult = atoi(num);
 | |
| 		zmod.flags |= MOD_MULT;
 | |
| 		break;
 | |
| 	    case 'N':
 | |
| 		saveflag = 1;
 | |
| 		zmod.mult = 1;
 | |
| 		zmod.flags &= ~MOD_MULT;
 | |
| 		break;
 | |
| 	    case 'K':
 | |
| 		keymap_tmp = args[0][1] ? args[0]+1 : args[1];
 | |
| 		if (!keymap_tmp) {
 | |
| 		    zwarnnam(name, "keymap expected after -%c", **args);
 | |
| 		    return 1;
 | |
| 		}
 | |
| 		if (!args[0][1])
 | |
| 		    *++args = "" - 1;
 | |
| 		keymap_restore = dupstring(curkeymapname);
 | |
| 		if (selectkeymap(keymap_tmp, 0))
 | |
| 		    return 1;
 | |
| 		break;
 | |
| 	    case 'w':
 | |
| 		setbindk = 1;
 | |
| 		break;
 | |
| 	    default:
 | |
| 		zwarnnam(name, "unknown option: %s", *args);
 | |
| 		return 1;
 | |
| 	    }
 | |
| 	}
 | |
| 	args++;
 | |
|     }
 | |
| 
 | |
|     t = rthingy(wname);
 | |
|     ret = execzlefunc(t, args, setbindk);
 | |
|     unrefthingy(t);
 | |
|     if (saveflag)
 | |
| 	zmod = modsave;
 | |
|     if (keymap_restore)
 | |
| 	selectkeymap(keymap_restore, 0);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Flag that the user has requested the terminal be trashed
 | |
|  * for whatever use.  We attempt to keep the tty settings in
 | |
|  * this mode synced with the normal (non-zle) settings unless
 | |
|  * they are frozen.
 | |
|  */
 | |
| 
 | |
| /**/
 | |
| int fetchttyinfo;
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_invalidate(UNUSED(char *name), UNUSED(char **args), UNUSED(Options ops), UNUSED(char func))
 | |
| {
 | |
|     /*
 | |
|      * Trash zle if trashable, but only indicate that zle is usable
 | |
|      * if it's possible to call a zle widget next.  This is not
 | |
|      * true if a completion widget is active.
 | |
|      */
 | |
|     if (zleactive) {
 | |
| 	int wastrashed = trashedzle;
 | |
| 	trashzle();
 | |
| 	if (!wastrashed && (zlereadflags & ZLRF_NOSETTY)) {
 | |
| 	    /*
 | |
| 	     * We normally wouldn't have restored the terminal
 | |
| 	     * in this case, but as it's at user request we do
 | |
| 	     * so (hence the apparently illogical sense of the
 | |
| 	     * second part of the test).
 | |
| 	     */
 | |
| 	    settyinfo(&shttyinfo);
 | |
| 	}
 | |
| 	fetchttyinfo = 1;
 | |
| 	return 0;
 | |
|     } else
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_fd(char *name, char **args, Options ops, UNUSED(char func))
 | |
| {
 | |
|     int fd = 0, i, found = 0;
 | |
|     char *endptr;
 | |
| 
 | |
|     if (*args) {
 | |
| 	fd = (int)zstrtol(*args, &endptr, 10);
 | |
| 
 | |
| 	if (*endptr || fd < 0) {
 | |
| 	    zwarnnam(name, "Bad file descriptor number for -F: %s", *args);
 | |
| 	    return 1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (OPT_ISSET(ops,'L') || !*args) {
 | |
| 	/* Listing handlers. */
 | |
| 	if (*args && args[1]) {
 | |
| 	    zwarnnam(name, "too many arguments for -FL");
 | |
| 	    return 1;
 | |
| 	}
 | |
| 	for (i = 0; i < nwatch; i++) {
 | |
| 	    Watch_fd watch_fd = watch_fds + i;
 | |
| 	    if (*args && watch_fd->fd != fd)
 | |
| 		continue;
 | |
| 	    found = 1;
 | |
| 	    printf("%s -F %s%d %s\n", name, watch_fd->widget ? "-w " : "",
 | |
| 		   watch_fd->fd, watch_fd->func);
 | |
| 	}
 | |
| 	/* only return status 1 if fd given and not found */
 | |
| 	return *args && !found;
 | |
|     }
 | |
| 
 | |
|     if (args[1]) {
 | |
| 	/* Adding or replacing a handler */
 | |
| 	char *funcnam = ztrdup(args[1]);
 | |
| 	if (nwatch) {
 | |
| 	    for (i = 0; i < nwatch; i++) {
 | |
| 		Watch_fd watch_fd = watch_fds + i;
 | |
| 		if (watch_fd->fd == fd) {
 | |
| 		    zsfree(watch_fd->func);
 | |
| 		    watch_fd->func = funcnam;
 | |
| 		    watch_fd->widget = OPT_ISSET(ops,'w') ? 1 : 0;
 | |
| 		    found = 1;
 | |
| 		    break;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	if (!found) {
 | |
| 	    /* zrealloc handles NULL pointers, so OK for first time through */
 | |
| 	    int newnwatch = nwatch+1;
 | |
| 	    Watch_fd new_fd;
 | |
| 	    watch_fds = (Watch_fd)zrealloc(watch_fds, 
 | |
| 					   newnwatch * sizeof(struct watch_fd));
 | |
| 	    new_fd = watch_fds + nwatch;
 | |
| 	    new_fd->fd = fd;
 | |
| 	    new_fd->func = funcnam;
 | |
| 	    new_fd->widget = OPT_ISSET(ops,'w') ? 1 : 0;
 | |
| 	    nwatch = newnwatch;
 | |
| 	}
 | |
|     } else {
 | |
| 	/* Deleting a handler */
 | |
| 	for (i = 0; i < nwatch; i++) {
 | |
| 	    Watch_fd watch_fd = watch_fds + i;
 | |
| 	    if (watch_fd->fd == fd) {
 | |
| 		int newnwatch = nwatch-1;
 | |
| 		Watch_fd new_fds;
 | |
| 
 | |
| 		zsfree(watch_fd->func);
 | |
| 		if (newnwatch) {
 | |
| 		    new_fds = zalloc(newnwatch*sizeof(struct watch_fd));
 | |
| 		    if (i) {
 | |
| 			memcpy(new_fds, watch_fds, i*sizeof(struct watch_fd));
 | |
| 		    }
 | |
| 		    if (i < newnwatch) {
 | |
| 			memcpy(new_fds+i, watch_fds+i+1,
 | |
| 			       (newnwatch-i)*sizeof(struct watch_fd));
 | |
| 		    }
 | |
| 		} else {
 | |
| 		    new_fds = NULL;
 | |
| 		}
 | |
| 		zfree(watch_fds, nwatch*sizeof(struct watch_fd));
 | |
| 		watch_fds = new_fds;
 | |
| 		nwatch = newnwatch;
 | |
| 		found = 1;
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (!found) {
 | |
| 	    zwarnnam(name, "No handler installed for fd %d", fd);
 | |
| 	    return 1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| static int
 | |
| bin_zle_transform(char *name, char **args, Options ops, UNUSED(char func))
 | |
| {
 | |
|     /*
 | |
|      * -1: too few arguments
 | |
|      * 0: just right
 | |
|      * 1: too many arguments
 | |
|      * 2: first argument not recognised
 | |
|      */
 | |
|     int badargs = 0;
 | |
| 
 | |
|     if (OPT_ISSET(ops,'L')) {
 | |
| 	if (args[0]) {
 | |
| 	    if (args[1]) {
 | |
| 		badargs = 1;
 | |
| 	    } else if (strcmp(args[0], "tc")) {
 | |
| 		badargs = 2;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (!badargs && tcout_func_name) {
 | |
| 	    fputs("zle -T tc ", stdout);
 | |
| 	    quotedzputs(tcout_func_name, stdout);
 | |
| 	    putchar('\n');
 | |
| 	}
 | |
|     } else if (OPT_ISSET(ops,'r')) {
 | |
| 	if (!args[0]) {
 | |
| 	    badargs = -1;
 | |
| 	} else if (args[1]) {
 | |
| 	    badargs = 1;
 | |
| 	} else if (tcout_func_name) {
 | |
| 	    zsfree(tcout_func_name);
 | |
| 	    tcout_func_name = NULL;
 | |
| 	}
 | |
|     } else {
 | |
| 	if (!args[0] || !args[1]) {
 | |
| 	    badargs = -1;
 | |
| 	    /* we've already checked args <= 2 */
 | |
| 	} else {
 | |
| 	    if (!strcmp(args[0], "tc")) {
 | |
| 		if (tcout_func_name) {
 | |
| 		    zsfree(tcout_func_name);
 | |
| 		}
 | |
| 		tcout_func_name = ztrdup(args[1]);
 | |
| 	    } else {
 | |
| 		badargs = 2;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (badargs) {
 | |
| 	if (badargs == 2) {
 | |
| 	    zwarnnam(name, "-T: no such transformation '%s'", args[0]);
 | |
| 	} else {
 | |
| 	    char *way = (badargs > 0) ? "many" : "few";
 | |
| 	    zwarnnam(name, "too %s arguments for option -T", way);
 | |
| 	}
 | |
| 	return 1;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*******************/
 | |
| /* initialiasation */
 | |
| /*******************/
 | |
| 
 | |
| /**/
 | |
| void
 | |
| init_thingies(void)
 | |
| {
 | |
|     Thingy t;
 | |
| 
 | |
|     createthingytab();
 | |
|     for(t = thingies; t->nam; t++)
 | |
| 	thingytab->addnode(thingytab, t->nam, t);
 | |
| }
 |