Patch 8.2.0675

5 views
Skip to first unread message

Bram Moolenaar

unread,
May 1, 2020, 9:45:05 AM5/1/20
to vim...@googlegroups.com

Patch 8.2.0675
Problem: Vim9: no support for closures.
Solution: Do not re-use stack entries.
Files: src/vim9compile.c, src/ex_docmd.c, src/proto/ex_docmd.pro,
src/evalvars.c, src/proto/evalvars.pro


*** ../vim-8.2.0674/src/vim9compile.c 2020-04-28 22:49:04.592008615 +0200
--- src/vim9compile.c 2020-05-01 15:33:54.721981415 +0200
***************
*** 97,102 ****
--- 97,103 ----
typedef struct {
char_u *lv_name;
type_T *lv_type;
+ int lv_idx; // index of the variable on the stack
int lv_const; // when TRUE cannot be assigned to
int lv_arg; // when TRUE this is an argument
} lvar_T;
***************
*** 112,118 ****
garray_T ctx_instr; // generated instructions

garray_T ctx_locals; // currently visible local variables
! int ctx_max_local; // maximum number of locals at one time

garray_T ctx_imports; // imported items

--- 113,119 ----
garray_T ctx_instr; // generated instructions

garray_T ctx_locals; // currently visible local variables
! int ctx_locals_count; // total number of local variables

garray_T ctx_imports; // imported items

***************
*** 120,125 ****
--- 121,129 ----
// commands after "else"
scope_T *ctx_scope; // current scope, NULL at toplevel

+ cctx_T *ctx_outer; // outer scope for lambda or nested
+ // function
+
garray_T ctx_type_stack; // type of each item on the stack
garray_T *ctx_type_list; // list of pointers to allocated types
};
***************
*** 135,158 ****
static int check_type(type_T *expected, type_T *actual, int give_msg);

/*
! * Lookup variable "name" in the local scope and return the index.
*/
! static int
lookup_local(char_u *name, size_t len, cctx_T *cctx)
{
int idx;

if (len == 0)
! return -1;
for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
{
lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;

if (STRNCMP(name, lvar->lv_name, len) == 0
&& STRLEN(lvar->lv_name) == len)
! return idx;
}
! return -1;
}

/*
--- 139,163 ----
static int check_type(type_T *expected, type_T *actual, int give_msg);

/*
! * Lookup variable "name" in the local scope and return it.
! * Return NULL if not found.
*/
! static lvar_T *
lookup_local(char_u *name, size_t len, cctx_T *cctx)
{
int idx;

if (len == 0)
! return NULL;
for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
{
lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;

if (STRNCMP(name, lvar->lv_name, len) == 0
&& STRLEN(lvar->lv_name) == len)
! return lvar;
}
! return NULL;
}

/*
***************
*** 217,223 ****
{
if (lookup_script(p, len) == OK
|| (cctx != NULL
! && (lookup_local(p, len, cctx) >= 0
|| find_imported(p, len, cctx) != NULL)))
{
semsg("E1073: imported name already defined: %s", p);
--- 222,228 ----
{
if (lookup_script(p, len) == OK
|| (cctx != NULL
! && (lookup_local(p, len, cctx) != NULL
|| find_imported(p, len, cctx) != NULL)))
{
semsg("E1073: imported name already defined: %s", p);
***************
*** 1458,1490 ****

/*
* Reserve space for a local variable.
! * Return the index or -1 if it failed.
*/
! static int
reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type)
{
- int idx;
lvar_T *lvar;

if (lookup_arg(name, len, cctx) >= 0 || lookup_vararg(name, len, cctx))
{
emsg_namelen(_("E1006: %s is used as an argument"), name, (int)len);
! return -1;
}

if (ga_grow(&cctx->ctx_locals, 1) == FAIL)
! return -1;
! idx = cctx->ctx_locals.ga_len;
! if (cctx->ctx_max_local < idx + 1)
! cctx->ctx_max_local = idx + 1;
! ++cctx->ctx_locals.ga_len;

- lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
lvar->lv_name = vim_strnsave(name, (int)(len == 0 ? STRLEN(name) : len));
lvar->lv_const = isConst;
lvar->lv_type = type;

! return idx;
}

