updated for version 7.0031

This commit is contained in:
Bram Moolenaar
2005-01-05 22:19:46 +00:00
parent 1280586e4b
commit 3d60ec2aaf
5 changed files with 433 additions and 181 deletions

View File

@ -1,4 +1,4 @@
*todo.txt* For Vim version 7.0aa. Last change: 2005 Jan 04
*todo.txt* For Vim version 7.0aa. Last change: 2005 Jan 05
VIM REFERENCE MANUAL by Bram Moolenaar
@ -30,16 +30,16 @@ be worked on, but only if you sponsor Vim development. See |sponsor|.
*known-bugs*
-------------------- Known bugs and current work -----------------------
When 'insertmode' is set CTRL-I 4isometext<Esc> and then some typing hangs
Vim. (Jens Paulus)
:let completion stops after the first argument.
List data type:
- When removing items from the condition stack may free cs_fors.
- don't copy the list, use a list-watcher to adjust the item pointer when it's
deleted.
- "for a in list"
Make copy of the list to avoid trouble when it changes. As one big block?
- "for [a, b] in [[1, 2], [3, 4]]"
- support list generator: items are obtained with a function by index.
"range(1, 400, 2)" creates one.
- == (same value) and "is" (same list)
- store in viminfo: read_viminfo_varlist()
- add many functions:
call(func, list) call function
keys(list) list of all indexes 0 - (len(list) - 1)
@ -56,6 +56,8 @@ List data type:
getval(list, idx[, default]) get value at idx or default
file2lines()
file2words()
str2list() parse string to list in several ways: white
separated, [] form, etc.
Fix the error numbers E999 in eval.c.
Function reference: Define a nameless (numbered) function and assign

View File

@ -1,4 +1,4 @@
*version7.txt* For Vim version 7.0aa. Last change: 2005 Jan 04
*version7.txt* For Vim version 7.0aa. Last change: 2005 Jan 05
VIM REFERENCE MANUAL by Bram Moolenaar
@ -645,4 +645,6 @@ Use a Vim command to fix all fileformats to dos before executing the tests.
When using ":new" and the file fits in the window, lines could still be above
the window. Now remove empty lines instead of keeping the relative position.
Cmdline completion didn't work after ":let var1 var<Tab>".
vim:tw=78:ts=8:ft=help:norl:

View File

