mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-28 06:30:57 +02:00
Back off previous fix as this only covered some subset of problems. Remaining problems happend after reset-prompt in TRAPINT. One was in complist and is fixed by not attempting to list after an error or interrupt. The other was owing to not resetting clearflag when ZLE was re-entered.
1012 lines
24 KiB
C
1012 lines
24 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 an 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 if (!strcmp(*flag, "vichange")) {
|
|
if (invicmdmode()) {
|
|
startvichange(-1);
|
|
if (zmod.flags & (MOD_MULT|MOD_TMULT)) {
|
|
Param pm = (Param) paramtab->getnode(paramtab, "NUMERIC");
|
|
if (pm && pm->node.flags & PM_SPECIAL)
|
|
pm->node.flags &= ~PM_UNSET;
|
|
}
|
|
}
|
|
} 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, remetafy;
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* zle is callable in traps, so we can't be sure the line is
|
|
* in its normal state.
|
|
*/
|
|
if (zlemetaline) {
|
|
unmetafy_line();
|
|
remetafy = 1;
|
|
} else
|
|
remetafy = 0;
|
|
|
|
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);
|
|
if (remetafy)
|
|
metafy_line();
|
|
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);
|
|
if (remetafy)
|
|
metafy_line();
|
|
return 1;
|
|
}
|
|
if (!args[0][1])
|
|
*++args = "" - 1;
|
|
keymap_restore = dupstring(curkeymapname);
|
|
if (selectkeymap(keymap_tmp, 0)) {
|
|
if (remetafy)
|
|
metafy_line();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 'w':
|
|
setbindk = 1;
|
|
break;
|
|
default:
|
|
zwarnnam(name, "unknown option: %s", *args);
|
|
if (remetafy)
|
|
metafy_line();
|
|
return 1;
|
|
}
|
|
}
|
|
args++;
|
|
}
|
|
|
|
t = rthingy(wname);
|
|
/* for internal widgets we set bindk except for when getting
|
|
* a vi range to detect a repeated key */
|
|
setbindk = setbindk ||
|
|
(t->widget && (t->widget->flags & (WIDGET_INT | ZLE_VIOPER)) == WIDGET_INT);
|
|
ret = execzlefunc(t, args, setbindk);
|
|
unrefthingy(t);
|
|
if (saveflag)
|
|
zmod = modsave;
|
|
if (keymap_restore)
|
|
selectkeymap(keymap_restore, 0);
|
|
if (remetafy)
|
|
metafy_line();
|
|
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);
|
|
}
|