/*
--- 1463,1496 ----

/*
* Reserve space for a local variable.
! * Return the variable or NULL if it failed.
*/
! static lvar_T *
reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type)
{
lvar_T *lvar;

if (lookup_arg(name, len, cctx) >= 0 || lookup_vararg(name, len, cctx))
{
emsg_namelen(_("E1006: %s is used as an argument"), name, (int)len);
! return NULL;
}

if (ga_grow(&cctx->ctx_locals, 1) == FAIL)
! return NULL;
! lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + cctx->ctx_locals.ga_len++;
!
! // Every local variable uses the next entry on the stack. We could re-use
! // the last ones when leaving a scope, but then variables used in a closure
! // might get overwritten. To keep things simple do not re-use stack
! // entries. This is less efficient, but memory is cheap these days.
! lvar->lv_idx = cctx->ctx_locals_count++;

lvar->lv_name = vim_strnsave(name, (int)(len == 0 ? STRLEN(name) : len));
lvar->lv_const = isConst;
lvar->lv_type = type;

! return lvar;
}

/*
***************
*** 1511,1517 ****
* Free all local variables.
*/
static void
! free_local(cctx_T *cctx)
{
unwind_locals(cctx, 0);
ga_clear(&cctx->ctx_locals);
--- 1517,1523 ----
* Free all local variables.
*/
static void
! free_locals(cctx_T *cctx)
{
unwind_locals(cctx, 0);
ga_clear(&cctx->ctx_locals);
***************
*** 2331,2340 ****
}
else
{
! idx = lookup_local(*arg, len, cctx);
! if (idx >= 0)
{
! type = (((lvar_T *)cctx->ctx_locals.ga_data) + idx)->lv_type;
gen_load = TRUE;
}
else
--- 2337,2348 ----
}
else
{
! lvar_T *lvar = lookup_local(*arg, len, cctx);
!
! if (lvar != NULL)
{
! type = lvar->lv_type;
! idx = lvar->lv_idx;
gen_load = TRUE;
}
else
***************
*** 4040,4046 ****
int semicolon = 0;
size_t varlen;
garray_T *instr = &cctx->ctx_instr;
- int idx = -1;
int new_local = FALSE;
char_u *op;
int opt_type;
--- 4048,4053 ----
***************
*** 4050,4056 ****
int oplen = 0;
int heredoc = FALSE;
type_T *type = &t_any;
! lvar_T *lvar;
char_u *name;
char_u *sp;
int has_type = FALSE;
--- 4057,4063 ----
int oplen = 0;
int heredoc = FALSE;
type_T *type = &t_any;
! lvar_T *lvar = NULL;
char_u *name;
char_u *sp;
int has_type = FALSE;
***************
*** 4203,4208 ****
--- 4210,4217 ----
}
else
{
+ int idx;
+
for (idx = 0; reserved[idx] != NULL; ++idx)
if (STRCMP(reserved[idx], name) == 0)
{
***************
*** 4210,4217 ****
goto theend;
}

! idx = lookup_local(arg, varlen, cctx);
! if (idx >= 0)
{
if (is_decl)
{
--- 4219,4226 ----
goto theend;
}

! lvar = lookup_local(arg, varlen, cctx);
! if (lvar != NULL)
{
if (is_decl)
{
***************
*** 4220,4229 ****
}
else
{
- lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
if (lvar->lv_const)
{
! semsg(_("E1018: Cannot assign to a constant: %s"), name);
goto theend;
}
}
--- 4229,4238 ----
}
else
{
if (lvar->lv_const)
{
! semsg(_("E1018: Cannot assign to a constant: %s"),
! name);
goto theend;
}
}
***************
*** 4262,4272 ****
type = parse_type(&p, cctx->ctx_type_list);
has_type = TRUE;
}
! else if (idx >= 0)
! {
! lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
type = lvar->lv_type;
- }
}

sp = p;
--- 4271,4278 ----
type = parse_type(&p, cctx->ctx_type_list);
has_type = TRUE;
}
! else if (lvar != NULL)
type = lvar->lv_type;
}

sp = p;
***************
*** 4288,4294 ****
goto theend;
}

