ftp.nice.ch/pub/next/unix/editor/vim-5.0f.s.tar.gz#/vim-5.0f/src/eval.c

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.