diff --git a/7.4.152 b/7.4.152 new file mode 100644 index 0000000..6e54ddb --- /dev/null +++ b/7.4.152 @@ -0,0 +1,708 @@ +To: vim_dev@googlegroups.com +Subject: Patch 7.4.152 +Fcc: outbox +From: Bram Moolenaar +Mime-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +------------ + +Patch 7.4.152 +Problem: Python: Cannot iterate over options. +Solution: Add options iterator. (ZyX) +Files: src/if_py_both.h, src/option.c, src/proto/option.pro, + src/testdir/test86.in, src/testdir/test86.ok, + src/testdir/test87.in, src/testdir/test87.ok, src/vim.h + + +*** ../vim-7.4.151/src/if_py_both.h 2014-01-14 16:36:40.000000000 +0100 +--- src/if_py_both.h 2014-01-14 16:51:30.000000000 +0100 +*************** +*** 2949,2958 **** + typedef struct + { + PyObject_HEAD +! int opt_type; +! void *from; +! checkfun Check; +! PyObject *fromObj; + } OptionsObject; + + static int +--- 2949,2958 ---- + typedef struct + { + PyObject_HEAD +! int opt_type; +! void *from; +! checkfun Check; +! PyObject *fromObj; + } OptionsObject; + + static int +*************** +*** 3072,3077 **** +--- 3072,3140 ---- + } + + static int ++ OptionsContains(OptionsObject *self, PyObject *keyObject) ++ { ++ char_u *key; ++ PyObject *todecref; ++ ++ if (!(key = StringToChars(keyObject, &todecref))) ++ return -1; ++ ++ if (*key == NUL) ++ { ++ Py_XDECREF(todecref); ++ return 0; ++ } ++ ++ if (get_option_value_strict(key, NULL, NULL, self->opt_type, NULL)) ++ { ++ Py_XDECREF(todecref); ++ return 1; ++ } ++ else ++ { ++ Py_XDECREF(todecref); ++ return 0; ++ } ++ } ++ ++ typedef struct ++ { ++ void *lastoption; ++ int opt_type; ++ } optiterinfo_T; ++ ++ static PyObject * ++ OptionsIterNext(optiterinfo_T **oii) ++ { ++ char_u *name; ++ ++ if ((name = option_iter_next(&((*oii)->lastoption), (*oii)->opt_type))) ++ return PyString_FromString((char *)name); ++ ++ return NULL; ++ } ++ ++ static PyObject * ++ OptionsIter(OptionsObject *self) ++ { ++ optiterinfo_T *oii; ++ ++ if (!(oii = PyMem_New(optiterinfo_T, 1))) ++ { ++ PyErr_NoMemory(); ++ return NULL; ++ } ++ ++ oii->opt_type = self->opt_type; ++ oii->lastoption = NULL; ++ ++ return IterNew(oii, ++ (destructorfun) PyMem_Free, (nextfun) OptionsIterNext, ++ NULL, NULL); ++ } ++ ++ static int + set_option_value_err(char_u *key, int numval, char_u *stringval, int opt_flags) + { + char_u *errmsg; +*************** +*** 3231,3236 **** +--- 3294,3312 ---- + return ret; + } + ++ static PySequenceMethods OptionsAsSeq = { ++ 0, /* sq_length */ ++ 0, /* sq_concat */ ++ 0, /* sq_repeat */ ++ 0, /* sq_item */ ++ 0, /* sq_slice */ ++ 0, /* sq_ass_item */ ++ 0, /* sq_ass_slice */ ++ (objobjproc) OptionsContains, /* sq_contains */ ++ 0, /* sq_inplace_concat */ ++ 0, /* sq_inplace_repeat */ ++ }; ++ + static PyMappingMethods OptionsAsMapping = { + (lenfunc) NULL, + (binaryfunc) OptionsItem, +*************** +*** 6121,6128 **** +--- 6197,6206 ---- + vim_memset(&OptionsType, 0, sizeof(OptionsType)); + OptionsType.tp_name = "vim.options"; + OptionsType.tp_basicsize = sizeof(OptionsObject); ++ OptionsType.tp_as_sequence = &OptionsAsSeq; + OptionsType.tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC; + OptionsType.tp_doc = "object for manipulating options"; ++ OptionsType.tp_iter = (getiterfunc)OptionsIter; + OptionsType.tp_as_mapping = &OptionsAsMapping; + OptionsType.tp_dealloc = (destructor)OptionsDestructor; + OptionsType.tp_traverse = (traverseproc)OptionsTraverse; +*** ../vim-7.4.151/src/option.c 2013-11-12 04:43:57.000000000 +0100 +--- src/option.c 2014-01-14 16:50:52.000000000 +0100 +*************** +*** 8861,8867 **** + } + #endif + +! #if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) + /* + * Returns the option attributes and its value. Unlike the above function it + * will return either global value or local value of the option depending on +--- 8861,8867 ---- + } + #endif + +! #if defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) || defined(PROTO) + /* + * Returns the option attributes and its value. Unlike the above function it + * will return either global value or local value of the option depending on +*************** +*** 8874,8880 **** + * opt_type). Uses + * + * Returned flags: +! * 0 hidden or unknown option + * see SOPT_* in vim.h for other flags + * + * Possible opt_type values: see SREQ_* in vim.h +--- 8874,8881 ---- + * opt_type). Uses + * + * Returned flags: +! * 0 hidden or unknown option, also option that does not have requested +! * type (see SREQ_* in vim.h) + * see SOPT_* in vim.h for other flags + * + * Possible opt_type values: see SREQ_* in vim.h +*************** +*** 8997,9002 **** +--- 8998,9065 ---- + + return r; + } ++ ++ /* ++ * Iterate over options. First argument is a pointer to a pointer to a structure ++ * inside options[] array, second is option type like in the above function. ++ * ++ * If first argument points to NULL it is assumed that iteration just started ++ * and caller needs the very first value. ++ * If first argument points to the end marker function returns NULL and sets ++ * first argument to NULL. ++ * ++ * Returns full option name for current option on each call. ++ */ ++ char_u * ++ option_iter_next(option, opt_type) ++ void **option; ++ int opt_type; ++ { ++ struct vimoption *ret = NULL; ++ do ++ { ++ if (*option == NULL) ++ *option = (void *) options; ++ else if (((struct vimoption *) (*option))->fullname == NULL) ++ { ++ *option = NULL; ++ return NULL; ++ } ++ else ++ *option = (void *) (((struct vimoption *) (*option)) + 1); ++ ++ ret = ((struct vimoption *) (*option)); ++ ++ /* Hidden option */ ++ if (ret->var == NULL) ++ { ++ ret = NULL; ++ continue; ++ } ++ ++ switch (opt_type) ++ { ++ case SREQ_GLOBAL: ++ if (!(ret->indir == PV_NONE || ret->indir & PV_BOTH)) ++ ret = NULL; ++ break; ++ case SREQ_BUF: ++ if (!(ret->indir & PV_BUF)) ++ ret = NULL; ++ break; ++ case SREQ_WIN: ++ if (!(ret->indir & PV_WIN)) ++ ret = NULL; ++ break; ++ default: ++ EMSG2(_(e_intern2), "option_iter_next()"); ++ return NULL; ++ } ++ } ++ while (ret == NULL); ++ ++ return (char_u *)ret->fullname; ++ } + #endif + + /* +*** ../vim-7.4.151/src/proto/option.pro 2013-11-05 07:12:59.000000000 +0100 +--- src/proto/option.pro 2014-01-14 16:51:41.000000000 +0100 +*************** +*** 23,28 **** +--- 23,29 ---- + char_u *check_stl_option __ARGS((char_u *s)); + int get_option_value __ARGS((char_u *name, long *numval, char_u **stringval, int opt_flags)); + int get_option_value_strict __ARGS((char_u *name, long *numval, char_u **stringval, int opt_type, void *from)); ++ char_u *option_iter_next __ARGS((void **option, int opt_type)); + char_u *set_option_value __ARGS((char_u *name, long number, char_u *string, int opt_flags)); + char_u *get_term_code __ARGS((char_u *tname)); + char_u *get_highlight_default __ARGS((void)); +*** ../vim-7.4.151/src/testdir/test86.in 2014-01-14 16:36:40.000000000 +0100 +--- src/testdir/test86.in 2014-01-14 16:49:10.000000000 +0100 +*************** +*** 506,511 **** +--- 506,516 ---- + :py bopts1=vim.buffers[vim.bindeval("g:bufs")[2]].options + :py bopts2=vim.buffers[vim.bindeval("g:bufs")[1]].options + :py bopts3=vim.buffers[vim.bindeval("g:bufs")[0]].options ++ :$put ='wopts iters equal: '.pyeval('list(wopts1) == list(wopts2)') ++ :$put ='bopts iters equal: '.pyeval('list(bopts1) == list(bopts2)') ++ :py gset=set(iter(gopts1)) ++ :py wset=set(iter(wopts1)) ++ :py bset=set(iter(bopts1)) + :set path=.,..,, + :let lst=[] + :let lst+=[['paste', 1, 0, 1, 2, 1, 1, 0 ]] +*************** +*** 536,541 **** +--- 541,548 ---- + : py oval3=bool(oval3) + : endif + : put ='>>> '.oname ++ : $put =' g/w/b:'.pyeval('oname in gset').'/'.pyeval('oname in wset').'/'.pyeval('oname in bset') ++ : $put =' g/w/b (in):'.pyeval('oname in gopts1').'/'.pyeval('oname in wopts1').'/'.pyeval('oname in bopts1') + : for v in ['gopts1', 'wopts1', 'bopts1'] + : try + : put =' p/'.v.': '.Ev('repr('.v.'['''.oname.'''])') +*************** +*** 1122,1127 **** +--- 1129,1141 ---- + ee('import failing') + vim.options['rtp'] = old_rtp + del old_rtp ++ cb.append("> Options") ++ cb.append(">> OptionsItem") ++ ee('vim.options["abcQ"]') ++ ee('vim.options[""]') ++ stringtochars_test('vim.options[%s]') ++ cb.append(">> OptionsContains") ++ stringtochars_test('%s in vim.options') + cb.append("> Dictionary") + cb.append(">> DictionaryConstructor") + ee('vim.Dictionary("abcI")') +*** ../vim-7.4.151/src/testdir/test86.ok 2014-01-14 16:36:40.000000000 +0100 +--- src/testdir/test86.ok 2014-01-14 16:49:10.000000000 +0100 +*************** +*** 112,118 **** +--- 112,122 ---- + def + bar + jkl ++ wopts iters equal: 1 ++ bopts iters equal: 1 + >>> paste ++ g/w/b:1/0/0 ++ g/w/b (in):1/0/0 + p/gopts1: False + p/wopts1! KeyError + inv: 2! KeyError +*************** +*** 133,138 **** +--- 137,144 ---- + W: 1:1 2:1 3:1 4:1 + B: 1:1 2:1 3:1 4:1 + >>> previewheight ++ g/w/b:1/0/0 ++ g/w/b (in):1/0/0 + p/gopts1: 12 + inv: 'a'! TypeError + p/wopts1! KeyError +*************** +*** 154,159 **** +--- 160,167 ---- + W: 1:5 2:5 3:5 4:5 + B: 1:5 2:5 3:5 4:5 + >>> operatorfunc ++ g/w/b:1/0/0 ++ g/w/b (in):1/0/0 + p/gopts1: '' + inv: 2! TypeError + p/wopts1! KeyError +*************** +*** 175,180 **** +--- 183,190 ---- + W: 1:'A' 2:'A' 3:'A' 4:'A' + B: 1:'A' 2:'A' 3:'A' 4:'A' + >>> number ++ g/w/b:0/1/0 ++ g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 0! KeyError + gopts1! KeyError +*************** +*** 193,198 **** +--- 203,210 ---- + W: 1:1 2:1 3:0 4:0 + B: 1:1 2:1 3:0 4:0 + >>> numberwidth ++ g/w/b:0/1/0 ++ g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: -100! KeyError + gopts1! KeyError +*************** +*** 212,217 **** +--- 224,231 ---- + W: 1:3 2:5 3:2 4:8 + B: 1:3 2:5 3:2 4:8 + >>> colorcolumn ++ g/w/b:0/1/0 ++ g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 'abc4'! KeyError + gopts1! KeyError +*************** +*** 231,236 **** +--- 245,252 ---- + W: 1:'+2' 2:'+3' 3:'+1' 4:'' + B: 1:'+2' 2:'+3' 3:'+1' 4:'' + >>> statusline ++ g/w/b:1/1/0 ++ g/w/b (in):1/1/0 + p/gopts1: '' + inv: 0! TypeError + p/wopts1: None +*************** +*** 248,253 **** +--- 264,271 ---- + W: 1:'2' 2:'1' 3:'1' 4:'1' + B: 1:'2' 2:'1' 3:'1' 4:'1' + >>> autoindent ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError +*************** +*** 266,271 **** +--- 284,291 ---- + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> shiftwidth ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 3! KeyError + gopts1! KeyError +*************** +*** 284,289 **** +--- 304,311 ---- + W: 1:0 2:2 3:8 4:1 + B: 1:0 2:2 3:8 4:1 + >>> omnifunc ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 1! KeyError + gopts1! KeyError +*************** +*** 303,308 **** +--- 325,332 ---- + W: 1:'A' 2:'B' 3:'' 4:'C' + B: 1:'A' 2:'B' 3:'' 4:'C' + >>> preserveindent ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError +*************** +*** 321,326 **** +--- 345,352 ---- + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> path ++ g/w/b:1/0/1 ++ g/w/b (in):1/0/1 + p/gopts1: '.,..,,' + inv: 0! TypeError + p/wopts1! KeyError +*************** +*** 509,514 **** +--- 535,555 ---- + import xxx_no_such_module_xxx:ImportError:('No module named xxx_no_such_module_xxx',) + import failing_import:ImportError:('No module named failing_import',) + import failing:NotImplementedError:() ++ > Options ++ >> OptionsItem ++ vim.options["abcQ"]:KeyError:('abcQ',) ++ vim.options[""]:ValueError:('empty keys are not allowed',) ++ >>> Testing StringToChars using vim.options[%s] ++ vim.options[1]:TypeError:('expected str() or unicode() instance, but got int',) ++ vim.options[u"\0"]:TypeError:('expected string without null bytes',) ++ vim.options["\0"]:TypeError:('expected string without null bytes',) ++ <<< Finished ++ >> OptionsContains ++ >>> Testing StringToChars using %s in vim.options ++ 1 in vim.options:TypeError:('expected str() or unicode() instance, but got int',) ++ u"\0" in vim.options:TypeError:('expected string without null bytes',) ++ "\0" in vim.options:TypeError:('expected string without null bytes',) ++ <<< Finished + > Dictionary + >> DictionaryConstructor + vim.Dictionary("abcI"):ValueError:('expected sequence element of size 2, but got sequence of size 1',) +*** ../vim-7.4.151/src/testdir/test87.in 2014-01-14 16:36:40.000000000 +0100 +--- src/testdir/test87.in 2014-01-14 16:49:10.000000000 +0100 +*************** +*** 503,508 **** +--- 503,513 ---- + :py3 bopts1=vim.buffers[vim.bindeval("g:bufs")[2]].options + :py3 bopts2=vim.buffers[vim.bindeval("g:bufs")[1]].options + :py3 bopts3=vim.buffers[vim.bindeval("g:bufs")[0]].options ++ :$put ='wopts iters equal: '.py3eval('list(wopts1) == list(wopts2)') ++ :$put ='bopts iters equal: '.py3eval('list(bopts1) == list(bopts2)') ++ :py3 gset=set(iter(gopts1)) ++ :py3 wset=set(iter(wopts1)) ++ :py3 bset=set(iter(bopts1)) + :set path=.,..,, + :let lst=[] + :let lst+=[['paste', 1, 0, 1, 2, 1, 1, 0 ]] +*************** +*** 533,538 **** +--- 538,545 ---- + : py3 oval3=bool(oval3) + : endif + : put ='>>> '.oname ++ : $put =' g/w/b:'.py3eval('oname in gset').'/'.py3eval('oname in wset').'/'.py3eval('oname in bset') ++ : $put =' g/w/b (in):'.py3eval('oname in gopts1').'/'.py3eval('oname in wopts1').'/'.py3eval('oname in bopts1') + : for v in ['gopts1', 'wopts1', 'bopts1'] + : try + : put =' p/'.v.': '.Ev('repr('.v.'['''.oname.'''])') +*************** +*** 1099,1104 **** +--- 1106,1118 ---- + ee('import failing') + vim.options['rtp'] = old_rtp + del old_rtp ++ cb.append("> Options") ++ cb.append(">> OptionsItem") ++ ee('vim.options["abcQ"]') ++ ee('vim.options[""]') ++ stringtochars_test('vim.options[%s]') ++ cb.append(">> OptionsContains") ++ stringtochars_test('%s in vim.options') + cb.append("> Dictionary") + cb.append(">> DictionaryConstructor") + ee('vim.Dictionary("abcI")') +*** ../vim-7.4.151/src/testdir/test87.ok 2014-01-14 16:36:40.000000000 +0100 +--- src/testdir/test87.ok 2014-01-14 16:49:10.000000000 +0100 +*************** +*** 112,118 **** +--- 112,122 ---- + def + bar + jkl ++ wopts iters equal: 1 ++ bopts iters equal: 1 + >>> paste ++ g/w/b:1/0/0 ++ g/w/b (in):1/0/0 + p/gopts1: False + p/wopts1! KeyError + inv: 2! KeyError +*************** +*** 133,138 **** +--- 137,144 ---- + W: 1:1 2:1 3:1 4:1 + B: 1:1 2:1 3:1 4:1 + >>> previewheight ++ g/w/b:1/0/0 ++ g/w/b (in):1/0/0 + p/gopts1: 12 + inv: 'a'! TypeError + p/wopts1! KeyError +*************** +*** 154,159 **** +--- 160,167 ---- + W: 1:5 2:5 3:5 4:5 + B: 1:5 2:5 3:5 4:5 + >>> operatorfunc ++ g/w/b:1/0/0 ++ g/w/b (in):1/0/0 + p/gopts1: b'' + inv: 2! TypeError + p/wopts1! KeyError +*************** +*** 175,180 **** +--- 183,190 ---- + W: 1:'A' 2:'A' 3:'A' 4:'A' + B: 1:'A' 2:'A' 3:'A' 4:'A' + >>> number ++ g/w/b:0/1/0 ++ g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 0! KeyError + gopts1! KeyError +*************** +*** 193,198 **** +--- 203,210 ---- + W: 1:1 2:1 3:0 4:0 + B: 1:1 2:1 3:0 4:0 + >>> numberwidth ++ g/w/b:0/1/0 ++ g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: -100! KeyError + gopts1! KeyError +*************** +*** 212,217 **** +--- 224,231 ---- + W: 1:3 2:5 3:2 4:8 + B: 1:3 2:5 3:2 4:8 + >>> colorcolumn ++ g/w/b:0/1/0 ++ g/w/b (in):0/1/0 + p/gopts1! KeyError + inv: 'abc4'! KeyError + gopts1! KeyError +*************** +*** 231,236 **** +--- 245,252 ---- + W: 1:'+2' 2:'+3' 3:'+1' 4:'' + B: 1:'+2' 2:'+3' 3:'+1' 4:'' + >>> statusline ++ g/w/b:1/1/0 ++ g/w/b (in):1/1/0 + p/gopts1: b'' + inv: 0! TypeError + p/wopts1: None +*************** +*** 248,253 **** +--- 264,271 ---- + W: 1:'2' 2:'1' 3:'1' 4:'1' + B: 1:'2' 2:'1' 3:'1' 4:'1' + >>> autoindent ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError +*************** +*** 266,271 **** +--- 284,291 ---- + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> shiftwidth ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 3! KeyError + gopts1! KeyError +*************** +*** 284,289 **** +--- 304,311 ---- + W: 1:0 2:2 3:8 4:1 + B: 1:0 2:2 3:8 4:1 + >>> omnifunc ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 1! KeyError + gopts1! KeyError +*************** +*** 303,308 **** +--- 325,332 ---- + W: 1:'A' 2:'B' 3:'' 4:'C' + B: 1:'A' 2:'B' 3:'' 4:'C' + >>> preserveindent ++ g/w/b:0/0/1 ++ g/w/b (in):0/0/1 + p/gopts1! KeyError + inv: 2! KeyError + gopts1! KeyError +*************** +*** 321,326 **** +--- 345,352 ---- + W: 1:0 2:1 3:0 4:1 + B: 1:0 2:1 3:0 4:1 + >>> path ++ g/w/b:1/0/1 ++ g/w/b (in):1/0/1 + p/gopts1: b'.,..,,' + inv: 0! TypeError + p/wopts1! KeyError +*************** +*** 509,514 **** +--- 535,555 ---- + import xxx_no_such_module_xxx:(, ImportError('No module named xxx_no_such_module_xxx',)) + import failing_import:(, ImportError('No module named failing_import',)) + import failing:(, NotImplementedError()) ++ > Options ++ >> OptionsItem ++ vim.options["abcQ"]:(, KeyError('abcQ',)) ++ vim.options[""]:(, ValueError('empty keys are not allowed',)) ++ >>> Testing StringToChars using vim.options[%s] ++ vim.options[1]:(, TypeError('expected bytes() or str() instance, but got int',)) ++ vim.options[b"\0"]:(, TypeError('expected bytes with no null',)) ++ vim.options["\0"]:(, TypeError('expected bytes with no null',)) ++ <<< Finished ++ >> OptionsContains ++ >>> Testing StringToChars using %s in vim.options ++ 1 in vim.options:(, TypeError('expected bytes() or str() instance, but got int',)) ++ b"\0" in vim.options:(, TypeError('expected bytes with no null',)) ++ "\0" in vim.options:(, TypeError('expected bytes with no null',)) ++ <<< Finished + > Dictionary + >> DictionaryConstructor + vim.Dictionary("abcI"):(, ValueError('expected sequence element of size 2, but got sequence of size 1',)) +*** ../vim-7.4.151/src/vim.h 2013-11-09 03:31:45.000000000 +0100 +--- src/vim.h 2014-01-14 16:49:10.000000000 +0100 +*************** +*** 2249,2254 **** +--- 2249,2255 ---- + #define SOPT_BUF 0x20 /* Option has buffer-local value */ + #define SOPT_UNSET 0x40 /* Option does not have local value set */ + ++ /* Option types for various functions in option.c */ + #define SREQ_GLOBAL 0 /* Request global option */ + #define SREQ_WIN 1 /* Request window-local option */ + #define SREQ_BUF 2 /* Request buffer-local option */ +*** ../vim-7.4.151/src/version.c 2014-01-14 16:36:40.000000000 +0100 +--- src/version.c 2014-01-14 16:43:58.000000000 +0100 +*************** +*** 740,741 **** +--- 740,743 ---- + { /* Add new patch number below this line */ ++ /**/ ++ 152, + /**/ + +-- +hundred-and-one symptoms of being an internet addict: +160. You get in the elevator and double-click the button for the floor + you want. + + /// Bram Moolenaar -- Bram@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 ///