! if (idx < 0 && dest == dest_local && cctx->ctx_skip != TRUE)
{
if (oplen > 1 && !heredoc)
{
--- 4294,4300 ----
goto theend;
}

! if (lvar == NULL && dest == dest_local && cctx->ctx_skip != TRUE)
{
if (oplen > 1 && !heredoc)
{
***************
*** 4301,4308 ****
// new local variable
if (type->tt_type == VAR_FUNC && var_check_func_name(name, TRUE))
goto theend;
! idx = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type);
! if (idx < 0)
goto theend;
new_local = TRUE;
}
--- 4307,4314 ----
// new local variable
if (type->tt_type == VAR_FUNC && var_check_func_name(name, TRUE))
goto theend;
! lvar = reserve_local(cctx, arg, varlen, cmdidx == CMD_const, type);
! if (lvar == NULL)
goto theend;
new_local = TRUE;
}
***************
*** 4370,4376 ****
generate_LOADV(cctx, name + 2, TRUE);
break;
case dest_local:
! generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
break;
}
}
--- 4376,4382 ----
generate_LOADV(cctx, name + 2, TRUE);
break;
case dest_local:
! generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
break;
}
}
***************
*** 4392,4400 ****
stack = &cctx->ctx_type_stack;
stacktype = stack->ga_len == 0 ? &t_void
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
! if (idx >= 0 && (is_decl || !has_type))
{
- lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
if (new_local && !has_type)
{
if (stacktype->tt_type == VAR_VOID)
--- 4398,4405 ----
stack = &cctx->ctx_type_stack;
stacktype = stack->ga_len == 0 ? &t_void
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
! if (lvar != NULL && (is_decl || !has_type))
{
if (new_local && !has_type)
{
if (stacktype->tt_type == VAR_VOID)
***************
*** 4546,4551 ****
--- 4551,4557 ----
char_u *rawname = name + (name[1] == ':' ? 2 : 0);
imported_T *import = NULL;
int sid = current_sctx.sc_sid;
+ int idx;

if (name[1] != ':')
{
***************
*** 4581,4586 ****
--- 4587,4593 ----
}
break;
case dest_local:
+ if (lvar != NULL)
{
isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;

***************
*** 4593,4605 ****
garray_T *stack = &cctx->ctx_type_stack;

isn->isn_type = ISN_STORENR;
! isn->isn_arg.storenr.stnr_idx = idx;
isn->isn_arg.storenr.stnr_val = val;
if (stack->ga_len > 0)
--stack->ga_len;
}
else
! generate_STORE(cctx, ISN_STORE, idx, NULL);
}
break;
}
--- 4600,4612 ----
garray_T *stack = &cctx->ctx_type_stack;

isn->isn_type = ISN_STORENR;
! isn->isn_arg.storenr.stnr_idx = lvar->lv_idx;
isn->isn_arg.storenr.stnr_val = val;
if (stack->ga_len > 0)
--stack->ga_len;
}
else
! generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
}
break;
}
***************
*** 5283,5290 ****
garray_T *instr = &cctx->ctx_instr;
garray_T *stack = &cctx->ctx_type_stack;
scope_T *scope;
! int loop_idx; // index of loop iteration variable
! int var_idx; // index of "var"
type_T *vartype;

// TODO: list of variables: "for [key, value] in dict"
--- 5290,5297 ----
garray_T *instr = &cctx->ctx_instr;
garray_T *stack = &cctx->ctx_type_stack;
scope_T *scope;
! lvar_T *loop_lvar; // loop iteration variable
! lvar_T *var_lvar; // variable for "var"
type_T *vartype;

// TODO: list of variables: "for [key, value] in dict"
***************
*** 5292,5299 ****
for (p = arg; eval_isnamec1(*p); ++p)
;
varlen = p - arg;
! var_idx = lookup_local(arg, varlen, cctx);
! if (var_idx >= 0)
{
semsg(_("E1023: variable already defined: %s"), arg);
return NULL;
--- 5299,5306 ----
for (p = arg; eval_isnamec1(*p); ++p)
;
varlen = p - arg;
! var_lvar = lookup_local(arg, varlen, cctx);
! if (var_lvar != NULL)
{
semsg(_("E1023: variable already defined: %s"), arg);
return NULL;
***************
*** 5314,5336 ****
return NULL;

// Reserve a variable to store the loop iteration counter.
! loop_idx = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
! if (loop_idx < 0)
{
! // only happens when out of memory
drop_scope(cctx);
return NULL;
}

// Reserve a variable to store "var"
! var_idx = reserve_local(cctx, arg, varlen, FALSE, &t_any);
! if (var_idx < 0)
{
drop_scope(cctx);
return NULL;
}

! generate_STORENR(cctx, loop_idx, -1);

// compile "expr", it remains on the stack until "endfor"
arg = p;
--- 5321,5344 ----
return NULL;

// Reserve a variable to store the loop iteration counter.
! loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
! if (loop_lvar == NULL)
{
! // out of memory
drop_scope(cctx);
return NULL;
}

// Reserve a variable to store "var"
! var_lvar = reserve_local(cctx, arg, varlen, FALSE, &t_any);
! if (var_lvar == NULL)
{
+ // out of memory or used as an argument
drop_scope(cctx);
return NULL;
}

! generate_STORENR(cctx, loop_lvar->lv_idx, -1);

// compile "expr", it remains on the stack until "endfor"
arg = p;
***************
*** 5349,5365 ****
return NULL;
}
if (vartype->tt_member->tt_type != VAR_ANY)
! {
! lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + var_idx;
!
! lvar->lv_type = vartype->tt_member;
! }

// "for_end" is set when ":endfor" is found
scope->se_u.se_for.fs_top_label = instr->ga_len;

! generate_FOR(cctx, loop_idx);
! generate_STORE(cctx, ISN_STORE, var_idx, NULL);

return arg;
}
--- 5357,5369 ----
return NULL;
}
if (vartype->tt_member->tt_type != VAR_ANY)
! var_lvar->lv_type = vartype->tt_member;