@ -82,13 +82,23 @@ struct listitem_S
typedef struct listitem_S listitem;
/*
* Structure to hold the info about a list.
* Struct used by those that are using an item in a list.
*/
typedef struct listwatch_S
{
listitem *lw_item; /* item being watched */
struct listwatch_S *lw_next; /* next watcher */
} listwatch;
/*
* Structure to hold info about a list.
*/
struct listvar_S
{
int lv_refcount; /* reference count */
listitem *lv_first; /* first item, NULL if none */
listitem *lv_last; /* last item, NULL if none */
listwatch *lv_watch; /* first watcher, NULL if none */
};
typedef struct listvar_S listvar;
@ -169,6 +179,18 @@ struct funccall
int level; /* top nesting level of executed function */
};
/*
* Info used by a ":for" loop.
*/
typedef struct forinfo_S
{
int fi_semicolon; /* TRUE if ending in '; var]' */
int fi_varcount; /* nr of variables in the list */
listwatch fi_lw; /* keep an eye on the item used. */
listvar *fi_list; /* list being used */
} forinfo;
/*
* Return the name of the executed function.
*/
@ -504,10 +526,14 @@ static void call_user_func __ARGS((ufunc_T *fp, int argcount, typeval *argvars,
static char_u * make_expanded_name __ARGS((char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end));
static int ex_let_vars __ARGS((char_u *arg, typeval *tv, int copy, int semicolon, int var_count, char_u *nextchars));
static char_u *skip_var_list __ARGS((char_u *arg, int *var_count, int *semicolon));
static char_u *skip_var_one __ARGS((char_u *arg));
static void list_all_vars __ARGS((void));
static char_u *list_arg_vars __ARGS((exarg_T *eap, char_u *arg));
static char_u *ex_let_one __ARGS((char_u *arg, typeval *tv, int copy, char_u *endchars));
static char_u *set_var_idx __ARGS((char_u *name, char_u *ip, typeval *rettv, int copy, char_u *endchars));
static void list_add_watch __ARGS((listvar *l, listwatch *lw));
/*
* Set an internal variable to a string value. Creates the variable if it does
@ -1021,14 +1047,16 @@ ex_let(eap)
int i;
int var_count = 0;
int semicolon = 0;
listvar *l;
listitem *item;
if (*arg != '[')
expr = vim_strchr(find_name_end(arg, NULL, NULL, TRUE), '=');
if (*arg != '[' && expr == NULL)
expr = skip_var_list(arg, &var_count, &semicolon);
if (expr == NULL)
return;
expr = vim_strchr(expr, '=');
if (expr == NULL)
{
if (!ends_excmd(*arg))
if (*arg == '[')
EMSG(_(e_invarg));
else if (!ends_excmd(*arg))
/* ":let var1 var2" */
arg = list_arg_vars(eap, arg);
else if (!eap->skip)
@ -1038,54 +1066,11 @@ ex_let(eap)
}
else
{
if (*arg == '[')
{
/* ":let [a, b] = expr": find the matching ']' to get to the
* expression. */
while (1)
{
arg = skipwhite(arg + 1);
if (vim_strchr((char_u *)"$@&", *arg) != NULL)
++arg;
expr = find_name_end(arg, NULL, NULL, TRUE);
if (expr == arg)
{
EMSG2(_(e_invarg2), arg);
return;
}
++var_count;
arg = skipwhite(expr);
if (*arg == ']')
break;
else if (*arg == ';')
{
if (semicolon == 1)
{
EMSG(_("Double ; in :let"));
return;
}
semicolon = 1;
}
else if (*arg != ',')
{
EMSG2(_(e_invarg2), arg);
return;
}
}
/* check for '=' after the ']' */
expr = skipwhite(arg + 1);
if (*expr != '=')
{
EMSG(_(e_letunexp));
return;
}
}
expr = skipwhite(expr + 1);
if (eap->skip)
++emsg_skip;
i = eval0(expr + 1, &rettv, &eap->nextcmd, !eap->skip);
i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
if (eap->skip)
{
if (i != FAIL)
@ -1094,72 +1079,171 @@ ex_let(eap)
}
else if (i != FAIL)
{
/* Move "arg" back to the variable name(s). */
arg = eap->arg;
if (*arg != '[')
{
/* ":let var = expr" */
(void)ex_let_one(arg, &rettv, FALSE, (char_u *)"=");
}
else
{
/* ":let [v1, v2] = list" */
l = rettv.vval.v_list;
if (rettv.v_type != VAR_LIST || l == NULL)
EMSG(_("E999: List required"));
else
{
i = list_len(l);
if (semicolon == 0 && var_count < i)
EMSG(_("E999: Less targets than List items"));
else if (var_count - semicolon > i)
EMSG(_("E999: More targets than List items"));
else
{
item = l->lv_first;
while (*arg != ']')
{
arg = skipwhite(arg + 1);
arg = ex_let_one(arg, &item->li_tv,
TRUE, (char_u *)",;]");
item = item->li_next;
if (arg == NULL)
break;
arg = skipwhite(arg);
if (*arg == ';')
{
/* Put the rest of the list (may be empty) in
* the var after ';'. */
l = list_alloc();
if (l == NULL)
break;
while (item != NULL)
{
list_append_tv(l, &item->li_tv);
item = item->li_next;
}
list_unref(rettv.vval.v_list);
rettv.vval.v_list = l;
l->lv_refcount = 1;
(void)ex_let_one(skipwhite(arg + 1), &rettv,
FALSE, (char_u *)"]");
break;
}
else if (*arg != ',' && *arg != ']')
{
EMSG2(_(e_intern2), "ex_let()");
break;
}
}
}
}
}
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
(char_u *)"=");
clear_tv(&rettv);
}
}
}
/*
* Assign the typevalue "tv" to the variable or variables at "arg_start".
* Handles both "var" with any type and "[var, var; var]" with a list type.
* Returns OK or FAIL;
*/
static int
ex_let_vars(arg_start, tv, copy, semicolon, var_count, nextchars)
char_u *arg_start;
typeval *tv;
int copy; /* copy values from "tv", don't move */
int semicolon; /* from skip_var_list() */
int var_count; /* from skip_var_list() */
char_u *nextchars; /* characters that must follow or NULL */
{
char_u *arg = arg_start;
listvar *l;
int i;
listitem *item;
typeval ltv;
if (*arg != '[')
{
/*
* ":let var = expr" or ":for var in list"
*/
if (ex_let_one(arg, tv, copy, nextchars) == NULL)
return FAIL;
return OK;
}
/*
* ":let [v1, v2] = list" or ":for [v1, v2] in listlist"
*/
l = tv->vval.v_list;
if (tv->v_type != VAR_LIST || l == NULL)
{
EMSG(_(e_listreq));
return FAIL;
}
i = list_len(l);
if (semicolon == 0 && var_count < i)
{
EMSG(_("E999: Less targets than List items"));
return FAIL;
}
if (var_count - semicolon > i)
{
EMSG(_("E999: More targets than List items"));
return FAIL;
}
item = l->lv_first;
while (*arg != ']')
{
arg = skipwhite(arg + 1);
arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]");
item = item->li_next;
if (arg == NULL)
return FAIL;
arg = skipwhite(arg);
if (*arg == ';')
{
/* Put the rest of the list (may be empty) in the var after ';'.
* Create a new list for this. */
l = list_alloc();
if (l == NULL)
return FAIL;
while (item != NULL)
{
list_append_tv(l, &item->li_tv);
item = item->li_next;
}
ltv.v_type = VAR_LIST;
ltv.vval.v_list = l;
l->lv_refcount = 1;
arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, (char_u *)"]");
clear_tv(&ltv);
if (arg == NULL)
return FAIL;
break;
}
else if (*arg != ',' && *arg != ']')
{
EMSG2(_(e_intern2), "ex_let_vars()");
return FAIL;
}
}
return OK;
}
/*
* Skip over assignable variable "var" or list of variables "[var, var]".
* Used for ":let varvar = expr" and ":for varvar in expr".
* For "[var, var]" increment "*var_count" for each variable.
* for "[var, var; var]" set "semicolon".
* Return NULL for an error.
*/
static char_u *
skip_var_list(arg, var_count, semicolon)
char_u *arg;
int *var_count;
int *semicolon;
{
char_u *p, *s;
if (*arg == '[')
{
/* "[var, var]": find the matching ']'. */
p = arg;
while (1)
{
p = skipwhite(p + 1); /* skip whites after '[', ';' or ',' */
s = skip_var_one(p);
if (s == p)
{
EMSG2(_(e_invarg2), p);
return NULL;
}
++*var_count;
p = skipwhite(s);
if (*p == ']')
break;
else if (*p == ';')
{
if (*semicolon == 1)
{
EMSG(_("Double ; in list of variables"));
return NULL;
}
*semicolon = 1;
}
else if (*p != ',')
{
EMSG2(_(e_invarg2), p);
return NULL;
}
}
return p + 1;
}
else
return skip_var_one(arg);
}
static char_u *
skip_var_one(arg)
char_u *arg;
{
if (vim_strchr((char_u *)"$@&", *arg) != NULL)
++arg;
return find_name_end(arg, NULL, NULL, TRUE);
}
static void
list_all_vars()
{
@ -1309,7 +1393,7 @@ ex_let_one(arg, tv, copy, endchars)
char_u *arg; /* points to variable name */
typeval *tv; /* value to assign to variable */
int copy; /* copy value from "tv" */
char_u *endchars; /* valid chars after variable name */
char_u *endchars; /* valid chars after variable name or NULL */
{
int c1;
char_u *name;
@ -1331,7 +1415,8 @@ ex_let_one(arg, tv, copy, endchars)
EMSG2(_(e_invarg2), name - 1);
else
{
if (vim_strchr(endchars, *skipwhite(arg)) == NULL)
if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg)) == NULL)
EMSG(_(e_letunexp));
else
{
@ -1360,7 +1445,8 @@ ex_let_one(arg, tv, copy, endchars)
{
/* Find the end of the name. */
p = find_option_end(&arg, &opt_flags);
if (p == NULL || vim_strchr(endchars, *skipwhite(p)) == NULL)
if (p == NULL || (endchars != NULL
&& vim_strchr(endchars, *skipwhite(p)) == NULL))
EMSG(_(e_letunexp));
else
{
@ -1379,7 +1465,8 @@ ex_let_one(arg, tv, copy, endchars)
else if (*arg == '@')
{
++arg;
if (vim_strchr(endchars, *skipwhite(arg + 1)) == NULL)
if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg + 1)) == NULL)
EMSG(_(e_letunexp));
else
{
@ -1415,7 +1502,8 @@ ex_let_one(arg, tv, copy, endchars)
}
else if (*p == '[')
arg_end = set_var_idx(arg, p, tv, copy, endchars);
else if (vim_strchr(endchars, *skipwhite(p)) == NULL)
else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(p)) == NULL)
EMSG(_(e_letunexp));
else if (STRNCMP(arg, "b:changedtick", 13) == 0
&& !eval_isnamec(arg[13]))
@ -1505,7 +1593,7 @@ set_var_idx(name, ip, rettv, copy, endchars)
if (p != NULL)
{
if (vim_strchr(endchars, *p) == NULL)
if (endchars != NULL && vim_strchr(endchars, *p) == NULL)
{
EMSG(_(e_letunexp));
p = NULL;
@ -1525,6 +1613,157 @@ set_var_idx(name, ip, rettv, copy, endchars)
return p;
}
/*
* Add a watcher to a list.
*/
static void
list_add_watch(l, lw)
listvar *l;
listwatch *lw;
{
lw->lw_next = l->lv_watch;
l->lv_watch = lw;
}
/*
* Remove a watches from a list.
* No warning when it isn't found...
*/
static void
list_rem_watch(l, lwrem)
listvar *l;
listwatch *lwrem;
{
listwatch *lw, **lwp;
lwp = &l->lv_watch;
for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
{
if (lw == lwrem)
{
*lwp = lw->lw_next;
break;
}
lwp = &lw->lw_next;
}
}
/*
* Just before removing an item from a list: advance watchers to the next
* item.
*/
static void
list_fix_watch(l, item)
listvar *l;
listitem *item;
{
listwatch *lw;
for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next)
if (lw->lw_item == item)
lw->lw_item = item->li_next;
}
/*
* Evaluate the expression used in a ":for var in expr" command.
* "arg" points to "var".
* Set "*errp" to TRUE for an error, FALSE otherwise;
* Return a pointer that holds the info. Null when there is an error.
*/
void *
eval_for_line(arg, errp, nextcmdp, skip)
char_u *arg;
int *errp;
char_u **nextcmdp;
int skip;
{
forinfo *fi;
char_u *expr;
typeval tv;
listvar *l;
*errp = TRUE; /* default: there is an error */
fi = (forinfo *)alloc_clear(sizeof(forinfo));
if (fi == NULL)
return NULL;
expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
if (expr == NULL)
return fi;
expr = skipwhite(expr);
if (expr[0] != 'i' || expr[1] != 'n' || !vim_iswhite(expr[2]))
{
EMSG(_("E999: Missing \"in\" after :for"));
return fi;
}
if (skip)
++emsg_skip;
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK)
{
*errp = FALSE;
if (!skip)
{
l = tv.vval.v_list;
if (tv.v_type != VAR_LIST || l == NULL)
EMSG(_(e_listreq));
else
{
fi->fi_list = l;
list_add_watch(l, &fi->fi_lw);
fi->fi_lw.lw_item = l->lv_first;
}
}
}
if (skip)
--emsg_skip;
return fi;
}
/*
* Use the first item in a ":for" list. Advance to the next.
* Assign the values to the variable (list). "arg" points to the first one.
* Return TRUE when a valid item was found, FALSE when at end of list or
* something wrong.
*/
int
next_for_item(fi_void, arg)
void *fi_void;
char_u *arg;
{
forinfo *fi = (forinfo *)fi_void;
int result;
listitem *item;
item = fi->fi_lw.lw_item;
if (item == NULL)
result = FALSE;
else
{
fi->fi_lw.lw_item = item->li_next;
result = (ex_let_vars(arg, &item->li_tv, TRUE,
fi->fi_semicolon, fi->fi_varcount, NULL) == OK);
}
return result;
}
/*
* Free the structure used to store info used by ":for".
*/
void
free_for_info(fi_void)
void *fi_void;
{
forinfo *fi = (forinfo *)fi_void;
if (fi->fi_list != NULL)
list_rem_watch(fi->fi_list, &fi->fi_lw);
vim_free(fi);
}
#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
void
@ -1535,10 +1774,27 @@ set_context_for_expression(xp, arg, cmdidx)
{
int got_eq = FALSE;
int c;
char_u *p;
xp->xp_context = cmdidx == CMD_let ? EXPAND_USER_VARS
: cmdidx == CMD_call ? EXPAND_FUNCTIONS
: EXPAND_EXPRESSION;
if (cmdidx == CMD_let)
{
xp->xp_context = EXPAND_USER_VARS;
if (vim_strchr(arg, '=') == NULL)
{
/* ":let var1 var2 ...": find last space. */
for (p = arg + STRLEN(arg); p > arg; )
{
xp->xp_pattern = p;
p = mb_ptr_back(arg, p);
if (vim_iswhite(*p))
break;
}
return;
}
}
else
xp->xp_context = cmdidx == CMD_call ? EXPAND_FUNCTIONS
: EXPAND_EXPRESSION;
while ((xp->xp_pattern = vim_strpbrk(arg,
(char_u *)"\"'+-*/%.=!?~|&$([<>,#")) != NULL)
{
@ -1601,7 +1857,9 @@ set_context_for_expression(xp, arg, cmdidx)
xp->xp_context = EXPAND_EXPRESSION;
}
else
xp->xp_context = EXPAND_NOTHING;
/* Doesn't look like something valid, expand as an expression
* anyway. */
xp->xp_context = EXPAND_EXPRESSION;
arg = xp->xp_pattern;
if (*arg != NUL)
while ((c = *++arg) != NUL && (c == ' ' || c == '\t'))
@ -3303,7 +3561,7 @@ listitem_alloc()
}
/*
* Free a list item. Also clears the value;
* Free a list item. Also clears the value. Does not notify watchers.
*/
static void
listitem_free(item)
@ -3471,6 +3729,7 @@ list_getrem(l, n)
item = list_find(l, n);
if (item != NULL)
{
list_fix_watch(l, item); /* notify watchers */
if (item->li_next == NULL)
l->lv_last = item->li_prev;
else
@ -11806,8 +12065,7 @@ read_viminfo_varlist(virp, writing)
{
char_u *tab;
int is_string = FALSE;
typeval *tvp = NULL;
char_u *val;
typeval tv;
if (!writing && (find_viminfo_parameter('!') != NULL))
{
@ -11821,29 +12079,20 @@ read_viminfo_varlist(virp, writing)
tab = vim_strchr(tab, '\t');
if (tab != NULL)
{
/* create a typeval to hold the value */
if (is_string)
{
val = viminfo_readstring(virp,
tv.v_type = VAR_STRING;
tv.vval.v_string = viminfo_readstring(virp,
(int)(tab - virp->vir_line + 1), TRUE);
if (val != NULL)
tvp = alloc_string_tv(val);
}
else
{
tvp = alloc_tv();
if (tvp != NULL)
{
tvp->v_type = VAR_NUMBER;
tvp->vval.v_number = atol((char *)tab + 1);
}
}
/* assign the value to the variable */
if (tvp != NULL)
{
set_var(virp->vir_line + 1, tvp, FALSE);
free_tv(tvp);
tv.v_type = VAR_NUMBER;
tv.vval.v_number = atol((char *)tab + 1);
}
set_var(virp->vir_line + 1, &tv, FALSE);
if (is_string)
vim_free(tv.vval.v_string);
}
}
}
@ -11878,11 +12127,7 @@ write_viminfo_varlist(fp)
{
case VAR_STRING: s = "STR"; break;
case VAR_NUMBER: s = "NUM"; break;
case VAR_LIST: s = "LST"; break;
case VAR_FUNC: s = "FUN"; break;
default:
EMSGN(_("E999: Internal error: write_viminfo_varlist(): %ld"), (long)this_var->tv.v_type);
s = "ERR";
default: continue;
}
fprintf(fp, "!%s\t%s\t", this_var->v_name, s);
viminfo_writestring(fp, tv2string(&this_var->tv, &tofree));
@ -11905,34 +12150,33 @@ store_session_globals(fd)
for (i = gap->ga_len; --i >= 0; )
{
this_var = &VAR_GAP_ENTRY(i, gap);
if (this_var->v_name != NULL)
if (this_var->v_name != NULL
&& (this_var->tv.v_type == VAR_NUMBER
|| this_var->tv.v_type == VAR_STRING)
&& var_flavour(this_var->v_name) == VAR_FLAVOUR_SESSION)
{
if (var_flavour(this_var->v_name) == VAR_FLAVOUR_SESSION)
{
/* Escapse special characters with a backslash. Turn a LF and
* CR into \n and \r. */
p = vim_strsave_escaped(get_var_string(this_var),
/* Escape special characters with a backslash. Turn a LF and
* CR into \n and \r. */
p = vim_strsave_escaped(get_var_string(this_var),
(char_u *)"\\\"\n\r");
if (p == NULL) /* out of memory */
continue;
for (t = p; *t != NUL; ++t)
if (*t == '\n')
*t = 'n';
else if (*t == '\r')
*t = 'r';
if ((fprintf(fd, "let %s = %c%s%c",
this_var->v_name,
(this_var->tv.v_type == VAR_STRING) ? '"' : ' ',
p,
(this_var->tv.v_type == VAR_STRING) ? '"' : ' ') < 0)
|| put_eol(fd) == FAIL)
{
vim_free(p);
return FAIL;
}
if (p == NULL) /* out of memory */
continue;
for (t = p; *t != NUL; ++t)
if (*t == '\n')
*t = 'n';
else if (*t == '\r')
*t = 'r';
if ((fprintf(fd, "let %s = %c%s%c",
this_var->v_name,
(this_var->tv.v_type == VAR_STRING) ? '"' : ' ',
p,
(this_var->tv.v_type == VAR_STRING) ? '"' : ' ') < 0)
|| put_eol(fd) == FAIL)
{
vim_free(p);
return FAIL;
}
vim_free(p);
}
}
return OK;

View File

@ -3258,13 +3258,13 @@ ex_append(eap)
if (eap->getline == NULL)
theline = getcmdline(
#ifdef FEAT_EVAL
eap->cstack->cs_whilelevel > 0 ? -1 :
eap->cstack->cs_looplevel > 0 ? -1 :
#endif
NUL, 0L, 0);
else
theline = eap->getline(
#ifdef FEAT_EVAL
eap->cstack->cs_whilelevel > 0 ? -1 :
eap->cstack->cs_looplevel > 0 ? -1 :
#endif
NUL, eap->cookie, 0);
lines_left = Rows - 1;

View File

@ -342,6 +342,8 @@ EX(CMD_endif, "endif", ex_endif,
TRLBAR|SBOXOK|CMDWIN),
EX(CMD_endfunction, "endfunction", ex_endfunction,
TRLBAR|CMDWIN),
EX(CMD_endfor, "endfor", ex_endwhile,
TRLBAR|SBOXOK|CMDWIN),
EX(CMD_endtry, "endtry", ex_endtry,
TRLBAR|SBOXOK|CMDWIN),
EX(CMD_endwhile, "endwhile", ex_endwhile,
@ -382,6 +384,8 @@ EX(CMD_folddoclosed, "folddoclosed", ex_folddo,
RANGE|DFLALL|NEEDARG|EXTRA|NOTRLCOM),
EX(CMD_foldopen, "foldopen", ex_foldopen,
RANGE|BANG|WHOLEFOLD|TRLBAR|SBOXOK|CMDWIN),
EX(CMD_for, "for", ex_while,
EXTRA|NOTRLCOM|SBOXOK|CMDWIN),
EX(CMD_function, "function", ex_function,
EXTRA|BANG|CMDWIN),
EX(CMD_global, "global", ex_global,