This is eval.c in view mode; [Download] [Up]
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* eval.c: Expression evaluation.
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
#ifdef HAVE_FCNTL_H
# include <fcntl.h> /* contains setenv() declaration for Amiga */
#endif
struct var
{
char_u *var_name; /* name of variable */
char var_type; /* VAR_NUMBER or VAR_STRING */
union
{
#if SIZEOF_INT <= 3 /* use long if int is smaller than 32 bits */
long var_number; /* number value */
#else
int var_number; /* number value */
#endif
char_u *var_string; /* string value */
} var_val;
};
#define VAR_UNKNOWN 0
#define VAR_NUMBER 1
#define VAR_STRING 2
typedef struct var * VAR;
/*
* All user-defined internal variables are stored in variables.
*/
struct growarray variables;
#define VAR_ENTRY(idx) (((VAR)(variables.ga_data))[idx])
static VAR eval0 __ARGS((char_u *arg));
static VAR eval1 __ARGS((char_u **arg));
static VAR eval2 __ARGS((char_u **arg));
static VAR eval3 __ARGS((char_u **arg));
static VAR eval4 __ARGS((char_u **arg));
static VAR eval5 __ARGS((char_u **arg));
static VAR eval6 __ARGS((char_u **arg));
static VAR get_option_var __ARGS((char_u **arg, int error_msg));
static VAR get_string_var __ARGS((char_u **arg));
static VAR get_env_var __ARGS((char_u **arg));
static VAR get_func_var __ARGS((char_u *name, char_u **arg));
static char_u *get_env_name __ARGS((char_u **arg));
static char_u *get_ident __ARGS((char_u **arg));
static int isnamechar __ARGS((int c));
static VAR get_var_var __ARGS((char_u *name, int error_msg));
static VAR alloc_var __ARGS((void));
static void free_var __ARGS((VAR varp));
static long get_var_number __ARGS((VAR varp));
static char_u *get_var_string __ARGS((VAR varp, char_u *buf));
static int find_var __ARGS((char_u *name));
static void set_var __ARGS((char_u *name, VAR varp));
/*
* Top level evaluation function, returning a boolean.
* Returns TRUE or FALSE.
* Sets "error" to TRUE if there was an error.
*/
int
bool_eval(arg, error)
char_u *arg;
int *error;
{
VAR varp;
int retval;
varp = eval0(arg);
if (varp == NULL)
{
retval = FALSE;
*error = TRUE;
}
else
{
retval = (get_var_number(varp) != 0);
*error = FALSE;
}
free_var(varp);
return retval;
}
/*
* Top level evaluation function, returning a string.
* Returns pointer to allocated memory, or NULL for failure.
*/
char_u *
string_eval(arg)
char_u *arg;
{
VAR varp;
char_u *retval;
char_u sbuf[NUMBUFLEN];
varp = eval0(arg);
if (varp == NULL)
retval = NULL;
else
retval = vim_strsave(get_var_string(varp, sbuf));
free_var(varp);
return retval;
}
/*
* ":let var = expr" command.
*/
void
do_let(arg)
char_u *arg;
{
char_u *expr;
char_u *name;
VAR varp;
char_u *p;
char_u sbuf[NUMBUFLEN];
int c;
#ifndef HAVE_SETENV
char_u *envbuf;
#endif
expr = vim_strchr(arg, '=');
if (expr == NULL)
EMSG("Missing '='");
else
{
varp = eval0(expr + 1);
if (varp != NULL)
{
/*
* ":let $VAR = expr": Set environment variable.
*/
if (*arg == '$')
{
/*
* Find the end of the name;
*/
++arg;
name = get_env_name(&arg);
if (name != NULL)
{
if (*skipwhite(arg) != '=')
EMSG("Unexpected characters before '='");
else
{
p = get_var_string(varp, sbuf);
#ifdef HAVE_SETENV
# ifdef AMIGA
setenv((char *)name, (char *)p);
# else
setenv((char *)name, (char *)p, 1);
# endif
#else
/*
* Putenv does not copy the string, it has to remain
* valid. The allocated memory will never be freed.
*/
envbuf = alloc((unsigned)(STRLEN(p) +
STRLEN(name) + 2));
if (envbuf != NULL)
{
sprintf((char *)envbuf, "%s=%s", name, p);
putenv((char *)envbuf);
}
#endif
}
vim_free(name);
}
}
/*
* ":let 'option' = expr": Set option value.
*/
else if (*arg == '\'')
{
/*
* Find the end of the name;
*/
++arg;
p = vim_strchr(arg, '\'');
if (p == NULL)
EMSG2("Missing quote: %s", arg - 1);
else if (*skipwhite(p + 1) != '=')
EMSG("Unexpected characters before '='");
else
{
*p = NUL;
set_option_value(arg, get_var_number(varp),
get_var_string(varp, sbuf));
*p = '\''; /* put ' back for error messages */
}
}
/*
* ":let var = expr": Set internal variable.
*/
else if (isnamechar(*arg) && !isdigit(*arg))
{
/*
* Find the end of the name;
*/
for (p = arg; isnamechar(*p); ++p)
;
if (*skipwhite(p) != '=')
EMSG("Unexpected characters before '='");
else
{
c = *p;
*p = NUL;
set_var(arg, varp);
*p = c; /* put char back for error messages */
}
}
else
{
EMSG2("Invalid argument: %s", arg);
}
free_var(varp);
}
}
}
/*
* ":unlet var" command.
*/
void
do_unlet(arg)
char_u *arg;
{
char_u *name;
char_u *name_end;
int i;
name_end = skiptowhite(arg);
name = vim_strnsave(arg, (int)(name_end - arg));
i = find_var(name);
vim_free(name);
if (i >= 0) /* existing variable, may need to free string */
{
vim_free(VAR_ENTRY(i).var_name);
VAR_ENTRY(i).var_name = NULL;
if (VAR_ENTRY(i).var_type == VAR_STRING)
vim_free(VAR_ENTRY(i).var_val.var_string);
VAR_ENTRY(i).var_val.var_string = NULL;
}
else /* non-existing variable */
EMSG2("No such variable: %s", arg);
}
/*
* types for expressions.
*/
enum exp_type
{
TYPE_UNKNOWN = 0,
TYPE_EQUAL, /* == */
TYPE_NEQUAL, /* != */
TYPE_GREATER, /* > */
TYPE_GEQUAL, /* >= */
TYPE_SMALLER, /* < */
TYPE_SEQUAL, /* <= */
TYPE_MATCH, /* =~ */
TYPE_NOMATCH /* !~ */
};
/*
* Handle zero level expression. This calls eval1() and handles error message
* and nextcomm.
*/
static VAR
eval0(arg)
char_u *arg;
{
VAR varp;
char_u *p;
p = skipwhite(arg);
varp = eval1(&p);
if (varp == NULL || !ends_excmd(*p))
EMSG2("Invalid expression: %s", arg);
/* TODO: set nextcomm */
return varp;
}
/*
* Handle first level expression:
* expr2 || expr2 || expr2 logical OR
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Returns a variable, or NULL when there is any error.
*/
static VAR
eval1(arg)
char_u **arg;
{
VAR var1, var2;
long n1, n2;
/*
* Get the first variable.
*/
var1 = eval2(arg);
if (var1 == NULL)
return NULL;
/*
* Repeat until there is no following "||".
*/
while ((*arg)[0] == '|' && (*arg)[1] == '|')
{
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
var2 = eval2(arg);
if (var2 == NULL)
{
free_var(var1);
return NULL;
}
/*
* Compute the result.
*/
n1 = get_var_number(var1);
n2 = get_var_number(var2);
free_var(var1);
free_var(var2);
var1 = alloc_var();
if (var1 == NULL)
return NULL;
var1->var_type = VAR_NUMBER;
var1->var_val.var_number = (n1 || n2);
}
return var1;
}
/*
* Handle second level expression:
* expr3 && expr3 && expr3 logical AND
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Returns a variable, or NULL when there is any error.
*/
static VAR
eval2(arg)
char_u **arg;
{
VAR var1, var2;
long n1, n2;
/*
* Get the first variable.
*/
var1 = eval3(arg);
if (var1 == NULL)
return NULL;
/*
* Repeat until there is no following "&&".
*/
while ((*arg)[0] == '&' && (*arg)[1] == '&')
{
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
var2 = eval3(arg);
if (var2 == NULL)
{
free_var(var1);
return NULL;
}
/*
* Compute the result.
*/
n1 = get_var_number(var1);
n2 = get_var_number(var2);
free_var(var1);
free_var(var2);
var1 = alloc_var();
if (var1 == NULL)
return NULL;
var1->var_type = VAR_NUMBER;
var1->var_val.var_number = (n1 && n2);
}
return var1;
}
/*
* Handle third level expression:
* var1 == var2
* var1 != var2
* var1 > var2
* var1 >= var2
* var1 < var2
* var1 <= var2
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Returns a variable, or NULL when there is any error.
*/
static VAR
eval3(arg)
char_u **arg;
{
VAR var1, var2;
char_u *p;
int i;
int type = TYPE_UNKNOWN;
int len = 2;
long n1 = FALSE, n2;
char_u *s1, *s2;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
vim_regexp *prog;
/*
* Get the first variable.
*/
var1 = eval4(arg);
if (var1 == NULL)
return NULL;
p = *arg;
switch (p[0])
{
case '=': if (p[1] == '=')
type = TYPE_EQUAL;
else if (p[1] == '~')
type = TYPE_MATCH;
break;
case '!': if (p[1] == '=')
type = TYPE_NEQUAL;
else if (p[1] == '~')
type = TYPE_NOMATCH;
break;
case '>': if (p[1] != '=')
{
type = TYPE_GREATER;
len = 1;
}
else
type = TYPE_GEQUAL;
break;
case '<': if (p[1] != '=')
{
type = TYPE_SMALLER;
len = 1;
}
else
type = TYPE_SEQUAL;
break;
}
/*
* If there is a comparitive operator, use it.
*/
if (type != TYPE_UNKNOWN)
{
/*
* Get the second variable.
*/
*arg = skipwhite(p + len);
var2 = eval4(arg);
if (var2 == NULL)
{
free_var(var1);
return NULL;
}
/*
* If one of the two variables is a number, compare as a number.
* When using "=~" or "!~", compare as string.
*/
if ((var1->var_type == VAR_NUMBER || var2->var_type == VAR_NUMBER) &&
type != TYPE_MATCH && type != TYPE_NOMATCH)
{
n1 = get_var_number(var1);
n2 = get_var_number(var2);
switch (type)
{
case TYPE_EQUAL: n1 = (n1 == n2); break;
case TYPE_NEQUAL: n1 = (n1 != n2); break;
case TYPE_GREATER: n1 = (n1 > n2); break;
case TYPE_GEQUAL: n1 = (n1 >= n2); break;
case TYPE_SMALLER: n1 = (n1 < n2); break;
case TYPE_SEQUAL: n1 = (n1 <= n2); break;
}
}
else
{
s1 = get_var_string(var1, buf1);
s2 = get_var_string(var2, buf2);
i = STRCMP(s1, s2);
switch (type)
{
case TYPE_EQUAL: n1 = (i == 0); break;
case TYPE_NEQUAL: n1 = (i != 0); break;
case TYPE_GREATER: n1 = (i > 0); break;
case TYPE_GEQUAL: n1 = (i >= 0); break;
case TYPE_SMALLER: n1 = (i < 0); break;
case TYPE_SEQUAL: n1 = (i <= 0); break;
case TYPE_MATCH:
case TYPE_NOMATCH: reg_ic = p_ic;
prog = vim_regcomp(s2, TRUE);
if (prog != NULL)
{
n1 = vim_regexec(prog, s1, TRUE);
vim_free(prog);
}
break;
}
}
free_var(var1);
free_var(var2);
var1 = alloc_var();
if (var1 == NULL)
return NULL;
var1->var_type = VAR_NUMBER;
var1->var_val.var_number = n1;
}
return var1;
}
/*
* Handle fourth level expression:
* + number addition
* - number subtraction
* . string concatenation
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Returns a variable, or NULL when there is any error.
*/
static VAR
eval4(arg)
char_u **arg;
{
VAR var1, var2;
VAR varp;
int c;
long n1 = 0, n2 = 0;
char_u *s1 = NULL, *s2 = NULL;
char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
char_u *p;
/*
* Get the first variable.
*/
var1 = eval5(arg);
if (var1 == NULL)
return NULL;
/*
* Repeat computing, until no '+', '-' or '.' is following.
*/
for (;;)
{
c = **arg;
if (c != '+' && c != '-' && c != '.')
break;
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
var2 = eval5(arg);
if (var2 == NULL)
{
free_var(var1);
return NULL;
}
/*
* Compute the result.
*/
varp = alloc_var();
if (varp == NULL)
{
free_var(var1);
free_var(var2);
return NULL;
}
if (c == '.')
{
s1 = get_var_string(var1, buf1);
s2 = get_var_string(var2, buf2);
varp->var_type = VAR_STRING;
p = alloc((unsigned)(STRLEN(s1) + STRLEN(s2) + 1));
if (p != NULL)
{
STRCPY(p, s1);
STRCAT(p, s2);
varp->var_val.var_string = p;
}
}
else
{
n1 = get_var_number(var1);
n2 = get_var_number(var2);
varp->var_type = VAR_NUMBER;
if (c == '+')
varp->var_val.var_number = (n1 + n2);
else
varp->var_val.var_number = (n1 - n2);
}
free_var(var1);
free_var(var2);
var1 = varp;
}
return var1;
}
/*
* Handle fifth level expression:
* * number multiplication
* / number division
* % number modulo
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Returns a variable, or NULL when there is any error.
*/
static VAR
eval5(arg)
char_u **arg;
{
VAR var1, var2;
int c;
long n1, n2;
/*
* Get the first variable.
*/
var1 = eval6(arg);
if (var1 == NULL)
return NULL;
/*
* Repeat computing, until no '*', '/' or '%' is following.
*/
for (;;)
{
c = **arg;
if (c != '*' && c != '/' && c != '%')
break;
/*
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
var2 = eval6(arg);
if (var2 == NULL)
{
free_var(var1);
return NULL;
}
/*
* Compute the result.
*/
n1 = get_var_number(var1);
n2 = get_var_number(var2);
free_var(var1);
free_var(var2);
var1 = alloc_var();
if (var1 == NULL)
return NULL;
var1->var_type = VAR_NUMBER;
if (c == '*')
var1->var_val.var_number = (n1 * n2);
else if (c == '/')
{
if (n2 == 0) /* give an error message? */
var1->var_val.var_number = 0x7fffffff;
else
var1->var_val.var_number = (n1 / n2);
}
else
{
if (n2 == 0) /* give an error message? */
var1->var_val.var_number = 0;
else
var1->var_val.var_number = (n1 % n2);
}
}
return var1;
}
/*
* Handle sixth level expression:
* number number constant
* "string" string contstant
* 'option-name' option value
* identifier variable value
* $VAR environment variable
* (expression) nested expression
*
* Also handle:
* ! in front logical NOT
* trailing [] subscript in String
*
* "arg" must point to the first non-white of the expression.
* "arg" is advanced to the next non-white after the recognized expression.
*
* Returns a variable, or NULL when there is any error.
*/
static VAR
eval6(arg)
char_u **arg;
{
VAR retvar = NULL;
VAR varp;
long n;
int len;
char_u *s;
char_u buf[NUMBUFLEN];
int not = FALSE;
int val;
char_u *errormsg;
linenr_t dummy;
/*
* Check for '!' character.
*/
if (**arg == '!')
{
not = TRUE;
*arg = skipwhite(*arg + 1);
}
switch (**arg)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': /*
* Number constant.
*/
n = str2nr(*arg, NULL, &len, TRUE, TRUE);
*arg += len;
retvar = alloc_var();
if (retvar != NULL)
{
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = n;
}
break;
case '\"': /*
* String constant: "string".
*/
retvar = get_string_var(arg);
break;
case '\'': /*
* Option value: 'name'
*/
retvar = get_option_var(arg, TRUE);
break;
case '(': /*
* nested expression: (expression).
*/
*arg = skipwhite(*arg + 1);
retvar = eval1(arg);
if (**arg != ')')
EMSG("Missing ')'");
else
++*arg;
break;
case '$': /*
* Environment variable: $VAR.
*/
retvar = get_env_var(arg);
break;
case '%':
case '#':
case '<':
s = eval_vars(*arg, &len, &dummy, &errormsg);
if (errormsg != NULL)
{
if (*errormsg)
emsg(errormsg);
}
else if (s != NULL)
{
retvar = alloc_var();
if (retvar != NULL)
{
retvar->var_type = VAR_STRING;
retvar->var_val.var_string = s;
}
*arg += len;
}
break;
default: /*
* Must be a variable or function name then.
*/
s = get_ident(arg);
if (s != NULL)
{
if (**arg == '(')
retvar = get_func_var(s, arg);
else
retvar = get_var_var(s, TRUE);
}
break;
}
*arg = skipwhite(*arg);
/*
* Handle expr[expr] subscript.
*/
if (**arg == '[' && retvar != NULL)
{
/*
* Get the variable from inside the [].
*/
*arg = skipwhite(*arg + 1);
varp = eval1(arg);
if (varp == NULL)
{
free_var(retvar);
return NULL;
}
s = get_var_string(retvar, buf);
n = get_var_number(varp);
free_var(varp);
/*
* Check for the ']' and skip it.
*/
if (**arg != ']')
{
EMSG("Missing ']'");
free_var(retvar);
return NULL;
}
*arg = skipwhite(*arg + 1);
/*
* The resulting variable is a string of a single character.
* If the index is too big, the result is empty.
*/
varp = alloc_var();
if (varp == NULL)
{
free_var(retvar);
return NULL;
}
varp->var_type = VAR_STRING;
if (n >= (long)STRLEN(s))
varp->var_val.var_string = NULL;
else
varp->var_val.var_string = vim_strnsave(s + n, 1);
free_var(retvar);
retvar = varp;
}
/*
* Apply logical NOT.
*/
if (not && retvar != NULL)
{
val = !get_var_number(retvar);
free_var(retvar);
retvar = alloc_var();
if (retvar != NULL)
{
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = val;
}
}
return retvar;
}
/*
* Allocate a variable for an option value.
*/
static VAR
get_option_var(arg, error_msg)
char_u **arg;
int error_msg; /* give error message for invalid name */
{
VAR retvar;
char_u *option_end;
long numval;
char_u *stringval;
int opt_type;
/*
* Isolate the option name between single quotes and find its value.
*/
option_end = vim_strchr(*arg + 1, '\'');
if (option_end == NULL)
{
EMSG2("Missing quote: %s", *arg);
return NULL;
}
*option_end = NUL;
opt_type = get_option_value(*arg + 1, &numval, &stringval);
if (opt_type < 0) /* invalid name */
{
if (error_msg)
EMSG2("Unknown option: %s", *arg + 1);
retvar = NULL;
}
else
{
/*
* Store the value in a variable.
*/
retvar = alloc_var();
if (retvar == NULL)
{
if (opt_type == 0)
vim_free(stringval);
}
else
{
if (opt_type == 1) /* number option */
{
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number = numval;
}
else /* string option */
{
retvar->var_type = VAR_STRING;
retvar->var_val.var_string = stringval;
}
}
}
*option_end = '\''; /* put ' back for error messages */
*arg = option_end + 1;
return retvar;
}
/*
* Allocate a variable for an string constant.
*/
static VAR
get_string_var(arg)
char_u **arg;
{
char_u *p;
char_u *name;
VAR retvar;
int i;
int n;
/*
* Find the end of the string, skipping backslashed characters.
*/
for (p = *arg + 1; *p && *p != '\"'; ++p)
if (*p == '\\' && p[1] != NUL)
++p;
if (*p != '\"')
{
EMSG2("Missing quote: %s", *arg);
return NULL;
}
/*
* Copy the string into allocated memory, handling backslashed
* characters.
*/
name = alloc((unsigned)(p - *arg));
if (name == NULL)
return NULL;
i = 0;
for (p = *arg + 1; *p && *p != '\"'; ++p)
{
if (*p == '\\')
{
switch (*++p)
{
case 'b': name[i++] = BS; break;
case 'e': name[i++] = ESC; break;
case 'f': name[i++] = FF; break;
case 'n': name[i++] = NL; break;
case 'r': name[i++] = CR; break;
case 't': name[i++] = TAB; break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': n = *p - '0';
if (isdigit(p[1]))
{
n = (n << 3) + *++p - '0';
if (isdigit(p[1]))
n = (n << 3) + *++p - '0';
}
name[i++] = n;
break;
default: name[i++] = *p; break;
}
}
else
name[i++] = *p;
}
name[i] = NUL;
*arg = p + 1;
retvar = alloc_var();
if (retvar == NULL)
vim_free(name);
else
{
retvar->var_type = VAR_STRING;
retvar->var_val.var_string = name;
}
return retvar;
}
/*
* Allocate a variable for an environment variable.
*/
static VAR
get_env_var(arg)
char_u **arg;
{
char_u *name;
VAR retvar;
char_u *string;
/*
* Find the end of the name;
*/
++*arg;
name = get_env_name(arg);
if (name == NULL)
return NULL;
string = vim_getenv(name);
vim_free(name);
/*
* Allocate a variable. If the environment variable was not set, silently
* assume it is empty.
*/
retvar = alloc_var();
if (retvar == NULL)
return NULL;
retvar->var_type = VAR_STRING;
if (string != NULL)
retvar->var_val.var_string = vim_strsave(string);
return retvar;
}
/*
* Allocate a variable for the result of a function.
*/
static VAR
get_func_var(name, arg)
char_u *name; /* name of the function */
char_u **arg; /* argument, pointing to the '(' */
{
char_u *argp;
VAR retvar = NULL;
VAR varp;
#define MAX_FUNC_ARGS 3
VAR argvar[MAX_FUNC_ARGS]; /* vars for arguments */
int argcount = 0; /* number of arguments found */
#define ERROR_NONE 0
#define ERROR_INVARG 1
#define ERROR_OTHER 2
int error = ERROR_NONE;
char_u sbuf[NUMBUFLEN];
char_u *p;
char_u *s;
int n;
int len;
int slen;
linenr_t lnum;
/*
* Get the arguments.
*/
argp = *arg;
while (*argp != ')' && argcount < MAX_FUNC_ARGS)
{
argp = skipwhite(argp + 1); /* skip the '(' or ',' */
if ((argvar[argcount] = eval1(&argp)) == NULL)
{
error = ERROR_OTHER;
break;
}
++argcount;
if (*argp != ',')
break;
}
if (*argp != ')')
error = ERROR_INVARG;
if (!error)
{
/*
* Allocate a variable for the result.
*/
retvar = alloc_var();
if (retvar != NULL)
{
/*
* "buffer_exists()"
*/
if (STRCMP(name, "buffer_exists") == 0)
{
if (argcount != 1)
error = ERROR_INVARG;
else
{
retvar->var_type = VAR_NUMBER;
if (argvar[0]->var_type == VAR_NUMBER)
n = (buflist_findnr(argvar[0]->var_val.var_number)
!= NULL);
else if (argvar[0]->var_val.var_string != NULL)
n = (buflist_findname(argvar[0]->var_val.var_string)
!= NULL);
else
n = FALSE;
retvar->var_val.var_number = n;
}
}
/*
* "exists()"
*/
else if (STRCMP(name, "exists") == 0)
{
if (argcount != 1)
error = ERROR_INVARG;
else
{
retvar->var_type = VAR_NUMBER;
p = get_var_string(argvar[0], sbuf);
if (*p == '$') /* environment variable */
{
++p;
p = get_env_name(&p);
if (p == NULL)
retvar->var_val.var_number = FALSE;
else
{
retvar->var_val.var_number =
(vim_getenv(p) != NULL);
vim_free(p);
}
}
else
{
if (*p == '\'') /* option */
varp = get_option_var(&p, FALSE);
else /* internal variable */
{
s = get_ident(&p);
if (s == NULL)
varp = NULL;
else
varp = get_var_var(s, FALSE);
}
if (varp == NULL)
retvar->var_val.var_number = FALSE;
else
{
free_var(varp);
retvar->var_val.var_number = TRUE;
}
}
}
}
/*
* "expand()"
*/
else if (STRCMP(name, "expand") == 0)
{
if (argcount != 1)
error = ERROR_INVARG;
else
{
retvar->var_type = VAR_STRING;
retvar->var_val.var_string =
ExpandOne(get_var_string(argvar[0], sbuf), NULL,
WILD_LIST_NOTFOUND, WILD_ALL);
}
}
/*
* "file_readable()"
*/
else if (STRCMP(name, "file_readable") == 0)
{
if (argcount != 1)
error = ERROR_INVARG;
else
{
FILE *fd;
retvar->var_type = VAR_NUMBER;
p = get_var_string(argvar[0], sbuf);
if (!mch_isdir(p) && (fd = fopen(p, "r")) != NULL)
{
n = TRUE;
fclose(fd);
}
else
n = FALSE;
retvar->var_val.var_number = n;
}
}
/*
* "getline(lnum)"
*/
else if (STRCMP(name, "getline") == 0)
{
if (argcount != 1)
error = ERROR_INVARG;
else
{
retvar->var_type = VAR_STRING;
lnum = get_var_number(argvar[0]);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
p = ml_get(lnum);
else
p = (char_u *)"";
retvar->var_val.var_string = vim_strsave(p);
}
}
/*
* "strlen()"
*/
else if (STRCMP(name, "strlen") == 0)
{
if (argcount != 1)
error = ERROR_INVARG;
else
{
retvar->var_type = VAR_NUMBER;
retvar->var_val.var_number =
STRLEN(get_var_string(argvar[0], sbuf));
}
}
/*
* "substr()"
*/
else if (STRCMP(name, "substr") == 0)
{
if (argcount != 3)
error = ERROR_INVARG;
else
{
retvar->var_type = VAR_STRING;
p = get_var_string(argvar[0], sbuf);
n = get_var_number(argvar[1]);
len = get_var_number(argvar[2]);
slen = STRLEN(p);
if (n > slen)
n = slen;
if (n + len > slen)
len = slen - n;
retvar->var_val.var_string = vim_strnsave(p + n, len);
}
}
/*
* Unknown function.
*/
else
{
EMSG2("Unknown function: %s", name);
error = ERROR_OTHER;
}
}
*arg = skipwhite(argp + 1);
if (error != ERROR_NONE)
{
free_var(retvar);
retvar = NULL;
}
}
while (--argcount >= 0)
free_var(argvar[argcount]);
if (error == ERROR_INVARG)
EMSG2("Invalid arguments for function %s", name);
vim_free(name);
return retvar;
}
/*
* Get the name of an environment variable.
* Return a pointer to allocated memory.
* Return NULL for error.
* Advance "arg" to the first character after the name.
*/
static char_u *
get_env_name(arg)
char_u **arg;
{
char_u *p;
char_u *name;
for (p = *arg; isidchar(*p); ++p)
;
if (p == *arg) /* no name found */
return NULL;
/*
* Copy the name into allocated memory and get the value.
*/
name = vim_strnsave(*arg, (int)(p - *arg));
*arg = p;
return name;
}
/*
* Get the name of a function or internal variable.
* Returns a pointer to allocated memory, or NULL if something is wrong.
*/
static char_u *
get_ident(arg)
char_u **arg;
{
char_u *p;
char_u *name;
/*
* Find the end of the name;
*/
for (p = *arg; isnamechar(*p); ++p)
;
if (p == *arg) /* no name found */
return NULL;
/*
* Copy the name into allocated memory.
*/
name = vim_strnsave(*arg, (int)(p - *arg));
*arg = skipwhite(p);
return name;
}
static int
isnamechar(c)
int c;
{
return (isalpha(c) || isdigit(c) || c == '_');
}
/*
* Allocate a variable for an internal variable.
* The "name" argument must be in allocated memory and will be eaten.
* Returns a new variable, or NULL if something is wrong.
*/
static VAR
get_var_var(name, error_msg)
char_u *name;
int error_msg;
{
VAR retvar;
int type = VAR_UNKNOWN;
long number = 1;
char_u *string = NULL;
int i;
static char *(has_list[]) =
{
#ifdef MSDOS
# ifdef DJGPP
"has_dos32",
# else
"has_dos16",
# endif
#endif
#ifdef WIN32
"has_win32",
#endif
#ifdef AMIGA
"has_amiga",
# ifndef NO_ARP
"has_arp",
# endif
#endif
#ifdef UNIX
"has_unix",
#endif
#ifdef AUTOCMD
"has_autocmd",
#endif
#if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS)
"has_builtin_terms",
# ifdef ALL_BUILTIN_TCAPS
"has_all_builtin_terms",
# endif
#endif
#ifdef CINDENT
"has_cindent",
#endif
#ifdef COMPATIBLE
"has_compatible",
#endif
#ifdef DEBUG
"has_debug",
#endif
#ifdef DIGRAPHS
"has_digraphs",
#endif
#ifdef EMACS_TAGS
"has_emacs_tags",
#endif
#if !defined(USE_SYSTEM) && defined(UNIX)
"has_fork",
#endif
#ifdef USE_GUI
"has_gui",
#endif
#ifdef USE_GUI_ATHENA
"has_gui_athena",
#endif
#ifdef USE_GUI_MOTIF
"has_gui_motif",
#endif
#ifdef USE_GUI_WIN32
"has_gui_win32",
#endif
#ifdef INSERT_EXPAND
"has_insert_expand",
#endif
#ifdef HAVE_LANGMAP
"has_langmap",
#endif
#ifdef LISPINDENT
"has_lispindent",
#endif
#ifdef HAVE_PYTHON
"has_python",
#endif
#ifdef HAVE_PERL_INTERP
"has_perl",
#endif
#ifdef RIGHTLEFT
"has_rightleft",
#endif
#ifdef SMARTINDENT
"has_smartindent",
#endif
#ifdef SYNTAX_HL
"has_syntax",
#endif
#if defined(USE_SYSTEM) || !defined(UNIX)
"has_system",
#endif
#ifdef TERMINFO
"has_terminfo",
#endif
#ifdef HAVE_TGETENT
"has_tgetent",
#endif
#ifdef VIMINFO
"has_viminfo",
#endif
#ifdef WRITEBACKUP
"has_writebackup",
#endif
#if defined(UNIX) && defined(WANT_X11) && defined(HAVE_X11)
"has_x11",
#endif
NULL
};
/*
* Check for predefined variables.
*/
for (i = 0; has_list[i] != NULL; ++i)
if (STRCMP(name, has_list[i]) == 0)
break;
if (has_list[i] != NULL)
type = VAR_NUMBER;
else if (STRCMP(name, "version") == 0)
{
type = VAR_NUMBER;
number = get_version();
}
#ifdef USE_GUI
else if (STRCMP(name, "has_gui_running") == 0)
{
if (gui.in_use || gui.starting)
type = VAR_NUMBER;
}
#endif
#ifdef SYNTAX_HL
else if (STRCMP(name, "has_syntax_items") == 0)
{
if (syntax_present(curbuf))
type = VAR_NUMBER;
}
#endif
/*
* Check for user-defined variables.
*/
else
{
i = find_var(name);
if (i >= 0)
{
type = VAR_ENTRY(i).var_type;
if (type == VAR_NUMBER)
number = VAR_ENTRY(i).var_val.var_number;
else
string = vim_strsave(VAR_ENTRY(i).var_val.var_string);
}
}
if (type == VAR_UNKNOWN)
{
if (error_msg)
EMSG2("Undefined variable: %s", name);
retvar = NULL;
}
else
{
retvar = alloc_var();
if (retvar == NULL)
vim_free(string);
else
{
retvar->var_type = type;
if (type == VAR_NUMBER)
retvar->var_val.var_number = number;
else
retvar->var_val.var_string = string;
}
}
vim_free(name);
return retvar;
}
/*
* Allocate memory for a variable, and make it emtpy (0 or NULL value).
*/
static VAR
alloc_var()
{
return (VAR)alloc_clear((unsigned)sizeof(struct var));
}
/*
* Free the memory for a variable.
*/
static void
free_var(varp)
VAR varp;
{
if (varp != NULL)
{
if (varp->var_type == VAR_STRING)
vim_free(varp->var_val.var_string);
vim_free(varp->var_name);
vim_free(varp);
}
}
/*
* Get the number value of a variable.
* If it is a String variable, use str2nr().
*/
static long
get_var_number(varp)
VAR varp;
{
if (varp->var_type == VAR_NUMBER)
return (long)(varp->var_val.var_number);
else if (varp->var_val.var_string == NULL)
return 0L;
else
return str2nr(varp->var_val.var_string, NULL, NULL, TRUE, TRUE);
}
/*
* Get the string value of a variable.
* If it is a Number variable, the number is converted into a string, into the
* provided buffer buf[NUMBUFLEN].
* If the String variable has never been set, return an empty string.
* Never returns NULL;
*/
static char_u *
get_var_string(varp, buf)
VAR varp;
char_u *buf;
{
if (varp->var_type == VAR_NUMBER)
{
sprintf((char *)buf, "%ld", (long)varp->var_val.var_number);
return buf;
}
else if (varp->var_val.var_string == NULL)
return (char_u *)"";
else
return varp->var_val.var_string;
}
/*
* Find variable "name" in the list of variables.
* Return its index if found, -1 if not found.
*/
static int
find_var(name)
char_u *name;
{
int i;
/*
* Initialize the variables for use.
*/
variables.ga_itemsize = sizeof(struct var);
variables.ga_growsize = 4;
for (i = variables.ga_len; --i >= 0; )
if (VAR_ENTRY(i).var_name != NULL
&& STRCMP(VAR_ENTRY(i).var_name, name) == 0)
break;
return i;
}
/*
* Set variable "name" to value in "varp".
* If the variable already exists, the value is updated.
* Otherwise the variable is created.
*/
static void
set_var(name, varp)
char_u *name;
VAR varp;
{
int i;
i = find_var(name);
if (i >= 0) /* existing variable, may need to free string */
{
if (VAR_ENTRY(i).var_type == VAR_STRING)
vim_free(VAR_ENTRY(i).var_val.var_string);
}
else /* add a new variable */
{
/* Try to use an empty entry */
for (i = variables.ga_len; --i >= 0; )
if (VAR_ENTRY(i).var_name == NULL)
break;
if (i < 0) /* need to allocated more room */
{
if (ga_grow(&variables, 1) == FAIL)
return;
i = variables.ga_len;
}
if ((VAR_ENTRY(i).var_name = vim_strsave(name)) == NULL)
return;
if (i == variables.ga_len)
{
++variables.ga_len;
--variables.ga_room;
}
}
VAR_ENTRY(i).var_type = varp->var_type;
if (varp->var_type == VAR_STRING)
VAR_ENTRY(i).var_val.var_string = vim_strsave(varp->var_val.var_string);
else
VAR_ENTRY(i).var_val.var_number = varp->var_val.var_number;
}
/*
* Implementation of
* ":echo expr1 .." print each argument separated with a space, add a
* newline at the end.
* ":echon expr1 .." print each argument plain.
*/
void
do_echo(arg, echo)
char_u *arg;
int echo; /* ":echo" command */
{
VAR varp;
char_u sbuf[NUMBUFLEN];
char_u *p;
if (echo)
msg_start();
while (*arg)
{
varp = eval1(&arg);
if (varp == NULL)
break;
for (p = get_var_string(varp, sbuf); *p != NUL; ++p)
if (*p == '\n' || *p == '\r' || *p == TAB)
msg_putchar(*p);
else
msg_puts(transchar(*p));
arg = skipwhite(arg);
if (*arg && echo)
msg_putchar(' ');
}
msg_clr_eos();
if (echo)
msg_end();
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.