// "for_end" is set when ":endfor" is found
scope->se_u.se_for.fs_top_label = instr->ga_len;

! generate_FOR(cctx, loop_lvar->lv_idx);
! generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL);

return arg;
}
***************
*** 6158,6164 ****
|| *ea.cmd == '$'
|| *ea.cmd == '@'
|| ((p - ea.cmd) > 2 && ea.cmd[1] == ':')
! || lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0
|| lookup_script(ea.cmd, p - ea.cmd) == OK
|| find_imported(ea.cmd, p - ea.cmd, &cctx) != NULL)
{
--- 6162,6168 ----
|| *ea.cmd == '$'
|| *ea.cmd == '@'
|| ((p - ea.cmd) > 2 && ea.cmd[1] == ':')
! || lookup_local(ea.cmd, p - ea.cmd, &cctx) != NULL
|| lookup_script(ea.cmd, p - ea.cmd) == OK
|| find_imported(ea.cmd, p - ea.cmd, &cctx) != NULL)
{
***************
*** 6175,6181 ****
* COMMAND after range
*/
ea.cmd = skip_range(ea.cmd, NULL);
! p = find_ex_command(&ea, NULL, is_ex_command ? NULL : lookup_local,
&cctx);

if (p == ea.cmd && ea.cmdidx != CMD_SIZE)
--- 6179,6186 ----
* COMMAND after range
*/
ea.cmd = skip_range(ea.cmd, NULL);
! p = find_ex_command(&ea, NULL, is_ex_command ? NULL
! : (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
&cctx);

if (p == ea.cmd && ea.cmdidx != CMD_SIZE)
***************
*** 6349,6355 ****
dfunc->df_deleted = FALSE;
dfunc->df_instr = instr->ga_data;
dfunc->df_instr_count = instr->ga_len;
! dfunc->df_varcount = cctx.ctx_max_local;
}

{
--- 6354,6360 ----
dfunc->df_deleted = FALSE;
dfunc->df_instr = instr->ga_data;
dfunc->df_instr_count = instr->ga_len;
! dfunc->df_varcount = cctx.ctx_locals_count;
}

{
***************
*** 6431,6437 ****

current_sctx = save_current_sctx;
free_imported(&cctx);
! free_local(&cctx);
ga_clear(&cctx.ctx_type_stack);
}

--- 6436,6442 ----

current_sctx = save_current_sctx;
free_imported(&cctx);
! free_locals(&cctx);
ga_clear(&cctx.ctx_type_stack);
}

