www.pudn.com > vim53src.zip > eval.c
/* vi:set ts=8 sts=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" #ifdef HAVE_FCNTL_H # include/* contains setenv() declaration for Amiga */ #endif #ifdef AMIGA # include /* for strftime() */ #endif #ifdef WANT_EVAL /* * Structure to hold an internal variable. */ typedef struct { 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 (Careful: can be NULL!) */ } var_val; } var; #define VAR_UNKNOWN 0 #define VAR_NUMBER 1 #define VAR_STRING 2 typedef var * VAR; /* * All user-defined internal variables are stored in "variables". */ struct growarray variables = {0, 0, sizeof(var), 4, NULL}; #define VAR_ENTRY(idx) (((VAR)(variables.ga_data))[idx]) #define VAR_GAP_ENTRY(idx, gap) (((VAR)(gap->ga_data))[idx]) #define BVAR_ENTRY(idx) (((VAR)(curbuf->b_vars.ga_data))[idx]) #define WVAR_ENTRY(idx) (((VAR)(curwin->w_vars.ga_data))[idx]) static int echo_attr = 0; /* attributes used for ":echo" */ /* * Structure to hold info for a user function. */ struct ufunc { struct ufunc *next; /* next function in list */ char_u *name; /* name of function */ int varargs; /* variable nr of arguments */ int flags; struct growarray args; /* arguments */ struct growarray lines; /* function lines */ }; /* function flags */ #define FC_ABORT 1 /* abort function on error */ #define FC_RANGE 2 /* function accepts range */ /* * All user-defined functions are found in the forward-linked function list. * The first function is pointed at by firstfunc. */ struct ufunc *firstfunc = NULL; #define FUNCARG(fp, j) ((char_u **)(fp->args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->lines.ga_data))[j] /* structure to hold info for a function that is currently being executed. */ struct funccall { struct ufunc *func; /* function being called */ int linenr; /* next line to be executed */ int argcount; /* nr of arguments */ VAR argvars; /* arguments */ var a0_var; /* "a:0" variable */ var firstline; /* "a:firstline" variable */ var lastline; /* "a:lastline" variable */ struct growarray l_vars; /* local function variables */ VAR retvar; /* return value variable */ }; /* pointer to funccal for currently active function */ struct funccall *current_funccal = NULL; static char_u *cat_prefix_varname __ARGS((int prefix, char_u *name)); static int eval0 __ARGS((char_u *arg, VAR retvar, char_u **nextcmd)); static int eval1 __ARGS((char_u **arg, VAR retvar)); static int eval2 __ARGS((char_u **arg, VAR retvar)); static int eval3 __ARGS((char_u **arg, VAR retvar)); static int eval4 __ARGS((char_u **arg, VAR retvar)); static int eval5 __ARGS((char_u **arg, VAR retvar)); static int eval6 __ARGS((char_u **arg, VAR retvar)); static int get_option_var __ARGS((char_u **arg, VAR retvar)); static int get_string_var __ARGS((char_u **arg, VAR retvar)); static int get_lit_string_var __ARGS((char_u **arg, VAR retvar)); static int get_env_var __ARGS((char_u **arg, VAR retvar)); static int get_func_var __ARGS((char_u *name, int len, VAR retvar, char_u **arg, linenr_t firstline, linenr_t lastline, int *doesrange)); static void f_argc __ARGS((VAR argvars, VAR retvar)); static void f_argv __ARGS((VAR argvars, VAR retvar)); static void f_browse __ARGS((VAR argvars, VAR retvar)); static void f_bufexists __ARGS((VAR argvars, VAR retvar)); static BUF *get_buf_var __ARGS((VAR avar)); static void f_bufname __ARGS((VAR argvars, VAR retvar)); static void f_bufnr __ARGS((VAR argvars, VAR retvar)); static void f_char2nr __ARGS((VAR argvars, VAR retvar)); static void f_col __ARGS((VAR argvars, VAR retvar)); static void f_confirm __ARGS((VAR argvars, VAR retvar)); static void f_delete __ARGS((VAR argvars, VAR retvar)); static void f_escape __ARGS((VAR argvars, VAR retvar)); static void f_exists __ARGS((VAR argvars, VAR retvar)); static void f_expand __ARGS((VAR argvars, VAR retvar)); static void f_filereadable __ARGS((VAR argvars, VAR retvar)); static void f_fnamemodify __ARGS((VAR argvars, VAR retvar)); static void f_getcwd __ARGS((VAR argvars, VAR retvar)); static void f_getline __ARGS((VAR argvars, VAR retvar)); static void f_has __ARGS((VAR argvars, VAR retvar)); static void f_hlexists __ARGS((VAR argvars, VAR retvar)); static void f_hlID __ARGS((VAR argvars, VAR retvar)); static void f_hostname __ARGS((VAR argvars, VAR retvar)); static void f_isdirectory __ARGS((VAR argvars, VAR retvar)); static void f_input __ARGS((VAR argvars, VAR retvar)); static void f_last_buffer_nr __ARGS((VAR argvars, VAR retvar)); static void f_line __ARGS((VAR argvars, VAR retvar)); static void f_match __ARGS((VAR argvars, VAR retvar)); static void f_matchend __ARGS((VAR argvars, VAR retvar)); static void f_matchstr __ARGS((VAR argvars, VAR retvar)); static void f_nr2char __ARGS((VAR argvars, VAR retvar)); static void f_setline __ARGS((VAR argvars, VAR retvar)); static void f_some_match __ARGS((VAR argvars, VAR retvar, int start)); static void f_strftime __ARGS((VAR argvars, VAR retvar)); static void f_strlen __ARGS((VAR argvars, VAR retvar)); static void f_strpart __ARGS((VAR argvars, VAR retvar)); static void f_synID __ARGS((VAR argvars, VAR retvar)); static void f_synIDattr __ARGS((VAR argvars, VAR retvar)); static void f_synIDtrans __ARGS((VAR argvars, VAR retvar)); static void f_substitute __ARGS((VAR argvars, VAR retvar)); static void f_tempname __ARGS((VAR argvars, VAR retvar)); static void f_virtcol __ARGS((VAR argvars, VAR retvar)); static void f_winbufnr __ARGS((VAR argvars, VAR retvar)); static void f_winheight __ARGS((VAR argvars, VAR retvar)); static void f_winnr __ARGS((VAR argvars, VAR retvar)); static WIN *find_win_by_nr __ARGS((VAR vp)); static FPOS *var2fpos __ARGS((VAR varp)); static int get_env_len __ARGS((char_u **arg)); char_u *get_env_string __ARGS((char_u **arg)); static int get_id_len __ARGS((char_u **arg)); static int eval_isnamec __ARGS((int c)); static int get_var_var __ARGS((char_u *name, int len, VAR retvar)); static VAR alloc_var __ARGS((void)); static VAR alloc_string_var __ARGS((char_u *string)); static void free_var __ARGS((VAR varp)); static void clear_var __ARGS((VAR varp)); static long get_var_number __ARGS((VAR varp)); static char_u *get_var_string __ARGS((VAR varp)); static char_u *get_var_string_buf __ARGS((VAR varp, char_u *buf)); static VAR find_var __ARGS((char_u *name, int writing)); static struct growarray *find_var_ga __ARGS((char_u *name, char_u **varname)); static void var_free_one __ARGS((VAR v)); static void list_one_var __ARGS((VAR v, char_u *prefix)); static void set_var __ARGS((char_u *name, VAR varp)); static char_u *find_option_end __ARGS((char_u *p)); static void list_func_head __ARGS((struct ufunc *fp)); static struct ufunc *find_func __ARGS((char_u *name)); static void call_func __ARGS((struct ufunc *fp, int argcount, VAR argvars, VAR retvar, linenr_t firstline, linenr_t lastline)); /* * Set an internal variable to a string value. Creates the variable if it does * not already exist. */ void set_internal_string_var(name, value) char_u *name; char_u *value; { char_u *val; VAR varp; val = vim_strsave(value); if (val != NULL) { varp = alloc_string_var(val); if (varp != NULL) { set_var(name, varp); free_var(varp); } } } /* * Top level evaluation function, returning a boolean. * Sets "error" to TRUE if there was an error. * Return TRUE or FALSE. */ int eval_to_bool(arg, error, nextcmd) char_u *arg; int *error; char_u **nextcmd; { var retvar; int retval; if (eval0(arg, &retvar, nextcmd) == FAIL) { *error = TRUE; retval = FALSE; } else { *error = FALSE; retval = (get_var_number(&retvar) != 0); clear_var(&retvar); } return retval; } /* * Top level evaluation function, returning a string. * Return pointer to allocated memory, or NULL for failure. */ char_u * eval_to_string(arg, nextcmd) char_u *arg; char_u **nextcmd; { var retvar; char_u *retval; if (eval0(arg, &retvar, nextcmd) == FAIL) retval = NULL; else { retval = vim_strsave(get_var_string(&retvar)); clear_var(&retvar); } return retval; } /* * ":let var = expr" assignment command. * ":let var" list one variable value * ":let" list all variable values */ void do_let(eap) EXARG *eap; { char_u *arg = eap->arg; char_u *expr; char_u *name; VAR varp; var retvar; char_u *p; int c1, c2; int i; #ifndef HAVE_SETENV char_u *envbuf; #endif expr = vim_strchr(arg, '='); if (expr == NULL) { if (ends_excmd(*arg)) { if (!eap->skip) { /* * List all variables. */ for (i = 0; i < variables.ga_len; ++i) if (VAR_ENTRY(i).var_name != NULL) list_one_var(&VAR_ENTRY(i), (char_u *)""); for (i = 0; i < curbuf->b_vars.ga_len; ++i) if (BVAR_ENTRY(i).var_name != NULL) list_one_var(&BVAR_ENTRY(i), (char_u *)"b:"); for (i = 0; i < curwin->w_vars.ga_len; ++i) if (WVAR_ENTRY(i).var_name != NULL) list_one_var(&WVAR_ENTRY(i), (char_u *)"w:"); } } else { /* * List variables. */ while (!ends_excmd(*arg)) { for (p = arg; eval_isnamec(*p); ++p) ; if (!vim_iswhite(*p) && !ends_excmd(*p)) { EMSG(e_trailing); break; } if (!eap->skip) { c1 = *p; *p = NUL; varp = find_var(arg, FALSE); if (varp == NULL) EMSG2("Unknown variable: \"%s\"", arg); else { name = vim_strchr(arg, ':'); if (name != NULL) { c2 = *++name; *name = NUL; list_one_var(varp, arg); *name = c2; } else list_one_var(varp, (char_u *)""); } *p = c1; } arg = skipwhite(p); } } eap->nextcmd = check_nextcmd(arg); } else { if (eap->skip) ++emsg_off; i = eval0(expr + 1, &retvar, &eap->nextcmd); if (eap->skip) { if (i != FAIL) clear_var(&retvar); --emsg_off; } else if (i != FAIL) { /* * ":let $VAR = expr": Set environment variable. */ if (*arg == '$') { int len; int cc; /* Find the end of the name. */ ++arg; name = arg; len = get_env_len(&arg); if (name != NULL) { if (*skipwhite(arg) != '=') EMSG(e_letunexp); else { cc = name[len]; name[len] = NUL; p = get_var_string(&retvar); #ifdef HAVE_SETENV mch_setenv((char *)name, (char *)p, 1); #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 if (STRICMP(name, "home") == 0) init_homedir(); name[len] = cc; } } } /* * ":let &option = expr": Set option value. */ else if (*arg == '&') { /* * Find the end of the name; */ ++arg; p = find_option_end(arg); if (*skipwhite(p) != '=') EMSG(e_letunexp); else { c1 = *p; *p = NUL; set_option_value(arg, get_var_number(&retvar), get_var_string(&retvar)); *p = c1; /* put back for error messages */ } } /* * ":let @r = expr": Set register contents. */ else if (*arg == '@') { ++arg; if (*skipwhite(arg + 1) != '=') EMSG(e_letunexp); else write_reg_contents(*arg == '@' ? '"' : *arg, get_var_string(&retvar)); } /* * ":let var = expr": Set internal variable. */ else if (eval_isnamec(*arg) && !isdigit(*arg)) { /* * Find the end of the name; */ for (p = arg; eval_isnamec(*p); ++p) ; if (*skipwhite(p) != '=') EMSG(e_letunexp); else { c1 = *p; *p = NUL; set_var(arg, &retvar); *p = c1; /* put char back for error messages */ } } else { EMSG2(e_invarg2, arg); } clear_var(&retvar); } } } /* * ":1,25call func(arg1, arg2)" function call. */ void do_call(eap) EXARG *eap; { char_u *arg = eap->arg; char_u *startarg; char_u *name; var retvar; int len; linenr_t lnum; int doesrange; name = arg; len = get_id_len(&arg); startarg = arg; if (*startarg != '(') { EMSG2("Missing braces: %s", name); return; } /* * When skipping, evaluate the function once, to find the end of the * arguments. * When the function takes a range, this is discovered after the first * call, and the loop is broken. */ if (eap->skip) ++emsg_off; for (lnum = eap->line1; lnum <= eap->line2; ++lnum) { if (!eap->skip && eap->line1 != eap->line2) { curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; } arg = startarg; if (get_func_var(name, len, &retvar, &arg, eap->line1, eap->line2, &doesrange) == FAIL) break; clear_var(&retvar); if (doesrange || eap->skip) break; } if (eap->skip) --emsg_off; eap->nextcmd = check_nextcmd(arg); } /* * ":unlet[!] var1 ... " command. */ void do_unlet(arg, forceit) char_u *arg; int forceit; { char_u *name_end; VAR v; char_u cc; do { name_end = skiptowhite(arg); cc = *name_end; *name_end = NUL; v = find_var(arg, TRUE); if (v != NULL) /* existing variable, may need to free string */ var_free_one(v); else if (!forceit) /* non-existing variable */ { *name_end = cc; EMSG2("No such variable: \"%s\"", arg); break; } *name_end = cc; arg = skipwhite(name_end); } while (*arg != NUL); } /* * Local string buffer for the next two functions to store a variable name * with its prefix. Allocated in cat_prefix_varname(), freed later in * get_user_var_name(). */ static char_u *varnamebuf = NULL; static int varnamebuflen = 0; /* * Function to concatenate a prefix and a variable name. * TODO: Use string instead of char for 'prefix' when allowing 'b3:' like vars. */ static char_u * cat_prefix_varname(prefix, name) int prefix; char_u *name; { int len; len = STRLEN( name ) + 3; if ( len > varnamebuflen ) { vim_free(varnamebuf); len += 10; /* some additional space */ varnamebuf = alloc(len); if (varnamebuf) varnamebuflen = len; else { varnamebuflen = 0; return NULL; } } *varnamebuf = prefix; varnamebuf[1] = ':'; STRCPY(varnamebuf+2, name); return varnamebuf; } /* * Function given to ExpandGeneric() to obtain the list of user defined * (global/buffer/window) variable names. */ char_u * get_user_var_name(idx) int idx; { if (idx < variables.ga_len) /* Global variables */ return VAR_ENTRY(idx).var_name; /* Current buffer variables */ else if ((idx -= variables.ga_len) < curbuf->b_vars.ga_len) return cat_prefix_varname('b', BVAR_ENTRY(idx).var_name); /* Current window variables */ else if ((idx -= curbuf->b_vars.ga_len) < curwin->w_vars.ga_len) return cat_prefix_varname('w', WVAR_ENTRY(idx).var_name); else { vim_free(varnamebuf); varnamebuf = NULL; varnamebuflen = 0; return NULL; } } /* * 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 nextcmd. * Return OK or FAIL. */ static int eval0(arg, retvar, nextcmd) char_u *arg; VAR retvar; char_u **nextcmd; { int ret; char_u *p; p = skipwhite(arg); ret = eval1(&p, retvar); if (ret == FAIL || !ends_excmd(*p)) { if (ret != FAIL) clear_var(retvar); EMSG2(e_invexpr2, arg); ret = FAIL; } if (nextcmd != NULL) *nextcmd = check_nextcmd(p); return ret; } /* * 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. * * Return OK or FAIL. */ static int eval1(arg, retvar) char_u **arg; VAR retvar; { var var2; long n1, n2; /* * Get the first variable. */ if (eval2(arg, retvar) == FAIL) return FAIL; /* * Repeat until there is no following "||". */ while ((*arg)[0] == '|' && (*arg)[1] == '|') { n1 = get_var_number(retvar); clear_var(retvar); /* * Get the second variable. */ *arg = skipwhite(*arg + 2); if (eval2(arg, &var2) == FAIL) return FAIL; /* * Compute the result. */ n2 = get_var_number(&var2); clear_var(&var2); retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = (n1 || n2); } return OK; } /* * 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. * * Return OK or FAIL. */ static int eval2(arg, retvar) char_u **arg; VAR retvar; { var var2; long n1, n2; /* * Get the first variable. */ if (eval3(arg, retvar) == FAIL) return FAIL; /* * Repeat until there is no following "&&". */ while ((*arg)[0] == '&' && (*arg)[1] == '&') { n1 = get_var_number(retvar); clear_var(retvar); /* * Get the second variable. */ *arg = skipwhite(*arg + 2); if (eval3(arg, &var2) == FAIL) return FAIL; /* * Compute the result. */ n2 = get_var_number(&var2); clear_var(&var2); retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = (n1 && n2); } return OK; } /* * Handle third level expression: * var1 == var2 * var1 =~ var2 * 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. * * Return OK or FAIL. */ static int eval3(arg, retvar) char_u **arg; VAR retvar; { var var2; char_u *p; int i = 0; enum exp_type 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. */ if (eval4(arg, retvar) == FAIL) return FAIL; 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); if (eval4(arg, &var2) == FAIL) { clear_var(retvar); return FAIL; } /* * If one of the two variables is a number, compare as a number. * When using "=~" or "!~", always compare as string. */ if ((retvar->var_type == VAR_NUMBER || var2.var_type == VAR_NUMBER) && type != TYPE_MATCH && type != TYPE_NOMATCH) { n1 = get_var_number(retvar); 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; case TYPE_UNKNOWN: case TYPE_MATCH: case TYPE_NOMATCH: break; /* avoid gcc warning */ } } else { s1 = get_var_string_buf(retvar, buf1); s2 = get_var_string_buf(&var2, buf2); if (type != TYPE_MATCH && type != TYPE_NOMATCH) 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); if (type == TYPE_NOMATCH) n1 = !n1; } break; case TYPE_UNKNOWN: break; /* avoid gcc warning */ } } clear_var(retvar); clear_var(&var2); retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = n1; } return OK; } /* * 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. * * Return OK or FAIL. */ static int eval4(arg, retvar) char_u **arg; VAR retvar; { var var2; int op; long n1, n2; char_u *s1, *s2; char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; char_u *p; /* * Get the first variable. */ if (eval5(arg, retvar) == FAIL) return FAIL; /* * Repeat computing, until no '+', '-' or '.' is following. */ for (;;) { op = **arg; if (op != '+' && op != '-' && op != '.') break; /* * Get the second variable. */ *arg = skipwhite(*arg + 1); if (eval5(arg, &var2) == FAIL) { clear_var(retvar); return FAIL; } /* * Compute the result. */ if (op == '.') { s1 = get_var_string_buf(retvar, buf1); s2 = get_var_string_buf(&var2, buf2); p = alloc((unsigned)(STRLEN(s1) + STRLEN(s2) + 1)); if (p != NULL) { STRCPY(p, s1); STRCAT(p, s2); } clear_var(retvar); retvar->var_type = VAR_STRING; retvar->var_val.var_string = p; } else { n1 = get_var_number(retvar); n2 = get_var_number(&var2); clear_var(retvar); if (op == '+') n1 = n1 + n2; else n1 = n1 - n2; retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = n1; } clear_var(&var2); } return OK; } /* * 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. * * Return OK or FAIL. */ static int eval5(arg, retvar) char_u **arg; VAR retvar; { var var2; int op; long n1, n2; /* * Get the first variable. */ if (eval6(arg, retvar) == FAIL) return FAIL; /* * Repeat computing, until no '*', '/' or '%' is following. */ for (;;) { op = **arg; if (op != '*' && op != '/' && op != '%') break; n1 = get_var_number(retvar); clear_var(retvar); /* * Get the second variable. */ *arg = skipwhite(*arg + 1); if (eval6(arg, &var2) == FAIL) return FAIL; n2 = get_var_number(&var2); clear_var(&var2); /* * Compute the result. */ if (op == '*') n1 = n1 * n2; else if (op == '/') { if (n2 == 0) /* give an error message? */ n1 = 0x7fffffff; else n1 = n1 / n2; } else { if (n2 == 0) /* give an error message? */ n1 = 0; else n1 = n1 % n2; } retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = n1; } return OK; } /* * Handle sixth level expression: * number number constant * "string" string contstant * *option-name option value * @r register contents * identifier variable value * $VAR environment variable * (expression) nested expression * * Also handle: * ! in front logical NOT * - in front unary minus * 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. * * Return OK or FAIL. */ static int eval6(arg, retvar) char_u **arg; VAR retvar; { var var2; long n; int len; char_u *s; int val; char_u *start_leader, *end_leader; int ret = OK; /* * Skip '!' and '-' characters. They are handled later. */ start_leader = *arg; while (**arg == '!' || **arg == '-') *arg = skipwhite(*arg + 1); end_leader = *arg; switch (**arg) { /* * Number constant. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': retvar->var_type = VAR_NUMBER; vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL); retvar->var_val.var_number = n; *arg += len; break; /* * String constant: "string". */ case '\"': ret = get_string_var(arg, retvar); break; /* * Literal string constant: 'string'. */ case '\'': ret = get_lit_string_var(arg, retvar); break; /* * Option value: &name */ case '&': ret = get_option_var(arg, retvar); break; /* * Environment variable: $VAR. */ case '$': ret = get_env_var(arg, retvar); break; /* * Register contents: @r. */ case '@': retvar->var_type = VAR_STRING; retvar->var_val.var_string = get_reg_contents(*++*arg); if (**arg != NUL) ++*arg; break; /* * nested expression: (expression). */ case '(': *arg = skipwhite(*arg + 1); ret = eval1(arg, retvar); /* recursive! */ if (**arg != ')') EMSG("Missing ')'"); else ++*arg; break; /* * Must be a variable or function name then. */ default: s = *arg; len = get_id_len(arg); if (len) { if (**arg == '(') /* recursive! */ ret = get_func_var(s, len, retvar, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len); else ret = get_var_var(s, len, retvar); } else ret = FAIL; break; } *arg = skipwhite(*arg); /* * Handle expr[expr] subscript. */ if (**arg == '[' && ret == OK) { /* * Get the variable from inside the []. */ *arg = skipwhite(*arg + 1); if (eval1(arg, &var2) == FAIL) /* recursive! */ { clear_var(retvar); return FAIL; } n = get_var_number(&var2); clear_var(&var2); /* Check for the ']'. */ if (**arg != ']') { EMSG("Missing ']'"); clear_var(retvar); return FAIL; } /* * The resulting variable is a string of a single character. * If the index is too big or negative, the result is empty. */ s = get_var_string(retvar); if (n >= (long)STRLEN(s) || n < 0) s = NULL; else s = vim_strnsave(s + n, 1); clear_var(retvar); retvar->var_type = VAR_STRING; retvar->var_val.var_string = s; *arg = skipwhite(*arg + 1); /* skip the ']' */ } /* * Apply logical NOT and unary '-', from right to left. */ if (ret == OK && end_leader > start_leader) { val = get_var_number(retvar); while (end_leader > start_leader) { --end_leader; if (*end_leader == '!') val = !val; else if (*end_leader == '-') val = -val; } clear_var(retvar); retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = val; } return ret; } /* * Get an option value. * "arg" points to the '&' before the option name. * "arg" is advanced to character after the option name. * Return OK or FAIL. */ static int get_option_var(arg, retvar) char_u **arg; VAR retvar; /* when NULL, only check if option exists */ { char_u *option_end; long numval; char_u *stringval; int opt_type; int c; int ret = OK; /* * Isolate the option name and find its value. */ option_end = find_option_end(*arg + 1); if (option_end == *arg + 1) { if (retvar != NULL) EMSG2("Option name missing: %s", *arg); return FAIL; } c = *option_end; *option_end = NUL; opt_type = get_option_value(*arg + 1, &numval, retvar == NULL ? NULL : &stringval); if (opt_type < 0) /* invalid name */ { if (retvar != NULL) EMSG2("Unknown option: %s", *arg + 1); ret = FAIL; } else if (retvar != NULL) { 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 = c; /* put back for error messages */ *arg = option_end; return ret; } /* * Allocate a variable for an string constant. * Return OK or FAIL. */ static int get_string_var(arg, retvar) char_u **arg; VAR retvar; { char_u *p; char_u *name; int i; int extra = 0; /* * Find the end of the string, skipping backslashed characters. */ for (p = *arg + 1; *p && *p != '\"'; ++p) if (*p == '\\' && p[1] != NUL) { ++p; /* A "\ " form occupies at least 4 characters, and produces up * to 6 characters: reserve space for 2 extra */ if (*p == '<') extra += 2; } if (*p != '\"') { EMSG2("Missing quote: %s", *arg); return FAIL; } /* * Copy the string into allocated memory, handling backslashed * characters. */ name = alloc((unsigned)(p - *arg + extra)); if (name == NULL) return FAIL; 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; /* hex: "\x1", "\x12" */ case 'X': case 'x': if (isxdigit(p[1])) { ++p; name[i] = hex2nr(*p); if (isxdigit(p[1])) { ++p; name[i] = (name[i] << 4) + hex2nr(*p); } ++i; } else name[i++] = *p; break; /* octal: "\1", "\12", "\123" */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': name[i] = *p - '0'; if (p[1] >= '0' && p[1] <= '7') { ++p; name[i] = (name[i] << 3) + *p - '0'; if (p[1] >= '0' && p[1] <= '7') { ++p; name[i] = (name[i] << 3) + *p - '0'; } } ++i; break; /* Special key, e.g.: "\ " */ case '<': extra = trans_special(&p, name + i, FALSE); if (extra) { i += extra; --p; break; } /* FALLTHROUGH */ default: name[i++] = *p; break; } } else name[i++] = *p; } name[i] = NUL; *arg = p + 1; retvar->var_type = VAR_STRING; retvar->var_val.var_string = name; return OK; } /* * Allocate a variable for an backtick-string constant. * Return OK or FAIL. */ static int get_lit_string_var(arg, retvar) char_u **arg; VAR retvar; { char_u *p; char_u *name; /* * Find the end of the string. */ p = vim_strchr(*arg + 1, '\''); if (p == NULL) { EMSG2("Missing quote: %s", *arg); return FAIL; } /* * Copy the string into allocated memory. */ name = vim_strnsave(*arg + 1, (int)(p - (*arg + 1))); if (name == NULL) return FAIL; *arg = p + 1; retvar->var_type = VAR_STRING; retvar->var_val.var_string = name; return OK; } /* * Get the value of an environment variable. * Return OK or FAIL. */ static int get_env_var(arg, retvar) char_u **arg; VAR retvar; { char_u *string; string = get_env_string(arg); /* * Allocate a variable. If the environment variable was not set, silently * assume it is empty. */ if (string != NULL) string = vim_strsave(string); retvar->var_type = VAR_STRING; retvar->var_val.var_string = string; return OK; } /* * Allocate a variable for the result of a function. * Return OK or FAIL. */ static int get_func_var(name, len, retvar, arg, firstline, lastline, doesrange) char_u *name; /* name of the function */ int len; /* length of "name" */ VAR retvar; char_u **arg; /* argument, pointing to the '(' */ linenr_t firstline; /* first line of range */ linenr_t lastline; /* last line of range */ int *doesrange; /* return: function handled range */ { char_u *argp; int ret = FAIL; #define MAX_FUNC_ARGS 20 var argvars[MAX_FUNC_ARGS]; /* vars for arguments */ int argcount = 0; /* number of arguments found */ static char *errors[] = {"Invalid arguments for function %s", "Unknown function: %s", "Too many arguments for function: %s", "Not enough arguments for function: %s", }; #define ERROR_INVARG 0 #define ERROR_UNKOWN 1 #define ERROR_TOOMANY 2 #define ERROR_TOOFEW 3 #define ERROR_NONE 4 #define ERROR_OTHER 5 int error = ERROR_NONE; int i; struct ufunc *fp; int cc; static struct fst { char *f_name; /* function name */ char f_min_argc; /* minimal number of arguments */ char f_max_argc; /* miximal number of arguments */ void (*f_func) __ARGS((VAR args, VAR rvar)); /* impl. function */ } functions[] = { {"argc", 0, 0, f_argc}, {"argv", 1, 1, f_argv}, {"browse", 4, 4, f_browse}, {"buffer_exists", 1, 1, f_bufexists}, /* obsolete */ {"bufexists", 1, 1, f_bufexists}, {"buffer_name", 1, 1, f_bufname}, /* obsolete */ {"buffer_number", 1, 1, f_bufnr}, /* obsolete */ {"bufname", 1, 1, f_bufname}, {"bufnr", 1, 1, f_bufnr}, {"char2nr", 1, 1, f_char2nr}, {"col", 1, 1, f_col}, {"confirm", 2, 4, f_confirm}, {"delete", 1, 1, f_delete}, {"escape", 2, 2, f_escape}, {"exists", 1, 1, f_exists}, {"expand", 1, 1, f_expand}, {"file_readable", 1, 1, f_filereadable}, /* obsolete */ {"filereadable", 1, 1, f_filereadable}, {"fnamemodify", 2, 2, f_fnamemodify}, {"getcwd", 0, 0, f_getcwd}, {"getline", 1, 1, f_getline}, {"has", 1, 1, f_has}, {"highlight_exists", 1, 1, f_hlexists}, /* obsolete */ {"highlightID", 1, 1, f_hlID}, /* obsolete */ {"hlexists", 1, 1, f_hlexists}, {"hlID", 1, 1, f_hlID}, {"hostname", 0, 0, f_hostname}, {"input", 1, 1, f_input }, {"isdirectory", 1, 1, f_isdirectory}, {"last_buffer_nr", 0, 0, f_last_buffer_nr},/* obsolete */ {"line", 1, 1, f_line}, {"match", 2, 2, f_match}, {"matchend", 2, 2, f_matchend}, {"matchstr", 2, 2, f_matchstr}, {"nr2char", 1, 1, f_nr2char}, {"setline", 2, 2, f_setline}, #ifdef HAVE_STRFTIME {"strftime", 1, 1, f_strftime}, #endif {"strlen", 1, 1, f_strlen}, {"strpart", 3, 3, f_strpart}, {"synID", 3, 3, f_synID}, {"synIDattr", 2, 3, f_synIDattr}, {"synIDtrans", 1, 1, f_synIDtrans}, {"substitute", 4, 4, f_substitute}, {"tempname", 0, 0, f_tempname}, {"virtcol", 1, 1, f_virtcol}, {"winbufnr", 1, 1, f_winbufnr}, {"winheight", 1, 1, f_winheight}, {"winnr", 0, 0, f_winnr}, }; cc = name[len]; name[len] = NUL; *doesrange = FALSE; /* * Get the arguments. */ argp = *arg; while (argcount < MAX_FUNC_ARGS) { argp = skipwhite(argp + 1); /* skip the '(' or ',' */ if (*argp == ')' || *argp == ',' || *argp == NUL) break; if (eval1(&argp, &argvars[argcount]) == FAIL) { error = ERROR_OTHER; break; } ++argcount; if (*argp != ',') break; } if (*argp != ')' && error == ERROR_NONE) error = ERROR_INVARG; /* execute the function if no errors detected and executing */ if (error == ERROR_NONE && !emsg_off) { retvar->var_type = VAR_NUMBER; /* default is number retvar */ error = ERROR_UNKOWN; if (!islower(name[0])) { /* * User defined function. */ fp = find_func(name); if (fp != NULL) { if (fp->flags & FC_RANGE) *doesrange = TRUE; if (argcount < fp->args.ga_len) error = ERROR_TOOFEW; else if (!fp->varargs && argcount > fp->args.ga_len) error = ERROR_TOOMANY; else { /* * Call the user function. * Save and restore search patterns and redo buffer. */ save_search_patterns(); saveRedobuff(); call_func(fp, argcount, argvars, retvar, firstline, lastline); restoreRedobuff(); restore_search_patterns(); error = ERROR_NONE; } } } else { /* * Find the function name in the table, call its implementation. */ for (i = 0; i < (int)(sizeof(functions) / sizeof(struct fst)); ++i) if (STRCMP(name, functions[i].f_name) == 0) { if (argcount < functions[i].f_min_argc) error = ERROR_TOOFEW; else if (argcount > functions[i].f_max_argc) error = ERROR_TOOMANY; else { argvars[argcount].var_type = VAR_UNKNOWN; functions[i].f_func(argvars, retvar); error = ERROR_NONE; } break; } } *arg = skipwhite(argp + 1); if (error == ERROR_NONE) ret = OK; } while (--argcount >= 0) clear_var(&argvars[argcount]); if (error < ERROR_NONE) EMSG2((char_u *)errors[error], name); name[len] = cc; return ret; } /* * "argc()" function. */ /* ARGSUSED */ static void f_argc(argvars, retvar) VAR argvars; VAR retvar; { retvar->var_val.var_number = arg_file_count; } /* * "argv()" function. */ static void f_argv(argvars, retvar) VAR argvars; VAR retvar; { int idx; idx = get_var_number(&argvars[0]); if (idx >= 0 && idx < arg_file_count) retvar->var_val.var_string = vim_strsave(arg_files[idx]); else retvar->var_val.var_string = NULL; retvar->var_type = VAR_STRING; } static BUF * get_buf_var(avar) VAR avar; { char_u *name = avar->var_val.var_string; if (avar->var_type == VAR_NUMBER) return buflist_findnr((int)avar->var_val.var_number); else if (name == NULL || *name == NUL) return curbuf; else if (name[0] == '$' && name[1] == NUL) return lastbuf; else return buflist_findnr(buflist_findpat(name, name + STRLEN(name))); } /* * "buffer_name()" function. */ static void f_bufname(argvars, retvar) VAR argvars; VAR retvar; { BUF *buf; ++emsg_off; buf = get_buf_var(&argvars[0]); retvar->var_type = VAR_STRING; if (buf != NULL && buf->b_fname != NULL) retvar->var_val.var_string = vim_strsave(buf->b_fname); else retvar->var_val.var_string = NULL; --emsg_off; } /* * "buffer_number()" function. */ static void f_bufnr(argvars, retvar) VAR argvars; VAR retvar; { BUF *buf; ++emsg_off; buf = get_buf_var(&argvars[0]); if (buf != NULL) retvar->var_val.var_number = buf->b_fnum; else retvar->var_val.var_number = -1; --emsg_off; } /* * "buffer_exists()" function. */ static void f_bufexists(argvars, retvar) VAR argvars; VAR retvar; { int n = FALSE; char_u *name; if (argvars[0].var_type == VAR_NUMBER) n = (buflist_findnr((int)argvars[0].var_val.var_number) != NULL); else if (argvars[0].var_val.var_string != NULL) { /* First make the name into a full path name */ name = FullName_save(argvars[0].var_val.var_string, #ifdef UNIX TRUE /* force expansion, get rid of symbolic links */ #else FALSE #endif ); if (name != NULL) { n = (buflist_findname(name) != NULL); vim_free(name); } } retvar->var_val.var_number = n; } /* * "char2nr()" function */ static void f_char2nr(argvars, retvar) VAR argvars; VAR retvar; { retvar->var_val.var_number = get_var_string(&argvars[0])[0]; } /* * "col(string)" function */ static void f_col(argvars, retvar) VAR argvars; VAR retvar; { colnr_t col = 0; FPOS *fp; fp = var2fpos(&argvars[0]); if (fp != NULL && fp->lnum > 0) col = fp->col + 1; retvar->var_val.var_number = col; } /* * "confirm(message, buttons[, default [, type]])" function */ static void f_confirm(argvars, retvar) VAR argvars; VAR retvar; { #if defined(GUI_DIALOG) || defined(CON_DIALOG) char_u *message; char_u *buttons; char_u buf[NUMBUFLEN]; char_u buf2[NUMBUFLEN]; int def = 0; int type = VIM_GENERIC; int c; message = get_var_string(&argvars[0]); buttons = get_var_string_buf(&argvars[1], buf); if (argvars[2].var_type != VAR_UNKNOWN) { def = get_var_number(&argvars[2]); if (argvars[3].var_type != VAR_UNKNOWN) { /* avoid that TO_UPPER calls get_var_string_buf() twice */ c = *get_var_string_buf(&argvars[3], buf2); switch (TO_UPPER(c)) { case 'E': type = VIM_ERROR; break; case 'Q': type = VIM_QUESTION; break; case 'I': type = VIM_INFO; break; case 'W': type = VIM_WARNING; break; case 'G': type = VIM_GENERIC; break; } } } retvar->var_val.var_number = do_dialog(type, NULL, message, buttons, def); #else retvar->var_val.var_number = 0; #endif } /* * "browse(save, title, initdir, default)" function */ /* ARGSUSED */ static void f_browse(argvars, retvar) VAR argvars; VAR retvar; { #ifdef USE_BROWSE int save; char_u *title; char_u *initdir; char_u *defname; char_u buf[NUMBUFLEN]; char_u buf2[NUMBUFLEN]; save = get_var_number(&argvars[0]); title = get_var_string(&argvars[1]); initdir = get_var_string_buf(&argvars[2], buf); defname = get_var_string_buf(&argvars[3], buf2); retvar->var_val.var_string = do_browse(save, title, defname, NULL, initdir, NULL, curbuf); #else retvar->var_val.var_string = NULL; #endif retvar->var_type = VAR_STRING; } /* * "delete()" function */ static void f_delete(argvars, retvar) VAR argvars; VAR retvar; { retvar->var_val.var_number = mch_remove(get_var_string(&argvars[0])); } /* * "escape({string}, {chars})" function */ static void f_escape(argvars, retvar) VAR argvars; VAR retvar; { char_u buf[NUMBUFLEN]; retvar->var_val.var_string = vim_strsave_escaped(get_var_string(&argvars[0]), get_var_string_buf(&argvars[1], buf)); retvar->var_type = VAR_STRING; } /* * "exists()" function */ static void f_exists(argvars, retvar) VAR argvars; VAR retvar; { char_u *p; char_u *name; int n = FALSE; int len; p = get_var_string(&argvars[0]); if (*p == '$') /* environment variable */ n = (get_env_string(&p) != NULL); else if (*p == '&') /* option */ n = (get_option_var(&p, NULL) == OK); else /* internal variable */ { name = p; len = get_id_len(&p); if (len != 0) n = (get_var_var(name, len, NULL) == OK); } retvar->var_val.var_number = n; } /* * "expand()" function */ static void f_expand(argvars, retvar) VAR argvars; VAR retvar; { char_u *s; int len; char_u *errormsg; retvar->var_type = VAR_STRING; s = get_var_string(&argvars[0]); if (*s == '%' || *s == '#' || *s == '<') retvar->var_val.var_string = eval_vars(s, &len, NULL, &errormsg, s); else retvar->var_val.var_string = ExpandOne(s, NULL, WILD_USE_NL, WILD_ALL); } /* * "filereadable()" function */ static void f_filereadable(argvars, retvar) VAR argvars; VAR retvar; { FILE *fd; char_u *p; int n; p = get_var_string(&argvars[0]); if (*p && !mch_isdir(p) && (fd = fopen((char *)p, "r")) != NULL) { n = TRUE; fclose(fd); } else n = FALSE; retvar->var_val.var_number = n; } /* * "fnamemodify({fname}, {mods})" function */ static void f_fnamemodify(argvars, retvar) VAR argvars; VAR retvar; { char_u *fname; char_u *mods; int usedlen = 0; int len; char_u *fbuf = NULL; char_u buf[NUMBUFLEN]; fname = get_var_string(&argvars[0]); mods = get_var_string_buf(&argvars[1], buf); len = STRLEN(fname); (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); retvar->var_type = VAR_STRING; if (fname == NULL) retvar->var_val.var_string = NULL; else retvar->var_val.var_string = vim_strnsave(fname, len); vim_free(fbuf); } /* * "getcwd()" function */ /*ARGSUSED*/ static void f_getcwd(argvars, retvar) VAR argvars; VAR retvar; { char_u cwd[MAXPATHL]; retvar->var_type = VAR_STRING; if (mch_dirname(cwd, MAXPATHL) == FAIL) retvar->var_val.var_string = NULL; else retvar->var_val.var_string = vim_strsave(cwd); } /* * "getline(lnum)" function */ static void f_getline(argvars, retvar) VAR argvars; VAR retvar; { linenr_t lnum; char_u *p; lnum = get_var_number(&argvars[0]); if (lnum == 0) /* no valid number, try using line() */ { f_line(argvars, retvar); lnum = retvar->var_val.var_number; clear_var(retvar); } if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) p = ml_get(lnum); else p = (char_u *)""; retvar->var_type = VAR_STRING; retvar->var_val.var_string = vim_strsave(p); } /* * "has()" function */ static void f_has(argvars, retvar) VAR argvars; VAR retvar; { int i; char_u *name; int n = FALSE; static char *(has_list[]) = { #ifdef AMIGA "amiga", # ifndef NO_ARP "arp", # endif #endif #ifdef __BEOS__ "beos", #endif #ifdef MSDOS # ifdef DJGPP "dos32", # else "dos16", # endif #endif #ifdef macintosh "mac", #endif #ifdef RISCOS "riscos", #endif #ifdef UNIX "unix", #endif #ifdef VMS "vms", #endif #ifdef WIN32 "win32", #endif #ifndef CASE_INSENSITIVE_FILENAME "fname_case", #endif #ifdef AUTOCMD "autocmd", #endif #if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS) "builtin_terms", # ifdef ALL_BUILTIN_TCAPS "all_builtin_terms", # endif #endif #ifdef CINDENT "cindent", #endif #ifdef USE_CSCOPE "cscope", #endif #ifdef DEBUG "debug", #endif #ifdef CON_DIALOG "dialog_con", #endif #ifdef GUI_DIALOG "dialog_gui", #endif #ifdef DIGRAPHS "digraphs", #endif #ifdef EMACS_TAGS "emacs_tags", #endif "eval", /* always present, of course! */ #ifdef EX_EXTRA "ex_extra", #endif #ifdef EXTRA_SEARCH "extra_search", #endif #ifdef FKMAP "farsi", #endif #ifdef FILE_IN_PATH "file_in_path", #endif #ifdef WANT_FILETYPE "filetype", #endif #ifdef FIND_IN_PATH "find_in_path", #endif #if !defined(USE_SYSTEM) && defined(UNIX) "fork", #endif #ifdef USE_GUI "gui", #endif #ifdef USE_GUI_ATHENA "gui_athena", #endif #ifdef USE_GUI_BEOS "gui_beos", #endif #ifdef USE_GUI_MAC "gui_mac", #endif #ifdef USE_GUI_MOTIF "gui_motif", #endif #ifdef USE_GUI_WIN32 "gui_win32", #endif #ifdef INSERT_EXPAND "insert_expand", #endif #ifdef HAVE_LANGMAP "langmap", #endif #ifdef LISPINDENT "lispindent", #endif #ifdef WANT_MODIFY_FNAME "modify_fname", #endif #ifdef USE_MOUSE "mouse", #endif #ifdef UNIX # ifdef DEC_MOUSE "mouse_dec", # endif # ifdef NETTERM_MOUSE "mouse_netterm", # endif # ifdef XTERM_MOUSE "mouse_xterm", # endif #endif #ifdef MULTI_BYTE "multi_byte", #endif #ifdef MULTI_BYTE_IME "multi_byte_ime", #endif #ifdef HAVE_OLE "ole", #endif #ifdef HAVE_PERL_INTERP "perl", #endif #ifdef HAVE_PYTHON "python", #endif #ifdef QUICKFIX "quickfix", #endif #ifdef RIGHTLEFT "rightleft", #endif #ifdef SHOWCMD "showcmd", #endif #ifdef SMARTINDENT "smartindent", #endif #ifdef SYNTAX_HL "syntax", #endif #if defined(USE_SYSTEM) || !defined(UNIX) "system", #endif #ifdef BINARY_TAGS "tag_binary", #endif #ifdef OLD_STATIC_TAGS "tag_old_static", #endif #ifdef TAG_ANY_WHITE "tag_any_white", #endif #ifdef HAVE_TCL "tcl", #endif #ifdef TERMINFO "terminfo", #endif #ifdef TEXT_OBJECTS "textobjects", #endif #ifdef HAVE_TGETENT "tgetent", #endif #ifdef USER_COMMANDS "user-commands", #endif #ifdef VIMINFO "viminfo", #endif #ifdef WILDIGNORE "wildignore", #endif #ifdef WRITEBACKUP "writebackup", #endif #ifdef SAVE_XTERM_SCREEN "xterm_save", #endif #if defined(UNIX) && defined(WANT_X11) && defined(HAVE_X11) "X11", #endif NULL }; name = get_var_string(&argvars[0]); for (i = 0; has_list[i] != NULL; ++i) if (STRICMP(name, has_list[i]) == 0) { n = TRUE; break; } if (n == FALSE) { #ifdef USE_GUI if (STRICMP(name, "gui_running") == 0) { n = (gui.in_use || gui.starting); } # ifdef USE_GUI_WIN32 else if (STRICMP(name, "gui_win32s") == 0) { n = gui_is_win32s(); } # endif #ifdef USE_BROWSE else if (STRICMP(name, "browse") == 0) { n = gui.in_use; /* gui_mch_browse() works when GUI is running */ } #endif #endif #ifdef SYNTAX_HL # ifdef USE_GUI else # endif if (STRICMP(name, "syntax_items") == 0) { n = syntax_present(curbuf); } #endif } retvar->var_val.var_number = n; } /* * "highlight_exists()" function */ static void f_hlexists(argvars, retvar) VAR argvars; VAR retvar; { retvar->var_val.var_number = highlight_exists(get_var_string(&argvars[0])); } /* * "highlightID(name)" function */ static void f_hlID(argvars, retvar) VAR argvars; VAR retvar; { retvar->var_val.var_number = syn_name2id(get_var_string(&argvars[0])); } /* * "hostname()" function */ /*ARGSUSED*/ static void f_hostname(argvars, retvar) VAR argvars; VAR retvar; { char_u hostname[256]; mch_get_host_name(hostname, 256); retvar->var_type = VAR_STRING; retvar->var_val.var_string = vim_strsave(hostname); } /* * "input()" function */ static void f_input(argvars, retvar) VAR argvars; VAR retvar; { char_u *prompt = get_var_string(&argvars[0]); char_u *p; int c; retvar->var_type = VAR_STRING; #ifdef NO_CONSOLE /* * While starting up, there is no place to enter text. */ if (!gui.in_use || gui.starting) { retvar->var_val.var_string = NULL; return; } #endif if (prompt != NULL) { /* Only the part of the message after the last NL is considered as * prompt for the command line */ p = vim_strrchr(prompt, '\n'); if (p == NULL) p = prompt; else { ++p; c = *p; *p = NUL; msg_start(); msg_clr_eos(); msg_puts_attr(prompt, echo_attr); msg_didout = FALSE; *p = c; } cmdline_prompt(p, echo_attr); cmdline_row = msg_row; } retvar->var_val.var_string = getexline('@', NULL, 0); cmdline_prompt(NULL, 0); /* since the user typed this, no need to wait for return */ need_wait_return = FALSE; msg_didout = FALSE; dont_wait_return = TRUE; } /* * "isdirectory()" function */ static void f_isdirectory(argvars, retvar) VAR argvars; VAR retvar; { retvar->var_val.var_number = mch_isdir(get_var_string(&argvars[0])); } /* * "last_buffer_nr()" function. */ /*ARGSUSED*/ static void f_last_buffer_nr(argvars, retvar) VAR argvars; VAR retvar; { int n = 0; BUF *buf; for (buf = firstbuf; buf != NULL; buf = buf->b_next) if (n < buf->b_fnum) n = buf->b_fnum; retvar->var_val.var_number = n; } /* * "line(string)" function */ static void f_line(argvars, retvar) VAR argvars; VAR retvar; { linenr_t lnum = 0; FPOS *fp; fp = var2fpos(&argvars[0]); if (fp != NULL) lnum = fp->lnum; else if (get_var_string(&argvars[0])[0] == '$') /* last line in buffer */ lnum = curbuf->b_ml.ml_line_count; retvar->var_val.var_number = lnum; } /* * "match()" function */ static void f_match(argvars, retvar) VAR argvars; VAR retvar; { f_some_match(argvars, retvar, 1); } /* * "matchend()" function */ static void f_matchend(argvars, retvar) VAR argvars; VAR retvar; { f_some_match(argvars, retvar, 0); } /* * "matchstr()" function */ static void f_matchstr(argvars, retvar) VAR argvars; VAR retvar; { f_some_match(argvars, retvar, 2); } static void f_some_match(argvars, retvar, type) VAR argvars; VAR retvar; int type; { char_u *str; char_u *pat; vim_regexp *prog; char_u patbuf[NUMBUFLEN]; str = get_var_string(&argvars[0]); pat = get_var_string_buf(&argvars[1], patbuf); if (type == 2) { retvar->var_type = VAR_STRING; retvar->var_val.var_string = NULL; } else retvar->var_val.var_number = -1; prog = vim_regcomp(pat, TRUE); if (prog != NULL) { reg_ic = p_ic; if (vim_regexec(prog, str, TRUE)) { if (type == 2) retvar->var_val.var_string = vim_strnsave(prog->startp[0], (int)(prog->endp[0] - prog->startp[0])); else if (type) retvar->var_val.var_number = prog->startp[0] - str; else retvar->var_val.var_number = prog->endp[0] - str; } vim_free(prog); } } /* * "nr2char()" function */ static void f_nr2char(argvars, retvar) VAR argvars; VAR retvar; { char_u buf[2]; buf[0] = (char_u)get_var_number(&argvars[0]); retvar->var_type = VAR_STRING; retvar->var_val.var_string = vim_strnsave(buf, 1); } /* * "setline()" function */ static void f_setline(argvars, retvar) VAR argvars; VAR retvar; { linenr_t lnum; char_u *line; lnum = get_var_number(&argvars[0]); line = get_var_string(&argvars[1]); retvar->var_val.var_number = 1; /* FAIL is default */ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) { changed(); #ifdef SYNTAX_HL /* recompute syntax hl. for this line */ syn_changed(lnum); #endif redraw_curbuf_later(NOT_VALID); retvar->var_val.var_number = 0; } } } #ifdef HAVE_STRFTIME /* * "strftime()" function */ static void f_strftime(argvars, retvar) VAR argvars; VAR retvar; { char_u result_buf[80]; struct tm *curtime; time_t seconds; char_u *p; p = get_var_string(&argvars[0]); seconds = time(NULL); curtime = localtime(&seconds); (void)strftime((char *)result_buf, (size_t)80, (char *)p, curtime); retvar->var_type = VAR_STRING; retvar->var_val.var_string = vim_strsave(result_buf); } #endif /* * "strlen()" function */ static void f_strlen(argvars, retvar) VAR argvars; VAR retvar; { retvar->var_val.var_number = STRLEN(get_var_string(&argvars[0])); } /* * "strpart()" function */ static void f_strpart(argvars, retvar) VAR argvars; VAR retvar; { char_u *p; int n; int len; int slen; p = get_var_string(&argvars[0]); n = get_var_number(&argvars[1]); len = get_var_number(&argvars[2]); slen = STRLEN(p); /* * Only return the overlap between the specified part and the actual * string. */ if (n < 0) { len += n; n = 0; } else if (n > slen) n = slen; if (len < 0) len = 0; else if (n + len > slen) len = slen - n; retvar->var_type = VAR_STRING; retvar->var_val.var_string = vim_strnsave(p + n, len); } /* * "synID(line, col, trans)" function */ static void f_synID(argvars, retvar) VAR argvars; VAR retvar; { int id = 0; #ifdef SYNTAX_HL long line; long col; int trans; line = get_var_number(&argvars[0]); col = get_var_number(&argvars[1]) - 1; trans = get_var_number(&argvars[2]); if (line >= 1 && line <= curbuf->b_ml.ml_line_count && col >= 0 && col < (long)STRLEN(ml_get(line))) id = syn_get_id(line, col, trans); #endif retvar->var_val.var_number = id; } /* * "synIDattr(id, what [, mode])" function */ static void f_synIDattr(argvars, retvar) VAR argvars; VAR retvar; { char_u *p = NULL; #ifdef SYNTAX_HL int id; char_u *what; char_u *mode; char_u modebuf[NUMBUFLEN]; int modec; id = get_var_number(&argvars[0]); what = get_var_string(&argvars[1]); if (argvars[2].var_type != VAR_UNKNOWN) { mode = get_var_string_buf(&argvars[2], modebuf); modec = TO_LOWER(mode[0]); if (modec != 't' && modec != 'c' #ifdef USE_GUI && modec != 'g' #endif ) modec = 0; /* replace invalid with current */ } else { #ifdef USE_GUI if (gui.in_use) modec = 'g'; else #endif if (*T_CCO) modec = 'c'; else modec = 't'; } switch (TO_LOWER(what[0])) { case 'b': if (TO_LOWER(what[1]) == 'g') /* bg[#] */ p = highlight_color(id, what, modec); else /* bold */ p = highlight_has_attr(id, HL_BOLD, modec); break; case 'f': /* fg[#] */ p = highlight_color(id, what, modec); break; case 'i': if (TO_LOWER(what[1]) == 'n') /* inverse */ p = highlight_has_attr(id, HL_INVERSE, modec); else /* italic */ p = highlight_has_attr(id, HL_ITALIC, modec); break; case 'n': /* name */ p = get_highlight_name(id - 1); break; case 'r': /* reverse */ p = highlight_has_attr(id, HL_INVERSE, modec); break; case 's': /* standout */ p = highlight_has_attr(id, HL_STANDOUT, modec); break; case 'u': /* underline */ p = highlight_has_attr(id, HL_UNDERLINE, modec); break; } if (p != NULL) p = vim_strsave(p); #endif retvar->var_type = VAR_STRING; retvar->var_val.var_string = p; } /* * "synIDtrans(id)" function */ static void f_synIDtrans(argvars, retvar) VAR argvars; VAR retvar; { int id; #ifdef SYNTAX_HL id = get_var_number(&argvars[0]); if (id > 0) id = syn_get_final_id(id); else #endif id = 0; retvar->var_val.var_number = id; } /* * "substitute()" function */ static void f_substitute(argvars, retvar) VAR argvars; VAR retvar; { char_u patbuf[NUMBUFLEN]; char_u subbuf[NUMBUFLEN]; char_u flagsbuf[NUMBUFLEN]; retvar->var_type = VAR_STRING; retvar->var_val.var_string = do_string_sub( get_var_string(&argvars[0]), get_var_string_buf(&argvars[1], patbuf), get_var_string_buf(&argvars[2], subbuf), get_var_string_buf(&argvars[3], flagsbuf)); } /* * "tempname()" function */ /*ARGSUSED*/ static void f_tempname(argvars, retvar) VAR argvars; VAR retvar; { static int x = 'A'; retvar->var_type = VAR_STRING; retvar->var_val.var_string = vim_tempname(x); /* advance 'x', so that there are at least 26 different names */ if (x == 'Z') x = 'A'; else ++x; } /* * "virtcol(string)" function */ static void f_virtcol(argvars, retvar) VAR argvars; VAR retvar; { colnr_t vcol = 0; FPOS *fp; fp = var2fpos(&argvars[0]); if (fp != NULL) { getvcol(curwin, fp, NULL, NULL, &vcol); ++vcol; } retvar->var_val.var_number = vcol; } /* * "winbufnr(nr)" function */ static void f_winbufnr(argvars, retvar) VAR argvars; VAR retvar; { WIN *wp; wp = find_win_by_nr(&argvars[0]); if (wp == NULL) retvar->var_val.var_number = -1; else retvar->var_val.var_number = wp->w_buffer->b_fnum; } /* * "winheight(nr)" function */ static void f_winheight(argvars, retvar) VAR argvars; VAR retvar; { WIN *wp; wp = find_win_by_nr(&argvars[0]); if (wp == NULL) retvar->var_val.var_number = -1; else retvar->var_val.var_number = wp->w_height; } /* * "winnr()" function */ /* ARGSUSED */ static void f_winnr(argvars, retvar) VAR argvars; VAR retvar; { int nr; WIN *wp; nr = 1; for (wp = firstwin; wp != curwin; wp = wp->w_next) ++nr; retvar->var_val.var_number = nr; } static WIN * find_win_by_nr(vp) VAR vp; { WIN *wp; int nr; nr = get_var_number(vp); if (nr == 0) return curwin; for (wp = firstwin; wp != NULL; wp = wp->w_next) { if (--nr <= 0) break; } return wp; } /* * Translate a String variable into a position (for col() and virtcol()). */ static FPOS * var2fpos(varp) VAR varp; { char_u *name; name = get_var_string(varp); if (name[0] == '.') /* cursor */ return &curwin->w_cursor; if (name[0] == '\'') /* mark */ return getmark(name[1], FALSE); return NULL; } /* * Get the lenght of an environment variable name. * Advance "arg" to the first character after the name. * Return 0 for error. */ static int get_env_len(arg) char_u **arg; { char_u *p; int len; for (p = *arg; vim_isIDc(*p); ++p) ; if (p == *arg) /* no name found */ return 0; len = p - *arg; *arg = p; return len; } /* * Get the value of an environment variable. * "arg" points to the '$' before the env var name. * "arg" is advanced to the first character after the name. * Return NULL for failure. */ char_u * get_env_string(arg) char_u **arg; { int len; int cc; char_u *name; char_u *s; ++*arg; name = *arg; len = get_env_len(arg); if (len == 0) return NULL; cc = name[len]; name[len] = NUL; s = mch_getenv(name); name[len] = cc; return s; } /* * Get the length of the name of a function or internal variable. * "arg" is advanced to the first non-white character after the name. * Return 0 if something is wrong. */ static int get_id_len(arg) char_u **arg; { char_u *p; int len; /* Find the end of the name. */ for (p = *arg; eval_isnamec(*p); ++p) ; if (p == *arg) /* no name found */ return 0; len = p - *arg; *arg = skipwhite(p); return len; } static int eval_isnamec(c) int c; { return (isalpha(c) || isdigit(c) || c == '_' || c == ':'); } /* * Get the value of internal variable "name". * Return OK or FAIL. */ static int get_var_var(name, len, retvar) char_u *name; int len; /* length of "name" */ VAR retvar; /* NULL when only checking existence */ { int ret = OK; int type = VAR_UNKNOWN; long number = 1; char_u *string = NULL; VAR v; int cc; cc = name[len]; name[len] = NUL; /* * Check for built-in variables. */ if (len == 7 && STRCMP(name, "version") == 0) { type = VAR_NUMBER; number = get_version(); } else if (len == 5 && STRCMP(name, "count") == 0) { type = VAR_NUMBER; number = global_opnum; } else if (len == 11 && STRCMP(name, "shell_error") == 0) { type = VAR_NUMBER; number = call_shell_retval; } /* * Check for user-defined variables. */ else { v = find_var(name, FALSE); if (v != NULL) { type = v->var_type; number = v->var_val.var_number; string = v->var_val.var_string; } } if (type == VAR_UNKNOWN) { if (retvar != NULL) EMSG2("Undefined variable: %s", name); ret = FAIL; } else if (retvar != NULL) { retvar->var_type = type; if (type == VAR_NUMBER) retvar->var_val.var_number = number; else { if (string != NULL) string = vim_strsave(string); retvar->var_val.var_string = string; } } name[len] = cc; return ret; } /* * Allocate memory for a variable, and make it emtpy (0 or NULL value). */ static VAR alloc_var() { return (VAR)alloc_clear((unsigned)sizeof(var)); } /* * Allocate memory for a variable, and assign a string to it. * The string "s" must have been allocated, it is consumed. * Return NULL for out of memory, the variable otherwise. */ static VAR alloc_string_var(s) char_u *s; { VAR retvar; retvar = alloc_var(); if (retvar != NULL) { retvar->var_type = VAR_STRING; retvar->var_val.var_string = s; } else vim_free(s); return retvar; } /* * 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); } } /* * Free the memory for a variable value and set the value to NULL or 0. */ static void clear_var(varp) VAR varp; { if (varp != NULL) { if (varp->var_type == VAR_STRING) { vim_free(varp->var_val.var_string); varp->var_val.var_string = NULL; } else varp->var_val.var_number = 0; } } /* * Get the number value of a variable. * If it is a String variable, use vim_str2nr(). */ static long get_var_number(varp) VAR varp; { long n; if (varp->var_type == VAR_NUMBER) return (long)(varp->var_val.var_number); else if (varp->var_val.var_string == NULL) return 0L; else { vim_str2nr(varp->var_val.var_string, NULL, NULL, TRUE, TRUE, &n, NULL); return n; } } /* * Get the string value of a variable. * If it is a Number variable, the number is converted into a string. * get_var_string() uses a single, static buffer. You can only use it once! * get_var_string_buf() uses a given buffer. * If the String variable has never been set, return an empty string. * Never returns NULL; */ static char_u * get_var_string(varp) VAR varp; { static char_u mybuf[NUMBUFLEN]; return get_var_string_buf(varp, mybuf); } static char_u * get_var_string_buf(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 a pointer to it if found, NULL if not found. */ static VAR find_var(name, writing) char_u *name; int writing; { int i; char_u *varname; struct growarray *gap; /* When not writing, check for function arguments "a:" */ if (!writing && name[0] == 'a' && name[1] == ':') { name += 2; if (current_funccal == NULL) return NULL; if (isdigit(*name)) { i = atol((char *)name); if (i == 0) /* a:0 */ return ¤t_funccal->a0_var; i += current_funccal->func->args.ga_len; if (i > current_funccal->argcount) /* a:999 */ return NULL; return &(current_funccal->argvars[i - 1]); /* a:1, a:2, etc. */ } if (STRCMP(name, "firstline") == 0) return &(current_funccal->firstline); if (STRCMP(name, "lastline") == 0) return &(current_funccal->lastline); for (i = 0; i < current_funccal->func->args.ga_len; ++i) if (STRCMP(name, ((char_u **) (current_funccal->func->args.ga_data))[i]) == 0) return &(current_funccal->argvars[i]); /* a:name */ return NULL; } gap = find_var_ga(name, &varname); if (gap == NULL) return NULL; for (i = gap->ga_len; --i >= 0; ) if (VAR_GAP_ENTRY(i, gap).var_name != NULL && STRCMP(VAR_GAP_ENTRY(i, gap).var_name, varname) == 0) break; if (i < 0) return NULL; return &VAR_GAP_ENTRY(i, gap); } /* * Find the growarray and start of name without ':' for a variable name. */ static struct growarray * find_var_ga(name, varname) char_u *name; char_u **varname; { if (name[1] != ':') { *varname = name; if (current_funccal == NULL) return &variables; /* global variable */ return ¤t_funccal->l_vars; /* local function variable */ } *varname = name + 2; if (*name == 'b') /* buffer variable */ return &curbuf->b_vars; if (*name == 'w') /* window variable */ return &curwin->w_vars; if (*name == 'g') /* global variable */ return &variables; if (*name == 'l' && current_funccal != NULL)/* local function variable */ return ¤t_funccal->l_vars; return NULL; } /* * Initialize internal variables for use. */ void var_init(gap) struct growarray *gap; { ga_init2(gap, (int)sizeof(var), 4); } /* * Clean up a list of internal variables. */ void var_clear(gap) struct growarray *gap; { int i; for (i = gap->ga_len; --i >= 0; ) var_free_one(&VAR_GAP_ENTRY(i, gap)); ga_clear(gap); } static void var_free_one(v) VAR v; { vim_free(v->var_name); v->var_name = NULL; if (v->var_type == VAR_STRING) vim_free(v->var_val.var_string); v->var_val.var_string = NULL; } /* * List the value of one internal variable. */ static void list_one_var(v, prefix) VAR v; char_u *prefix; { msg(prefix); msg_puts(v->var_name); msg_putchar(' '); msg_advance(22); if (v->var_type == VAR_NUMBER) msg_putchar('#'); else msg_putchar(' '); msg_outtrans(get_var_string(v)); } /* * 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; VAR v; char_u *varname; struct growarray *gap; v = find_var(name, TRUE); if (v != NULL) /* existing variable, only need to free string */ { if (v->var_type == VAR_STRING) vim_free(v->var_val.var_string); } else /* add a new variable */ { gap = find_var_ga(name, &varname); if (gap == NULL) /* illegal name */ return; /* Try to use an empty entry */ for (i = gap->ga_len; --i >= 0; ) if (VAR_GAP_ENTRY(i, gap).var_name == NULL) break; if (i < 0) /* need to allocated more room */ { if (ga_grow(gap, 1) == FAIL) return; i = gap->ga_len; } v = &VAR_GAP_ENTRY(i, gap); if ((v->var_name = vim_strsave(varname)) == NULL) return; if (i == gap->ga_len) { ++gap->ga_len; --gap->ga_room; } } v->var_type = varp->var_type; if (varp->var_type == VAR_STRING) v->var_val.var_string = vim_strsave(get_var_string(varp)); else v->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(eap, echo) EXARG *eap; int echo; /* TRUE for ":echo" command, FALSE for ":echon" */ { char_u *arg = eap->arg; var retvar; char_u *p; int needclr = TRUE; int atstart = TRUE; if (eap->skip) ++emsg_off; else if (echo) msg_start(); while (*arg != NUL && *arg != '|' && *arg != '\n') { if (eval1(&arg, &retvar) == FAIL) break; if (!eap->skip) { if (atstart) atstart = FALSE; else if (echo) msg_puts_attr((char_u *)" ", echo_attr); for (p = get_var_string(&retvar); *p != NUL; ++p) if (*p == '\n' || *p == '\r' || *p == TAB) { if (*p != TAB && needclr) { /* remove any text still there from the command */ msg_clr_eos(); needclr = FALSE; } msg_putchar_attr(*p, echo_attr); } else (void)msg_outtrans_len_attr(p, 1, echo_attr); } clear_var(&retvar); arg = skipwhite(arg); } eap->nextcmd = check_nextcmd(arg); if (eap->skip) --emsg_off; else { /* remove text that may still be there from the command */ if (needclr) msg_clr_eos(); if (echo) msg_end(); } } /* * Implementation of ":echohl {name}". */ void do_echohl(arg) char_u *arg; { int id; id = syn_name2id(arg); if (id == 0) echo_attr = 0; else echo_attr = syn_id2attr(id); } /* * Implementation of * ":execute expr1 ..." execute the result of an expression. */ void do_execute(eap, getline, cookie) EXARG *eap; char_u *(*getline) __ARGS((int, void *, int)); void *cookie; /* argument for getline() */ { char_u *arg = eap->arg; var retvar; int ret = OK; char_u *p; struct growarray ga; int len; ga_init2(&ga, 1, 80); if (eap->skip) ++emsg_off; while (*arg != NUL && *arg != '|' && *arg != '\n') { if (eval1(&arg, &retvar) == FAIL) { ret = FAIL; break; } if (!eap->skip) { p = get_var_string(&retvar); len = STRLEN(p); if (ga_grow(&ga, len + 2) == FAIL) { clear_var(&retvar); ret = FAIL; break; } if (ga.ga_len) { ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; --ga.ga_room; } STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); ga.ga_room -= len; ga.ga_len += len; } clear_var(&retvar); arg = skipwhite(arg); } if (ret != FAIL && ga.ga_data != NULL) do_cmdline((char_u *)ga.ga_data, getline, cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); ga_clear(&ga); if (eap->skip) --emsg_off; eap->nextcmd = check_nextcmd(arg); } static char_u * find_option_end(p) char_u *p; { while (isalnum(*p) || *p == '_') ++p; return p; } /* * Handle the ":function" command. * "getline" can be NULL, in which case a line is obtained from the user. */ void do_function(eap, getline, cookie) EXARG *eap; char_u *(*getline) __ARGS((int, void *, int)); void *cookie; /* argument for getline() */ { char_u *theline; int j; int c; char_u *name; char_u *nameend; char_u *p; char_u *arg; struct growarray newargs; struct growarray newlines; int varargs = FALSE; int mustend = FALSE; int flags = 0; struct ufunc *fp; /* * ":function" without argument: list functions. */ if (*eap->arg == NUL) { if (!eap->skip) for (fp = firstfunc; fp != NULL; fp = fp->next) list_func_head(fp); return; } if (!isupper(*eap->arg)) { EMSG2("Function name must start with a capital: %s", eap->arg); return; } /* * ":function func" with only function name: list function. */ if (vim_strchr(eap->arg, '(') == NULL) { if (!eap->skip) { fp = find_func(eap->arg); if (fp != NULL) { list_func_head(fp); for (j = 0; j < fp->lines.ga_len; ++j) { msg_putchar('\n'); msg_prt_line(FUNCLINE(fp, j)); } MSG("endfunction"); } else EMSG2("Undefined function: %s", eap->arg); } return; } /* * ":function name(arg1, arg2)" Define function. */ name = eap->arg; for (p = name; isalpha(*p) || isdigit(*p) || *p == '_'; ++p) ; if (p == name) { EMSG("Function name required"); return; } nameend = p; p = skipwhite(p); if (*p != '(') { EMSG2("Missing '(': %s", name); return; } p = skipwhite(p + 1); ga_init2(&newargs, (int)sizeof(char_u *), 3); ga_init2(&newlines, (int)sizeof(char_u *), 3); /* * Isolate the arguments: "arg1, arg2, ...)" */ while (*p != ')') { if (p[0] == '.' && p[1] == '.' && p[2] == '.') { varargs = TRUE; p += 3; mustend = TRUE; } else { arg = p; while (isalpha(*p) || isdigit(*p) || *p == '_') ++p; if (arg == p || isdigit(*arg)) { EMSG2("Illegal argument: %s", arg); goto erret; } if (ga_grow(&newargs, 1) == FAIL) goto erret; c = *p; *p = NUL; arg = vim_strsave(arg); if (arg == NULL) goto erret; ((char_u **)(newargs.ga_data))[newargs.ga_len] = arg; *p = c; newargs.ga_len++; newargs.ga_room--; if (*p == ',') ++p; else mustend = TRUE; } p = skipwhite(p); if (mustend && *p != ')') { EMSG2(e_invarg2, eap->arg); goto erret; } } ++p; /* skip the ')' */ /* find extra arguments "range" and "abort" */ for (;;) { p = skipwhite(p); if (STRNCMP(p, "range", 5) == 0) { flags |= FC_RANGE; p += 5; } else if (STRNCMP(p, "abort", 5) == 0) { flags |= FC_ABORT; p += 5; } else break; } if (*p != NUL && *p != '\"' && *p != '\n') { EMSG(e_trailing); goto erret; } /* * Read the body of the function, until ":endfunction" is found. */ if (KeyTyped) { msg_putchar('\n'); /* don't overwrite the function name */ cmdline_row = msg_row; } for (;;) { msg_scroll = TRUE; need_wait_return = FALSE; if (getline == NULL) theline = getcmdline(':', 0L, 2); else theline = getline(':', cookie, 2); lines_left = Rows - 1; if (theline == NULL) goto erret; for (p = theline; vim_iswhite(*p) || *p == ':'; ++p) ; if (STRNCMP(p, "endf", 4) == 0) { vim_free(theline); break; } if (ga_grow(&newlines, 1) == FAIL) goto erret; ((char_u **)(newlines.ga_data))[newlines.ga_len] = theline; newlines.ga_len++; newlines.ga_room--; } if (eap->skip) goto erret; /* * If there are no errors, add the function */ *nameend = NUL; fp = find_func(name); if (fp != NULL) { if (!eap->forceit) { EMSG2("Function %s already exists, use ! to replace", name); goto erret; } /* redefine existing function */ ga_clear_strings(&(fp->args)); ga_clear_strings(&(fp->lines)); } else { fp = (struct ufunc *)alloc((unsigned)sizeof(struct ufunc)); if (fp == NULL) goto erret; name = vim_strsave(name); if (name == NULL) { vim_free(fp); goto erret; } /* insert the new function in the function list */ fp->next = firstfunc; firstfunc = fp; fp->name = name; } fp->args = newargs; fp->lines = newlines; fp->varargs = varargs; fp->flags = flags; return; erret: ga_clear_strings(&newargs); ga_clear_strings(&newlines); } /* * List the head of the function: "name(arg1, arg2)". */ static void list_func_head(fp) struct ufunc *fp; { int j; MSG("function "); msg_puts(fp->name); msg_putchar('('); for (j = 0; j < fp->args.ga_len; ++j) { if (j) MSG_PUTS(", "); msg_puts(FUNCARG(fp, j)); } if (fp->varargs) { if (j) MSG_PUTS(", "); MSG_PUTS("..."); } msg_putchar(')'); } /* * Find a function by name, return its index in ufuncs. * Return -1 for unknown function. */ static struct ufunc * find_func(name) char_u *name; { struct ufunc *fp; for (fp = firstfunc; fp != NULL; fp = fp->next) if (STRCMP(name, fp->name) == 0) break; return fp; } /* * Handle ":delfunction {name}". */ void do_delfunction(arg) char_u *arg; { struct ufunc *fp, *pfp; fp = find_func(arg); if (fp == NULL) { EMSG2("Undefined function: %s", arg); return; } /* clear this function */ vim_free(fp->name); ga_clear_strings(&(fp->args)); ga_clear_strings(&(fp->lines)); /* remove the function from the function list */ if (firstfunc == fp) firstfunc = fp->next; else for (pfp = firstfunc; pfp != NULL; pfp = pfp->next) if (pfp->next == fp) { pfp->next = fp->next; break; } } /* * Call a user function. */ static void call_func(fp, argcount, argvars, retvar, firstline, lastline) struct ufunc *fp; /* pointer to function */ int argcount; /* nr of args */ VAR argvars; /* arguments */ VAR retvar; /* return value */ linenr_t firstline; /* first line of range */ linenr_t lastline; /* last line of range */ { char_u *save_sourcing_name; int save_redrawing = RedrawingDisabled; struct funccall fc; struct funccall *save_fcp = current_funccal; int save_did_emsg; static int depth = 0; /* If depth of calling is getting too high, don't execute the function */ if (depth >= p_mfd) { EMSG("Function call depth is higher than 'maxfuncdepth'"); retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = -1; return; } ++depth; line_breakcheck(); /* check for CTRL-C hit */ /* set local variables */ var_init(&fc.l_vars); fc.func = fp; fc.argcount = argcount; fc.argvars = argvars; fc.retvar = retvar; retvar->var_val.var_number = 0; fc.linenr = 0; fc.a0_var.var_type = VAR_NUMBER; fc.a0_var.var_val.var_number = argcount - fp->args.ga_len; fc.a0_var.var_name = NULL; current_funccal = &fc; fc.firstline.var_type = VAR_NUMBER; fc.firstline.var_val.var_number = firstline; fc.firstline.var_name = NULL; fc.lastline.var_type = VAR_NUMBER; fc.lastline.var_val.var_number = lastline; fc.lastline.var_name = NULL; /* Don't redraw while executing the function. */ RedrawingDisabled = TRUE; save_sourcing_name = sourcing_name; sourcing_name = alloc((unsigned)((save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name)) + STRLEN(fp->name) + 10)); if (sourcing_name != NULL) { if (save_sourcing_name != NULL && STRNCMP(save_sourcing_name, "function ", 9) == 0) sprintf((char *)sourcing_name, "%s->", save_sourcing_name); else STRCPY(sourcing_name, "function "); STRCAT(sourcing_name, fp->name); } save_did_emsg = did_emsg; did_emsg = FALSE; /* call do_cmdline() to execute the lines */ do_cmdline(NULL, get_func_line, (void *)&fc, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); RedrawingDisabled = save_redrawing; vim_free(sourcing_name); sourcing_name = save_sourcing_name; /* when the function was aborted because of an error, return -1 */ if (did_emsg && (fp->flags & FC_ABORT)) { clear_var(retvar); retvar->var_type = VAR_NUMBER; retvar->var_val.var_number = -1; } did_emsg |= save_did_emsg; current_funccal = save_fcp; var_clear(&fc.l_vars); /* free all local variables */ --depth; } /* * Save the current function call pointer, and set it to NULL. * Used when executing autocommands and for ":source". */ void * save_funccal() { struct funccall *fc; fc = current_funccal; current_funccal = NULL; return (void *)fc; } void restore_funccal(fc) void *fc; { current_funccal = (struct funccall *)fc; } /* * Handle ":return [expr]". */ void do_return(eap) EXARG *eap; { char_u *arg = eap->arg; var retvar; if (current_funccal == NULL) { EMSG(":return not inside a function"); return; } if (eap->skip) ++emsg_off; else current_funccal->linenr = -1; if (*arg != NUL && *arg != '|' && *arg != '\n') { if (eval1(&arg, &retvar) != FAIL) { if (!eap->skip) { clear_var(current_funccal->retvar); *current_funccal->retvar = retvar; } else clear_var(&retvar); } } /* when skipping, advance to the next command in this line. When not * skipping, ignore the rest of the line. Following lines will be ignored * by get_func_line(). */ if (eap->skip) { --emsg_off; eap->nextcmd = check_nextcmd(arg); } else eap->nextcmd = NULL; } /* * Get next function line. * Called by do_cmdline() to get the next line. * Returns allocated string, or NULL for end of function. */ /* ARGSUSED */ char_u * get_func_line(c, cookie, indent) int c; /* not used */ void *cookie; int indent; /* not used */ { struct funccall *fcp = (struct funccall *)cookie; char_u *retval; struct growarray *gap; /* growarray with function lines */ gap = &fcp->func->lines; if ((fcp->func->flags & FC_ABORT) && did_emsg) retval = NULL; else if (fcp->linenr < 0 || fcp->linenr >= gap->ga_len) retval = NULL; else retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); if (p_verbose >= 15) { msg_scroll = TRUE; /* always scroll up, don't overwrite */ if (retval == NULL) msg((char_u *)"function returning"); else smsg((char_u *)"function line %s", retval); msg_puts((char_u *)"\n"); /* don't overwrite this either */ cmdline_row = msg_row; } return retval; } /* * Return TRUE if the currently active function should be ended, because a * return was encountered or an error occured. Used inside a ":while". */ int func_has_ended(cookie) void *cookie; { struct funccall *fcp = (struct funccall *)cookie; return (((fcp->func->flags & FC_ABORT) && did_emsg) || fcp->linenr < 0); } /* * return TRUE if cookie indicates a function which "abort"s on errors. */ int func_has_abort(cookie) void *cookie; { return ((struct funccall *)cookie)->func->flags & FC_ABORT; } #endif /* WANT_EVAL */ #if defined(WANT_MODIFY_FNAME) || defined(WANT_EVAL) /* * Adjust a filename, according to a string of modifiers. * *fnamep must be NUL terminated when called. When returning, the length is * determined by *fnamelen. * Returns valid flags. * When there is an error, *fnamep is set to NULL. */ int modify_fname(src, usedlen, fnamep, bufp, fnamelen) char_u *src; /* string with modifiers */ int *usedlen; /* characters after src that are used */ char_u **fnamep; /* file name so far */ char_u **bufp; /* buffer for allocated file name or NULL */ int *fnamelen; /* length of fnamep */ { int valid = 0; char_u *tail; char_u *s, *p; char_u dirname[MAXPATHL]; repeat: /* ":p" - full path/file_name */ if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') { valid |= VALID_PATH; *usedlen += 2; *fnamep = FullName_save(*fnamep, FALSE); vim_free(*bufp); /* free any allocated file name */ *bufp = *fnamep; if (*fnamep == NULL) return -1; } /* ":." - path relative to the current directory */ if (src[*usedlen] == ':' && src[*usedlen + 1] == '.') { *usedlen += 2; /* Need full path first (use force to remove a "~/") */ p = FullName_save(*fnamep, **fnamep == '~'); if (p != NULL) { mch_dirname(dirname, MAXPATHL); s = shorten_fname(p, dirname); if (s != NULL) { *fnamep = s; vim_free(*bufp); /* free any allocated file name */ *bufp = p; } else vim_free(p); } } /* ":~" - path relative to the home directory */ if (src[*usedlen] == ':' && src[*usedlen + 1] == '~') { *usedlen += 2; /* Need full path first */ p = FullName_save(*fnamep, FALSE); if (p != NULL) { home_replace(NULL, p, dirname, MAXPATHL, TRUE); /* Only replace it when it starts with '~' */ if (*dirname == '~') { s = vim_strsave(dirname); if (s != NULL) { *fnamep = s; vim_free(*bufp); *bufp = s; } } vim_free(p); } } tail = gettail(*fnamep); *fnamelen = STRLEN(*fnamep); /* ":h" - head, remove "/file_name", can be repeated */ /* Don't remove the first "/" or "c:\" */ while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') { valid |= VALID_HEAD; *usedlen += 2; s = get_past_head(*fnamep); while (tail > s && vim_ispathsep(tail[-1])) --tail; *fnamelen = tail - *fnamep; while (tail > s && !vim_ispathsep(tail[-1])) --tail; } /* ":t" - tail, just the basename */ if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') { *usedlen += 2; *fnamelen -= tail - *fnamep; *fnamep = tail; } /* ":e" - extension, can be repeated */ /* ":r" - root, without extension, can be repeated */ while (src[*usedlen] == ':' && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) { /* find a '.' in the tail: * - for second :e: before the current fname * - otherwise: The last '.' */ if (src[*usedlen + 1] == 'e' && *fnamep > tail) s = *fnamep - 2; else s = *fnamep + *fnamelen - 1; for ( ; s > tail; --s) if (s[0] == '.') break; if (src[*usedlen + 1] == 'e') /* :e */ { if (s > tail) { *fnamelen += *fnamep - (s + 1); *fnamep = s + 1; } else if (*fnamep <= tail) *fnamelen = 0; } else /* :r */ { if (s > tail) /* remove one extension */ *fnamelen = s - *fnamep; } *usedlen += 2; } /* ":s?pat?foo?" - substitute */ /* ":gs?pat?foo?" - global substitute */ if (src[*usedlen] == ':' && (src[*usedlen + 1] == 's' || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) { char_u *str; char_u *pat; char_u *sub; int sep; char_u *flags; int didit = FALSE; flags = (char_u *)""; s = src + *usedlen + 2; if (src[*usedlen + 1] == 'g') { flags = (char_u *)"g"; ++s; } sep = *s++; if (sep) { /* find end of pattern */ p = vim_strchr(s, sep); if (p != NULL) { pat = vim_strnsave(s, (int)(p - s)); if (pat != NULL) { s = p + 1; /* find end of substitution */ p = vim_strchr(s, sep); if (p != NULL) { sub = vim_strnsave(s, (int)(p - s)); str = vim_strnsave(*fnamep, *fnamelen); if (sub != NULL && str != NULL) { *usedlen = p + 1 - src; s = do_string_sub(str, pat, sub, flags); if (s != NULL) { *fnamep = s; *fnamelen = STRLEN(s); vim_free(*bufp); *bufp = s; didit = TRUE; } } vim_free(sub); vim_free(str); } vim_free(pat); } } /* after using ":s", repeat all the modifiers */ if (didit) goto repeat; } } return valid; } /* * Perform a substitution on "str" with pattern "pat" and substitute "sub". * "flags" can be "g" to do a global substitute. * Returns an allocated string, NULL for error. */ char_u * do_string_sub(str, pat, sub, flags) char_u *str; char_u *pat; char_u *sub; char_u *flags; { int sublen; vim_regexp *prog; int i; int do_all; char_u *tail; struct growarray ga; char_u *ret; ga_init2(&ga, 1, 200); do_all = (flags[0] == 'g'); reg_ic = p_ic; prog = vim_regcomp(pat, TRUE); if (prog != NULL) { tail = str; while (vim_regexec(prog, tail, tail == str)) { /* * Get some space for a temporary buffer to do the substitution * into. It will contain: * - The text up to where the match is. * - The substituted text. * - The text after the match. */ sublen = vim_regsub(prog, sub, tail, FALSE, TRUE); if (ga_grow(&ga, (int)(STRLEN(tail) + sublen - (prog->endp[0] - prog->startp[0]))) == FAIL) { ga_clear(&ga); break; } /* copy the text up to where the match is */ i = prog->startp[0] - tail; mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); /* add the substituted text */ (void)vim_regsub(prog, sub, (char_u *)ga.ga_data + ga.ga_len + i, TRUE, TRUE); ga.ga_len += i + sublen - 1; ga.ga_room -= i + sublen - 1; /* avoid getting stuck on a match with an empty string */ if (tail == prog->endp[0]) { *((char_u *)ga.ga_data + ga.ga_len) = *tail++; ++ga.ga_len; --ga.ga_room; } else { tail = prog->endp[0]; if (*tail == NUL) break; } if (!do_all) break; } if (ga.ga_data != NULL) STRCPY((char *)ga.ga_data + ga.ga_len, tail); vim_free(prog); } ret = vim_strsave(ga.ga_data == NULL ? str : (char_u *)ga.ga_data); ga_clear(&ga); return ret; } #endif /* defined(WANT_MODIFY_FNAME) || defined(WANT_EVAL) */