Patch 8.2.0517

8 views
Skip to first unread message

Bram Moolenaar

unread,
Apr 5, 2020, 3:39:04 PM4/5/20
to vim...@googlegroups.com

Patch 8.2.0517
Problem: Vim9: cannot separate "func" and "func(): void".
Solution: Use VAR_ANY for "any" and VAR_UNKNOWN for "no type".
Files: src/structs.h, src/globals.h, src/eval.c, src/evalfunc.c,
src/evalvars.c, src/testing.c, src/vim9compile.c,
src/vim9execute.c, src/viminfo.c, src/if_py_both.h, src/json.c,
src/testdir/test_vim9_func.vim


*** ../vim-8.2.0516/src/structs.h 2020-04-05 17:07:59.414556253 +0200
--- src/structs.h 2020-04-05 20:52:39.912621416 +0200
***************
*** 1321,1328 ****

typedef enum
{
! VAR_UNKNOWN = 0, // not set, also used for "any" type
! VAR_VOID, // no value
VAR_BOOL, // "v_number" is used: VVAL_TRUE or VVAL_FALSE
VAR_SPECIAL, // "v_number" is used: VVAL_NULL or VVAL_NONE
VAR_NUMBER, // "v_number" is used
--- 1321,1329 ----

typedef enum
{
! VAR_UNKNOWN = 0, // not set, any type or "void" allowed
! VAR_ANY, // used for "any" type
! VAR_VOID, // no value (function not returning anything)
VAR_BOOL, // "v_number" is used: VVAL_TRUE or VVAL_FALSE
VAR_SPECIAL, // "v_number" is used: VVAL_NULL or VVAL_NONE
VAR_NUMBER, // "v_number" is used
*** ../vim-8.2.0516/src/globals.h 2020-04-05 17:07:59.414556253 +0200
--- src/globals.h 2020-04-05 20:56:05.731770278 +0200
***************
*** 379,385 ****


// Commonly used types.
! EXTERN type_T t_any INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL);
EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL);
EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL);
--- 379,386 ----


// Commonly used types.
! EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, 0, NULL, NULL);
! EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, 0, NULL, NULL);
EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, 0, NULL, NULL);
EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, 0, NULL, NULL);
EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, 0, NULL, NULL);
***************
*** 390,395 ****
--- 391,397 ----
EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, 0, NULL, NULL);
EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, 0, NULL, NULL);

+ EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, 0, &t_unknown, NULL);
EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, 0, &t_void, NULL);
EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, 0, &t_any, NULL);
EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, 0, &t_number, NULL);
***************
*** 401,408 ****

EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL);
! EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_void, NULL);
! EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_void, NULL);

EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL);
EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL);
--- 403,410 ----

EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, 0, &t_any, NULL);
EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, 0, &t_any, NULL);
! EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, 0, &t_unknown, NULL);
! EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, 0, &t_unknown, NULL);

EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, 0, &t_bool, NULL);
EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, 0, &t_number, NULL);
*** ../vim-8.2.0516/src/eval.c 2020-04-02 18:50:42.419773128 +0200
--- src/eval.c 2020-04-05 21:04:58.161555942 +0200
***************
*** 1272,1277 ****
--- 1272,1278 ----
switch (tv1->v_type)
{
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_DICT:
case VAR_FUNC:
***************
*** 2967,2972 ****
--- 2968,2974 ----
emsg(_("E909: Cannot index a special variable"));
return FAIL;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
if (evaluate)
return FAIL;
***************
*** 3073,3078 ****
--- 3075,3081 ----
switch (rettv->v_type)
{
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_FUNC:
case VAR_PARTIAL:
***************
*** 3668,3674 ****
}

/*
! * Return the function name of the partial.
*/
char_u *
partial_name(partial_T *pt)
--- 3671,3677 ----
}

/*
! * Return the function name of partial "pt".
*/
char_u *
partial_name(partial_T *pt)
***************
*** 3856,3861 ****
--- 3859,3865 ----
return tv1->vval.v_string == tv2->vval.v_string;

case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
break;
}
***************
*** 4570,4575 ****
--- 4574,4580 ----