*** ../vim-8.2.0674/src/ex_docmd.c 2020-04-30 22:29:36.622024155 +0200
--- src/ex_docmd.c 2020-05-01 15:30:01.390841317 +0200
***************
*** 3157,3163 ****
find_ex_command(
exarg_T *eap,
int *full UNUSED,
! int (*lookup)(char_u *, size_t, cctx_T *) UNUSED,
cctx_T *cctx UNUSED)
{
int len;
--- 3157,3163 ----
find_ex_command(
exarg_T *eap,
int *full UNUSED,
! void *(*lookup)(char_u *, size_t, cctx_T *) UNUSED,
cctx_T *cctx UNUSED)
{
int len;
***************
*** 3197,3203 ****
// "g:var = expr"
// "var = expr" where "var" is a local var name.
if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
! || lookup(eap->cmd, p - eap->cmd, cctx) >= 0)
{
eap->cmdidx = CMD_let;
return eap->cmd;
--- 3197,3203 ----
// "g:var = expr"
// "var = expr" where "var" is a local var name.
if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
! || lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
{
eap->cmdidx = CMD_let;
return eap->cmd;
*** ../vim-8.2.0674/src/proto/ex_docmd.pro 2020-04-16 22:10:42.656733692 +0200
--- src/proto/ex_docmd.pro 2020-05-01 15:27:47.867333219 +0200
***************
*** 7,13 ****
int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
int checkforcmd(char_u **pp, char *cmd, int len);
! char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
--- 7,13 ----
int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
int checkforcmd(char_u **pp, char *cmd, int len);
! char_u *find_ex_command(exarg_T *eap, int *full, void *((*lookup)(char_u *, size_t, cctx_T *)), cctx_T *cctx);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
***************
*** 19,25 ****
char_u *skip_cmd_arg(char_u *p, int rembs);
int get_bad_opt(char_u *p, exarg_T *eap);
int ends_excmd(int c);
! int ends_excmd2(char_u *before, char_u *cmd);
char_u *find_nextcmd(char_u *p);
char_u *check_nextcmd(char_u *p);
char_u *get_command_name(expand_T *xp, int idx);
--- 19,25 ----
char_u *skip_cmd_arg(char_u *p, int rembs);
int get_bad_opt(char_u *p, exarg_T *eap);
int ends_excmd(int c);
! int ends_excmd2(char_u *cmd_start, char_u *cmd);
char_u *find_nextcmd(char_u *p);
char_u *check_nextcmd(char_u *p);
char_u *get_command_name(expand_T *xp, int idx);
*** ../vim-8.2.0674/src/evalvars.c 2020-05-01 14:10:10.188390740 +0200
--- src/evalvars.c 2020-05-01 15:25:42.127796319 +0200
***************
*** 2495,2513 ****

/*
* Look for "name[len]" in script-local variables.
! * Return -1 when not found.
*/
! int
lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
{
hashtab_T *ht = get_script_local_ht();
char_u buffer[30];
char_u *p;
! int res;
hashitem_T *hi;

if (ht == NULL)
! return -1;
if (len < sizeof(buffer) - 1)
{
vim_strncpy(buffer, name, len);
--- 2495,2513 ----

/*
* Look for "name[len]" in script-local variables.
! * Return a non-NULL pointer when found, NULL when not found.
*/
! void *
lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
{
hashtab_T *ht = get_script_local_ht();
char_u buffer[30];
char_u *p;
! void *res;
hashitem_T *hi;

if (ht == NULL)
! return NULL;
if (len < sizeof(buffer) - 1)
{
vim_strncpy(buffer, name, len);
***************
*** 2517,2531 ****
{
p = vim_strnsave(name, (int)len);
if (p == NULL)
! return -1;
}

hi = hash_find(ht, p);
! res = HASHITEM_EMPTY(hi) ? -1 : 1;

// if not script-local, then perhaps imported
! if (res == -1 && find_imported(p, 0, NULL) != NULL)
! res = 1;

if (p != buffer)
vim_free(p);
--- 2517,2531 ----
{
p = vim_strnsave(name, (int)len);
if (p == NULL)
! return NULL;
}

hi = hash_find(ht, p);
! res = HASHITEM_EMPTY(hi) ? NULL : hi;

// if not script-local, then perhaps imported
! if (res == NULL && find_imported(p, 0, NULL) != NULL)
! res = p;

if (p != buffer)
vim_free(p);
*** ../vim-8.2.0674/src/proto/evalvars.pro 2020-04-19 16:28:55.296495996 +0200
--- src/proto/evalvars.pro 2020-05-01 15:25:08.939918638 +0200
***************
*** 55,61 ****
void check_vars(char_u *name, int len);
dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
! int lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy);
hashtab_T *find_var_ht(char_u *name, char_u **varname);
char_u *get_var_value(char_u *name);
void new_script_vars(scid_T id);
--- 55,61 ----
void check_vars(char_u *name, int len);
dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
! void *lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy);
hashtab_T *find_var_ht(char_u *name, char_u **varname);
char_u *get_var_value(char_u *name);
void new_script_vars(scid_T id);
*** ../vim-8.2.0674/src/version.c 2020-05-01 14:26:17.132949262 +0200
--- src/version.c 2020-05-01 15:42:04.808364746 +0200
***************
*** 748,749 ****
--- 748,751 ----
{ /* Add new patch number below this line */
+ /**/
+ 675,
/**/

--
hundred-and-one symptoms of being an internet addict:
36. You miss more than five meals a week downloading the latest games from
Apogee.

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages