To: vim_dev@googlegroups.com Subject: Patch 7.4.1324 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1324 Problem: Channels with pipes don't work on MS-Windows. Solution: Add pipe I/O support. (Yasuhiro Matsumoto) Files: src/channel.c, src/os_win32.c, src/proto/channel.pro, src/structs.h, src/vim.h, src/testdir/test_channel.vim *** ../vim-7.4.1323/src/channel.c 2016-02-15 20:39:42.585803341 +0100 --- src/channel.c 2016-02-15 21:40:03.567647945 +0100 *************** *** 32,41 **** # undef EINTR # endif # define EINTR WSAEINTR ! # define sock_write(sd, buf, len) send(sd, buf, len, 0) ! # define sock_read(sd, buf, len) recv(sd, buf, len, 0) ! # define sock_close(sd) closesocket(sd) ! # define sleep(t) Sleep(t*1000) /* WinAPI Sleep() accepts milliseconds */ #else # include # include --- 32,40 ---- # undef EINTR # endif # define EINTR WSAEINTR ! # define sock_write(sd, buf, len) send((SOCKET)sd, buf, len, 0) ! # define sock_read(sd, buf, len) recv((SOCKET)sd, buf, len, 0) ! # define sock_close(sd) closesocket((SOCKET)sd) #else # include # include *************** *** 48,59 **** --- 47,92 ---- # define sock_write(sd, buf, len) write(sd, buf, len) # define sock_read(sd, buf, len) read(sd, buf, len) # define sock_close(sd) close(sd) + # define fd_read(fd, buf, len, timeout) read(fd, buf, len) + # define fd_write(sd, buf, len) write(sd, buf, len) + # define fd_close(sd) close(sd) #endif #ifdef FEAT_GUI_W32 extern HWND s_hwnd; /* Gvim's Window handle */ #endif + #ifdef WIN32 + static int + fd_read(sock_T fd, char_u *buf, size_t len, int timeout) + { + HANDLE h = (HANDLE)fd; + DWORD nread; + + if (!ReadFile(h, buf, (DWORD)len, &nread, NULL)) + return -1; + return (int)nread; + } + + static int + fd_write(sock_T fd, char_u *buf, size_t len) + { + HANDLE h = (HANDLE)fd; + DWORD nwrite; + + if (!WriteFile(h, buf, (DWORD)len, &nwrite, NULL)) + return -1; + return (int)nwrite; + } + + static void + fd_close(sock_T fd) + { + HANDLE h = (HANDLE)fd; + + CloseHandle(h); + } + #endif /* Log file opened with ch_logfile(). */ static FILE *log_fd = NULL; *************** *** 228,234 **** which = CHAN_SOCK; #endif { ! channel->ch_pfd[which].ch_fd = (sock_T)-1; #ifdef FEAT_GUI_X11 channel->ch_pfd[which].ch_inputHandler = (XtInputId)NULL; #endif --- 261,267 ---- which = CHAN_SOCK; #endif { ! channel->ch_pfd[which].ch_fd = CHAN_FD_INVALID; #ifdef FEAT_GUI_X11 channel->ch_pfd[which].ch_inputHandler = (XtInputId)NULL; #endif *************** *** 363,374 **** if (!CH_HAS_GUI) return; ! if (channel->CH_SOCK >= 0) channel_gui_register_one(channel, CHAN_SOCK); # ifdef CHANNEL_PIPES ! if (channel->CH_OUT >= 0) channel_gui_register_one(channel, CHAN_OUT); ! if (channel->CH_ERR >= 0) channel_gui_register_one(channel, CHAN_ERR); # endif } --- 396,407 ---- if (!CH_HAS_GUI) return; ! if (channel->CH_SOCK != CHAN_FD_INVALID) channel_gui_register_one(channel, CHAN_SOCK); # ifdef CHANNEL_PIPES ! if (channel->CH_OUT != CHAN_FD_INVALID) channel_gui_register_one(channel, CHAN_OUT); ! if (channel->CH_ERR != CHAN_FD_INVALID) channel_gui_register_one(channel, CHAN_ERR); # endif } *************** *** 457,463 **** return NULL; } ! if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { ch_error(NULL, "in socket() in channel_open().\n"); PERROR("E898: socket() in channel_open()"); --- 490,496 ---- return NULL; } ! if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ch_error(NULL, "in socket() in channel_open().\n"); PERROR("E898: socket() in channel_open()"); *************** *** 564,570 **** if (errno == ECONNREFUSED && close_cb != NULL) { sock_close(sd); ! if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { SOCK_ERRNO; ch_log(NULL, "socket() retry in channel_open()\n"); --- 597,603 ---- if (errno == ECONNREFUSED && close_cb != NULL) { sock_close(sd); ! if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { SOCK_ERRNO; ch_log(NULL, "socket() retry in channel_open()\n"); *************** *** 609,615 **** } } ! channel->CH_SOCK = sd; channel->ch_close_cb = close_cb; #ifdef FEAT_GUI --- 642,648 ---- } } ! channel->CH_SOCK = (sock_T)sd; channel->ch_close_cb = close_cb; #ifdef FEAT_GUI *************** *** 621,627 **** #if defined(CHANNEL_PIPES) || defined(PROTO) void ! channel_set_pipes(channel_T *channel, int in, int out, int err) { channel->CH_IN = in; channel->CH_OUT = out; --- 654,660 ---- #if defined(CHANNEL_PIPES) || defined(PROTO) void ! channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err) { channel->CH_IN = in; channel->CH_OUT = out; *************** *** 1144,1152 **** int channel_can_write_to(channel_T *channel) { ! return channel != NULL && (channel->CH_SOCK >= 0 #ifdef CHANNEL_PIPES ! || channel->CH_IN >= 0 #endif ); } --- 1177,1185 ---- int channel_can_write_to(channel_T *channel) { ! return channel != NULL && (channel->CH_SOCK != CHAN_FD_INVALID #ifdef CHANNEL_PIPES ! || channel->CH_IN != CHAN_FD_INVALID #endif ); } *************** *** 1158,1168 **** int channel_is_open(channel_T *channel) { ! return channel != NULL && (channel->CH_SOCK >= 0 #ifdef CHANNEL_PIPES ! || channel->CH_IN >= 0 ! || channel->CH_OUT >= 0 ! || channel->CH_ERR >= 0 #endif ); } --- 1191,1201 ---- int channel_is_open(channel_T *channel) { ! return channel != NULL && (channel->CH_SOCK != CHAN_FD_INVALID #ifdef CHANNEL_PIPES ! || channel->CH_IN != CHAN_FD_INVALID ! || channel->CH_OUT != CHAN_FD_INVALID ! || channel->CH_ERR != CHAN_FD_INVALID #endif ); } *************** *** 1193,1218 **** channel_gui_unregister(channel); #endif ! if (channel->CH_SOCK >= 0) { sock_close(channel->CH_SOCK); ! channel->CH_SOCK = -1; } #if defined(CHANNEL_PIPES) ! if (channel->CH_IN >= 0) { ! close(channel->CH_IN); ! channel->CH_IN = -1; } ! if (channel->CH_OUT >= 0) { ! close(channel->CH_OUT); ! channel->CH_OUT = -1; } ! if (channel->CH_ERR >= 0) { ! close(channel->CH_ERR); ! channel->CH_ERR = -1; } #endif --- 1226,1251 ---- channel_gui_unregister(channel); #endif ! if (channel->CH_SOCK != CHAN_FD_INVALID) { sock_close(channel->CH_SOCK); ! channel->CH_SOCK = CHAN_FD_INVALID; } #if defined(CHANNEL_PIPES) ! if (channel->CH_IN != CHAN_FD_INVALID) { ! fd_close(channel->CH_IN); ! channel->CH_IN = CHAN_FD_INVALID; } ! if (channel->CH_OUT != CHAN_FD_INVALID) { ! fd_close(channel->CH_OUT); ! channel->CH_OUT = CHAN_FD_INVALID; } ! if (channel->CH_ERR != CHAN_FD_INVALID) { ! fd_close(channel->CH_ERR); ! channel->CH_ERR = CHAN_FD_INVALID; } #endif *************** *** 1325,1331 **** * Always returns OK for FEAT_GUI_W32. */ static int ! channel_wait(channel_T *channel, int fd, int timeout) { #if defined(HAVE_SELECT) && !defined(FEAT_GUI_W32) struct timeval tval; --- 1358,1364 ---- * Always returns OK for FEAT_GUI_W32. */ static int ! channel_wait(channel_T *channel, sock_T fd, int timeout) { #if defined(HAVE_SELECT) && !defined(FEAT_GUI_W32) struct timeval tval; *************** *** 1334,1346 **** if (timeout > 0) ch_logn(channel, "Waiting for %d msec\n", timeout); FD_ZERO(&rfds); ! FD_SET(fd, &rfds); tval.tv_sec = timeout / 1000; tval.tv_usec = (timeout % 1000) * 1000; for (;;) { ! ret = select(fd + 1, &rfds, NULL, NULL, &tval); # ifdef EINTR if (ret == -1 && errno == EINTR) continue; --- 1367,1404 ---- if (timeout > 0) ch_logn(channel, "Waiting for %d msec\n", timeout); + + + # ifdef WIN32 + if (channel->CH_SOCK == CHAN_FD_INVALID) + { + DWORD nread; + int diff; + DWORD deadline = GetTickCount() + timeout; + + /* reading from a pipe, not a socket */ + while (TRUE) + { + if (PeekNamedPipe(fd, NULL, 0, NULL, &nread, NULL) && nread > 0) + return OK; + diff = deadline - GetTickCount(); + if (diff < 0) + break; + /* Wait for 5 msec. + * TODO: increase the sleep time when looping more often */ + Sleep(5); + } + return FAIL; + } + #endif + FD_ZERO(&rfds); ! FD_SET((int)fd, &rfds); tval.tv_sec = timeout / 1000; tval.tv_usec = (timeout % 1000) * 1000; for (;;) { ! ret = select((int)fd + 1, &rfds, NULL, NULL, &tval); # ifdef EINTR if (ret == -1 && errno == EINTR) continue; *************** *** 1385,1401 **** * Get the file descriptor to read from, either the socket or stdout. * TODO: should have a way to read stderr. */ ! static int get_read_fd(channel_T *channel) { ! if (channel->CH_SOCK >= 0) return channel->CH_SOCK; #if defined(CHANNEL_PIPES) ! if (channel->CH_OUT >= 0) return channel->CH_OUT; #endif ch_error(channel, "channel_read() called while socket is closed\n"); ! return -1; } /* --- 1443,1459 ---- * Get the file descriptor to read from, either the socket or stdout. * TODO: should have a way to read stderr. */ ! static sock_T get_read_fd(channel_T *channel) { ! if (channel->CH_SOCK != CHAN_FD_INVALID) return channel->CH_SOCK; #if defined(CHANNEL_PIPES) ! if (channel->CH_OUT != CHAN_FD_INVALID) return channel->CH_OUT; #endif ch_error(channel, "channel_read() called while socket is closed\n"); ! return CHAN_FD_INVALID; } /* *************** *** 1410,1423 **** static char_u *buf = NULL; int len = 0; int readlen = 0; ! int fd; int use_socket = FALSE; if (which < 0) fd = get_read_fd(channel); else fd = channel->ch_pfd[which].ch_fd; ! if (fd < 0) return; use_socket = fd == channel->CH_SOCK; --- 1468,1481 ---- static char_u *buf = NULL; int len = 0; int readlen = 0; ! sock_T fd; int use_socket = FALSE; if (which < 0) fd = get_read_fd(channel); else fd = channel->ch_pfd[which].ch_fd; ! if (fd == CHAN_FD_INVALID) return; use_socket = fd == channel->CH_SOCK; *************** *** 1439,1445 **** if (use_socket) len = sock_read(fd, buf, MAXMSGSIZE); else ! len = read(fd, buf, MAXMSGSIZE); if (len <= 0) break; /* error or nothing more to read */ --- 1497,1503 ---- if (use_socket) len = sock_read(fd, buf, MAXMSGSIZE); else ! len = fd_read(fd, buf, MAXMSGSIZE, channel->ch_timeout); if (len <= 0) break; /* error or nothing more to read */ *************** *** 1509,1520 **** ch_log(channel, "Reading raw\n"); if (channel_peek(channel) == NULL) { ! int fd = get_read_fd(channel); /* TODO: read both out and err if they are different */ ch_log(channel, "No readahead\n"); /* Wait for up to the channel timeout. */ ! if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout) == FAIL) return NULL; channel_read(channel, -1, "channel_read_block"); } --- 1567,1579 ---- ch_log(channel, "Reading raw\n"); if (channel_peek(channel) == NULL) { ! sock_T fd = get_read_fd(channel); /* TODO: read both out and err if they are different */ ch_log(channel, "No readahead\n"); /* Wait for up to the channel timeout. */ ! if (fd == CHAN_FD_INVALID ! || channel_wait(channel, fd, channel->ch_timeout) == FAIL) return NULL; channel_read(channel, -1, "channel_read_block"); } *************** *** 1533,1539 **** channel_read_json_block(channel_T *channel, int id, typval_T **rettv) { int more; ! int fd; ch_log(channel, "Reading JSON\n"); channel->ch_block_id = id; --- 1592,1598 ---- channel_read_json_block(channel_T *channel, int id, typval_T **rettv) { int more; ! sock_T fd; ch_log(channel, "Reading JSON\n"); channel->ch_block_id = id; *************** *** 1557,1564 **** /* Wait for up to the channel timeout. */ fd = get_read_fd(channel); ! if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout) ! == FAIL) break; channel_read(channel, -1, "channel_read_json_block"); } --- 1616,1623 ---- /* Wait for up to the channel timeout. */ fd = get_read_fd(channel); ! if (fd == CHAN_FD_INVALID ! || channel_wait(channel, fd, channel->ch_timeout) == FAIL) break; channel_read(channel, -1, "channel_read_json_block"); } *************** *** 1578,1584 **** channel_T *channel; int i; ! if (fd >= 0) for (channel = first_channel; channel != NULL; channel = channel->ch_next) { --- 1637,1643 ---- channel_T *channel; int i; ! if (fd != CHAN_FD_INVALID) for (channel = first_channel; channel != NULL; channel = channel->ch_next) { *************** *** 1607,1625 **** { int len = (int)STRLEN(buf); int res; ! int fd = -1; int use_socket = FALSE; ! if (channel->CH_SOCK >= 0) { fd = channel->CH_SOCK; use_socket = TRUE; } #if defined(CHANNEL_PIPES) ! else if (channel->CH_IN >= 0) fd = channel->CH_IN; #endif ! if (fd < 0) { if (!channel->ch_error && fun != NULL) { --- 1666,1684 ---- { int len = (int)STRLEN(buf); int res; ! sock_T fd = CHAN_FD_INVALID; int use_socket = FALSE; ! if (channel->CH_SOCK != CHAN_FD_INVALID) { fd = channel->CH_SOCK; use_socket = TRUE; } #if defined(CHANNEL_PIPES) ! else if (channel->CH_IN != CHAN_FD_INVALID) fd = channel->CH_IN; #endif ! if (fd == CHAN_FD_INVALID) { if (!channel->ch_error && fun != NULL) { *************** *** 1642,1648 **** if (use_socket) res = sock_write(fd, buf, len); else ! res = write(fd, buf, len); if (res != len) { if (!channel->ch_error && fun != NULL) --- 1701,1707 ---- if (use_socket) res = sock_write(fd, buf, len); else ! res = fd_write(fd, buf, len); if (res != len) { if (!channel->ch_error && fun != NULL) *************** *** 1680,1686 **** which = CHAN_SOCK; # endif { ! if (channel->ch_pfd[which].ch_fd >= 0) { channel->ch_pfd[which].ch_poll_idx = nfd; fds[nfd].fd = channel->ch_pfd[which].ch_fd; --- 1739,1745 ---- which = CHAN_SOCK; # endif { ! if (channel->ch_pfd[which].ch_fd != CHAN_FD_INVALID) { channel->ch_pfd[which].ch_poll_idx = nfd; fds[nfd].fd = channel->ch_pfd[which].ch_fd; *************** *** 1750,1760 **** { sock_T fd = channel->ch_pfd[which].ch_fd; ! if (fd >= 0) { ! FD_SET(fd, rfds); ! if (maxfd < fd) ! maxfd = fd; } } } --- 1809,1819 ---- { sock_T fd = channel->ch_pfd[which].ch_fd; ! if (fd != CHAN_FD_INVALID) { ! FD_SET((int)fd, rfds); ! if (maxfd < (int)fd) ! maxfd = (int)fd; } } } *************** *** 1783,1789 **** { sock_T fd = channel->ch_pfd[which].ch_fd; ! if (ret > 0 && fd >= 0 && FD_ISSET(fd, rfds)) { channel_read(channel, which, "channel_select_check"); --ret; --- 1842,1848 ---- { sock_T fd = channel->ch_pfd[which].ch_fd; ! if (ret > 0 && fd != CHAN_FD_INVALID && FD_ISSET(fd, rfds)) { channel_read(channel, which, "channel_select_check"); --ret; *** ../vim-7.4.1323/src/os_win32.c 2016-02-15 12:44:16.221956302 +0100 --- src/os_win32.c 2016-02-15 21:45:01.180513293 +0100 *************** *** 5041,5046 **** --- 5041,5057 ---- HANDLE jo; # ifdef FEAT_CHANNEL channel_T *channel; + HANDLE ifd[2]; + HANDLE ofd[2]; + HANDLE efd[2]; + SECURITY_ATTRIBUTES saAttr; + + ifd[0] = INVALID_HANDLE_VALUE; + ifd[1] = INVALID_HANDLE_VALUE; + ofd[0] = INVALID_HANDLE_VALUE; + ofd[1] = INVALID_HANDLE_VALUE; + efd[0] = INVALID_HANDLE_VALUE; + efd[1] = INVALID_HANDLE_VALUE; channel = add_channel(); if (channel == NULL) *************** *** 5057,5066 **** ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ! si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; ! if (!vim_create_process(cmd, FALSE, CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP | --- 5068,5092 ---- ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ! si.dwFlags |= STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; ! saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); ! saAttr.bInheritHandle = TRUE; ! saAttr.lpSecurityDescriptor = NULL; ! if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0) ! || !pSetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0) ! || !CreatePipe(&ofd[0], &ofd[1], &saAttr, 0) ! || !pSetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0) ! || !CreatePipe(&efd[0], &efd[1], &saAttr, 0) ! || !pSetHandleInformation(efd[0], HANDLE_FLAG_INHERIT, 0)) ! goto failed; ! si.dwFlags |= STARTF_USESTDHANDLES; ! si.hStdInput = ifd[0]; ! si.hStdOutput = ofd[1]; ! si.hStdError = efd[1]; ! ! if (!vim_create_process(cmd, TRUE, CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP | *************** *** 5085,5106 **** job->jv_job_object = jo; job->jv_status = JOB_STARTED; # ifdef FEAT_CHANNEL - # if 0 - /* TODO: connect stdin/stdout/stderr */ job->jv_channel = channel; ! channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]); channel_set_job(channel, job); # ifdef FEAT_GUI channel_gui_register(channel); # endif - # endif # endif return; failed: # ifdef FEAT_CHANNEL channel_free(channel); # else ; /* make compiler happy */ --- 5111,5139 ---- job->jv_job_object = jo; job->jv_status = JOB_STARTED; + CloseHandle(ifd[0]); + CloseHandle(ofd[1]); + CloseHandle(efd[1]); + # ifdef FEAT_CHANNEL job->jv_channel = channel; ! channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]); channel_set_job(channel, job); # ifdef FEAT_GUI channel_gui_register(channel); # endif # endif return; failed: # ifdef FEAT_CHANNEL + CloseHandle(ifd[0]); + CloseHandle(ofd[0]); + CloseHandle(efd[0]); + CloseHandle(ifd[1]); + CloseHandle(ofd[1]); + CloseHandle(efd[1]); channel_free(channel); # else ; /* make compiler happy */ *** ../vim-7.4.1323/src/proto/channel.pro 2016-02-14 23:02:29.838701624 +0100 --- src/proto/channel.pro 2016-02-15 21:45:20.188313115 +0100 *************** *** 5,11 **** void channel_gui_register(channel_T *channel); void channel_gui_register_all(void); channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)); ! void channel_set_pipes(channel_T *channel, int in, int out, int err); void channel_set_job(channel_T *channel, job_T *job); void channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode); void channel_set_timeout(channel_T *channel, int timeout); --- 5,11 ---- void channel_gui_register(channel_T *channel); void channel_gui_register_all(void); channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)); ! void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job); void channel_set_json_mode(channel_T *channel, ch_mode_T ch_mode); void channel_set_timeout(channel_T *channel, int timeout); *** ../vim-7.4.1323/src/structs.h 2016-02-14 23:02:29.834701666 +0100 --- src/structs.h 2016-02-15 21:45:44.444057673 +0100 *************** *** 1306,1313 **** #define CHAN_SOCK 0 #define CH_SOCK ch_pfd[CHAN_SOCK].ch_fd ! #ifdef UNIX # define CHANNEL_PIPES # define CHAN_OUT 1 # define CHAN_ERR 2 --- 1306,1314 ---- #define CHAN_SOCK 0 #define CH_SOCK ch_pfd[CHAN_SOCK].ch_fd ! #if defined(UNIX) || defined(WIN32) # define CHANNEL_PIPES + # define CHAN_FD_INVALID (-1) # define CHAN_OUT 1 # define CHAN_ERR 2 *** ../vim-7.4.1323/src/vim.h 2016-02-13 17:24:20.462210990 +0100 --- src/vim.h 2016-02-15 21:26:44.828064835 +0100 *************** *** 1730,1736 **** typedef int proftime_T; /* dummy for function prototypes */ #endif ! #ifdef WIN64 typedef __int64 sock_T; #else typedef int sock_T; --- 1730,1736 ---- typedef int proftime_T; /* dummy for function prototypes */ #endif ! #ifdef _WIN64 typedef __int64 sock_T; #else typedef int sock_T; *** ../vim-7.4.1323/src/testdir/test_channel.vim 2016-02-15 20:39:42.585803341 +0100 --- src/testdir/test_channel.vim 2016-02-15 21:46:18.383700257 +0100 *************** *** 280,286 **** endfunc func Test_pipe() ! if !has('job') || !has('unix') return endif let job = job_start("python test_channel_pipe.py") --- 280,286 ---- endfunc func Test_pipe() ! if !has('job') return endif let job = job_start("python test_channel_pipe.py") *** ../vim-7.4.1323/src/version.c 2016-02-15 21:22:06.470999629 +0100 --- src/version.c 2016-02-15 21:27:34.463541603 +0100 *************** *** 749,750 **** --- 749,752 ---- { /* Add new patch number below this line */ + /**/ + 1324, /**/ -- hundred-and-one symptoms of being an internet addict: 269. You receive an e-mail from the wife of a deceased president, offering to send you twenty million dollar, and you are not even surprised. /// 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 ///