case VAR_NUMBER:
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
*tofree = NULL;
r = tv_get_string_buf(tv, numbuf);
***************
*** 5422,5427 ****
--- 5427,5433 ----
#endif
case VAR_NUMBER:
case VAR_FLOAT:
+ case VAR_ANY:
case VAR_UNKNOWN:
case VAR_VOID:
case VAR_BOOL:
***************
*** 5486,5491 ****
--- 5492,5498 ----
varp->vval.v_channel = NULL;
#endif
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
break;
}
***************
*** 5565,5570 ****
--- 5572,5578 ----
emsg(_("E974: Using a Blob as a Number"));
break;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("tv_get_number(UNKNOWN)");
break;
***************
*** 5619,5624 ****
--- 5627,5633 ----
emsg(_("E975: Using a Blob as a Float"));
break;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("tv_get_float(UNKNOWN)");
break;
***************
*** 5742,5747 ****
--- 5751,5757 ----
#endif
break;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
emsg(_(e_inval_string));
break;
***************
*** 5891,5896 ****
--- 5901,5907 ----
}
break;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("copy_tv(UNKNOWN)");
break;
***************
*** 5970,5975 ****
--- 5981,5987 ----
ret = FAIL;
break;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("item_copy(UNKNOWN)");
ret = FAIL;
*** ../vim-8.2.0516/src/evalfunc.c 2020-04-05 20:20:40.104596563 +0200
--- src/evalfunc.c 2020-04-05 20:59:04.499028317 +0200
***************
*** 2042,2047 ****
--- 2042,2048 ----
break;
#endif
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("f_empty(UNKNOWN)");
n = TRUE;
***************
*** 5217,5222 ****
--- 5218,5224 ----
rettv->vval.v_number = dict_len(argvars[0].vval.v_dict);
break;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_BOOL:
case VAR_SPECIAL:
***************
*** 8805,8810 ****
--- 8807,8813 ----
case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break;
case VAR_BLOB: n = VAR_TYPE_BLOB; break;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("f_type(UNKNOWN)");
n = -1;
*** ../vim-8.2.0516/src/evalvars.c 2020-04-05 18:56:02.233436590 +0200
--- src/evalvars.c 2020-04-05 20:59:11.638998638 +0200
***************
*** 1668,1673 ****
--- 1668,1674 ----
switch (tv->v_type)
{
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_NUMBER:
case VAR_BOOL:
*** ../vim-8.2.0516/src/testing.c 2020-04-01 22:10:56.428201257 +0200
--- src/testing.c 2020-04-05 20:59:29.034926341 +0200
***************
*** 764,769 ****
--- 764,770 ----
switch (argvars[0].v_type)
{
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_NUMBER:
case VAR_BOOL:
*** ../vim-8.2.0516/src/vim9compile.c 2020-04-05 19:09:02.134503484 +0200
--- src/vim9compile.c 2020-04-05 21:29:13.304105938 +0200
***************
*** 249,257 ****
type_T *type;

// recognize commonly used types
! if (member_type->tt_type == VAR_UNKNOWN)
return &t_list_any;
! if (member_type->tt_type == VAR_VOID)
return &t_list_empty;
if (member_type->tt_type == VAR_BOOL)
return &t_list_bool;
--- 249,258 ----
type_T *type;

// recognize commonly used types
! if (member_type->tt_type == VAR_ANY)
return &t_list_any;
! if (member_type->tt_type == VAR_VOID
! || member_type->tt_type == VAR_UNKNOWN)
return &t_list_empty;
if (member_type->tt_type == VAR_BOOL)
return &t_list_bool;
***************
*** 277,285 ****
type_T *type;

// recognize commonly used types
! if (member_type->tt_type == VAR_UNKNOWN)
return &t_dict_any;
! if (member_type->tt_type == VAR_VOID)
return &t_dict_empty;
if (member_type->tt_type == VAR_BOOL)
return &t_dict_bool;
--- 278,287 ----
type_T *type;

// recognize commonly used types
! if (member_type->tt_type == VAR_ANY)
return &t_dict_any;
! if (member_type->tt_type == VAR_VOID
! || member_type->tt_type == VAR_UNKNOWN)
return &t_dict_empty;
if (member_type->tt_type == VAR_BOOL)
return &t_dict_bool;
***************
*** 482,490 ****
static int
check_number_or_float(vartype_T type1, vartype_T type2, char_u *op)
{
! if (!((type1 == VAR_NUMBER || type1 == VAR_FLOAT || type1 == VAR_UNKNOWN)
&& (type2 == VAR_NUMBER || type2 == VAR_FLOAT
! || type2 == VAR_UNKNOWN)))
{
if (*op == '+')
emsg(_("E1035: wrong argument type for +"));
--- 484,492 ----
static int
check_number_or_float(vartype_T type1, vartype_T type2, char_u *op)
{
! if (!((type1 == VAR_NUMBER || type1 == VAR_FLOAT || type1 == VAR_ANY)
&& (type2 == VAR_NUMBER || type2 == VAR_FLOAT
! || type2 == VAR_ANY)))
{
if (*op == '+')
emsg(_("E1035: wrong argument type for +"));
***************
*** 515,521 ****
// checking.
type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2];
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
! vartype = VAR_UNKNOWN;
if (type1->tt_type == type2->tt_type
&& (type1->tt_type == VAR_NUMBER
|| type1->tt_type == VAR_LIST
--- 517,523 ----
// checking.
type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2];
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
! vartype = VAR_ANY;
if (type1->tt_type == type2->tt_type
&& (type1->tt_type == VAR_NUMBER
|| type1->tt_type == VAR_LIST
***************
*** 528,535 ****
switch (*op)
{
case '+': if (vartype != VAR_LIST && vartype != VAR_BLOB
! && type1->tt_type != VAR_UNKNOWN
! && type2->tt_type != VAR_UNKNOWN
&& check_number_or_float(
type1->tt_type, type2->tt_type, op) == FAIL)
return FAIL;
--- 530,537 ----
switch (*op)
{
case '+': if (vartype != VAR_LIST && vartype != VAR_BLOB
! && type1->tt_type != VAR_ANY
! && type2->tt_type != VAR_ANY
&& check_number_or_float(
type1->tt_type, type2->tt_type, op) == FAIL)
return FAIL;
***************
*** 563,571 ****
? EXPR_MULT : *op == '/'? EXPR_DIV : EXPR_SUB;
break;

! case '%': if ((type1->tt_type != VAR_UNKNOWN
&& type1->tt_type != VAR_NUMBER)
! || (type2->tt_type != VAR_UNKNOWN
&& type2->tt_type != VAR_NUMBER))
{
emsg(_("E1035: % requires number arguments"));
--- 565,573 ----
? EXPR_MULT : *op == '/'? EXPR_DIV : EXPR_SUB;
break;

! case '%': if ((type1->tt_type != VAR_ANY
&& type1->tt_type != VAR_NUMBER)
! || (type2->tt_type != VAR_ANY
&& type2->tt_type != VAR_NUMBER))
{
emsg(_("E1035: % requires number arguments"));
***************
*** 579,585 ****
}

// correct type of result
! if (vartype == VAR_UNKNOWN)
{
type_T *type = &t_any;

--- 581,587 ----
}

// correct type of result
! if (vartype == VAR_ANY)
{
type_T *type = &t_any;

***************
*** 614,619 ****
--- 616,626 ----
// checking.
type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
+ if (type1 == VAR_UNKNOWN)
+ type1 = VAR_ANY;
+ if (type2 == VAR_UNKNOWN)
+ type2 = VAR_ANY;
+
if (type1 == type2)
{
switch (type1)
***************
*** 631,637 ****
default: isntype = ISN_COMPAREANY; break;
}
}
! else if (type1 == VAR_UNKNOWN || type2 == VAR_UNKNOWN
|| ((type1 == VAR_NUMBER || type1 == VAR_FLOAT)
&& (type2 == VAR_NUMBER || type2 ==VAR_FLOAT)))
isntype = ISN_COMPAREANY;
--- 638,644 ----
default: isntype = ISN_COMPAREANY; break;
}
}
! else if (type1 == VAR_ANY || type2 == VAR_ANY
|| ((type1 == VAR_NUMBER || type1 == VAR_FLOAT)
&& (type2 == VAR_NUMBER || type2 ==VAR_FLOAT)))
isntype = ISN_COMPAREANY;
***************
*** 1723,1730 ****
return FALSE;
switch (type1->tt_type)
{
- case VAR_VOID:
case VAR_UNKNOWN:
case VAR_SPECIAL:
case VAR_BOOL:
case VAR_NUMBER:
--- 1730,1738 ----
return FALSE;
switch (type1->tt_type)
{
case VAR_UNKNOWN:
+ case VAR_ANY:
+ case VAR_VOID:
case VAR_SPECIAL:
case VAR_BOOL:
case VAR_NUMBER:
***************
*** 1785,1790 ****
--- 1793,1799 ----
switch (type)
{
case VAR_UNKNOWN: break;
+ case VAR_ANY: return "any";
case VAR_VOID: return "void";
case VAR_SPECIAL: return "special";
case VAR_BOOL: return "bool";
***************
*** 1799,1805 ****
case VAR_FUNC: return "func";
case VAR_PARTIAL: return "partial";
}
! return "any";
}

/*
--- 1808,1814 ----
case VAR_FUNC: return "func";
case VAR_PARTIAL: return "partial";
}
! return "unknown";
}

/*
***************
*** 2396,2402 ****
{
int ret = OK;

! if (expected->tt_type != VAR_UNKNOWN)
{
if (expected->tt_type != actual->tt_type)
{
--- 2405,2411 ----
{
int ret = OK;

! if (expected->tt_type != VAR_UNKNOWN && expected->tt_type != VAR_ANY)
{
if (expected->tt_type != actual->tt_type)
{
***************
*** 2406,2418 ****
}
if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST)
{
! // void is used for an empty list or dict
! if (actual->tt_member != &t_void)
ret = check_type(expected->tt_member, actual->tt_member, FALSE);
}
else if (expected->tt_type == VAR_FUNC)
{
! if (expected->tt_member != &t_any)
ret = check_type(expected->tt_member, actual->tt_member, FALSE);
if (ret == OK && expected->tt_argcount != -1
&& (actual->tt_argcount < expected->tt_min_argcount
--- 2415,2428 ----
}
if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST)
{
! // "unknown" is used for an empty list or dict
! if (actual->tt_member != &t_unknown)
ret = check_type(expected->tt_member, actual->tt_member, FALSE);
}
else if (expected->tt_type == VAR_FUNC)
{
! if (expected->tt_member != &t_any
! && expected->tt_member != &t_unknown)
ret = check_type(expected->tt_member, actual->tt_member, FALSE);
if (ret == OK && expected->tt_argcount != -1
&& (actual->tt_argcount < expected->tt_min_argcount
***************
*** 2436,2442 ****
{
if (check_type(expected, actual, FALSE))
return OK;
! if (actual->tt_type != VAR_UNKNOWN)
{
type_mismatch(expected, actual);
return FAIL;
--- 2446,2452 ----
{
if (check_type(expected, actual, FALSE))
return OK;
! if (actual->tt_type != VAR_ANY && actual->tt_type != VAR_UNKNOWN)
{
type_mismatch(expected, actual);
return FAIL;
***************
*** 3642,3648 ****
{
// "set_return_type" cannot be TRUE, only used for a lambda which
// always has an argument.
! if (cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_VOID)
{
emsg(_("E1003: Missing return value"));
return NULL;
--- 3652,3659 ----
{
// "set_return_type" cannot be TRUE, only used for a lambda which
// always has an argument.
! if (cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_VOID
! && cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_UNKNOWN)
{
emsg(_("E1003: Missing return value"));
return NULL;
***************
*** 3936,3942 ****
}

if (oplen == 3 && !heredoc && dest != dest_global
! && type->tt_type != VAR_STRING && type->tt_type != VAR_UNKNOWN)
{
emsg(_("E1019: Can only concatenate to string"));
goto theend;
--- 3947,3953 ----
}

if (oplen == 3 && !heredoc && dest != dest_global
! && type->tt_type != VAR_STRING && type->tt_type != VAR_ANY)
{
emsg(_("E1019: Can only concatenate to string"));
goto theend;
***************
*** 4115,4120 ****
--- 4126,4132 ----
break;
case VAR_NUMBER:
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_SPECIAL: // cannot happen
generate_PUSHNR(cctx, 0);
***************
*** 4903,4909 ****
drop_scope(cctx);
return NULL;
}
! if (vartype->tt_member->tt_type != VAR_UNKNOWN)
{
lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + var_idx;

--- 4915,4921 ----
drop_scope(cctx);
return NULL;
}
! if (vartype->tt_member->tt_type != VAR_ANY)
{
lvar_T *lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + var_idx;

*** ../vim-8.2.0516/src/vim9execute.c 2020-04-05 17:07:59.414556253 +0200
--- src/vim9execute.c 2020-04-05 21:02:17.074227176 +0200
***************
*** 2239,2244 ****
--- 2239,2245 ----
case VAR_BLOB:
return tv->vval.v_blob != NULL && tv->vval.v_blob->bv_ga.ga_len > 0;
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
break;
}
*** ../vim-8.2.0516/src/viminfo.c 2020-03-23 22:12:15.496961030 +0100
--- src/viminfo.c 2020-04-05 21:02:33.234159874 +0200
***************
*** 1344,1349 ****
--- 1344,1350 ----
case VAR_SPECIAL: s = "XPL"; break;

case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_FUNC:
case VAR_PARTIAL:
*** ../vim-8.2.0516/src/if_py_both.h 2020-04-02 18:50:42.423773112 +0200
--- src/if_py_both.h 2020-04-05 21:02:40.294130478 +0200
***************
*** 6390,6395 ****
--- 6390,6396 ----
(char*) tv->vval.v_blob->bv_ga.ga_data,
(Py_ssize_t) tv->vval.v_blob->bv_ga.ga_len);
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
case VAR_CHANNEL:
case VAR_JOB:
*** ../vim-8.2.0516/src/json.c 2020-02-29 17:38:08.866485874 +0100
--- src/json.c 2020-04-05 21:03:09.222009967 +0200
***************
*** 351,356 ****
--- 351,357 ----
break;
#endif
case VAR_UNKNOWN:
+ case VAR_ANY:
case VAR_VOID:
internal_error_no_abort("json_encode_item()");
return FAIL;
*** ../vim-8.2.0516/src/testdir/test_vim9_func.vim 2020-04-05 17:07:59.418556237 +0200
--- src/testdir/test_vim9_func.vim 2020-04-05 21:33:50.235007998 +0200
***************
*** 386,391 ****
--- 386,412 ----
Ref1 = FuncNoArgNoRet
Ref1()
assert_equal(11, funcResult)
+
+ let Ref2: func
+ funcResult = 0
+ Ref2 = FuncNoArgNoRet
+ Ref2()
+ assert_equal(11, funcResult)
+
+ funcResult = 0
+ Ref2 = FuncOneArgNoRet
+ Ref2(12)
+ assert_equal(12, funcResult)
+
+ funcResult = 0
+ Ref2 = FuncNoArgRetNumber
+ assert_equal(1234, Ref2())
+ assert_equal(22, funcResult)
+
+ funcResult = 0
+ Ref2 = FuncOneArgRetNumber
+ assert_equal(13, Ref2(13))
+ assert_equal(13, funcResult)
enddef

def Test_func_type_fails()
*** ../vim-8.2.0516/src/version.c 2020-04-05 20:20:40.104596563 +0200
--- src/version.c 2020-04-05 21:34:15.006909207 +0200
***************
*** 740,741 ****
--- 740,743 ----
{ /* Add new patch number below this line */
+ /**/
+ 517,
/**/

--
The Law, in its majestic equality, forbids the rich, as well as the
poor, to sleep under the bridges, to beg in the streets, and to steal
bread. -- Anatole France

/// 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