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.