summaryrefslogtreecommitdiffstats
path: root/runtime/tools
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/tools')
-rw-r--r--runtime/tools/README.txt37
-rw-r--r--runtime/tools/blink.c24
-rw-r--r--runtime/tools/ccfilter.193
-rw-r--r--runtime/tools/ccfilter.c328
-rw-r--r--runtime/tools/ccfilter_README.txt101
-rw-r--r--runtime/tools/demoserver.py107
-rwxr-xr-xruntime/tools/efm_filter.pl39
-rw-r--r--runtime/tools/efm_filter.txt31
-rwxr-xr-xruntime/tools/efm_perl.pl153
-rw-r--r--runtime/tools/emoji_list.vim23
-rwxr-xr-xruntime/tools/mve.awk23
-rw-r--r--runtime/tools/mve.txt20
-rwxr-xr-xruntime/tools/pltags.pl300
-rwxr-xr-xruntime/tools/ref11
-rw-r--r--runtime/tools/shtags.161
-rwxr-xr-xruntime/tools/shtags.pl144
-rw-r--r--runtime/tools/unicode.vim471
-rwxr-xr-xruntime/tools/vim13213
-rw-r--r--runtime/tools/vim_vs_net.cmd23
-rwxr-xr-xruntime/tools/vimm6
-rwxr-xr-xruntime/tools/vimspell.sh55
-rw-r--r--runtime/tools/vimspell.txt22
-rw-r--r--runtime/tools/xcmdsrv_client.c578
23 files changed, 2663 insertions, 0 deletions
diff --git a/runtime/tools/README.txt b/runtime/tools/README.txt
new file mode 100644
index 0000000..19976b3
--- /dev/null
+++ b/runtime/tools/README.txt
@@ -0,0 +1,37 @@
+Some tools that can be used with Vim:
+
+blink.c: C program to make the cursor blink in an xterm.
+
+ccfilter*: C program to filter the output of a few compilers to a common
+ QuickFix format.
+
+efm_filter.*: Perl script to filter compiler messages to QuickFix format.
+
+efm_perl.pl: Perl script to filter error messages from the Perl interpreter
+ for use with Vim quickfix mode.
+
+mve.* Awk script to filter error messages to QuickFix format.
+
+pltags.pl: Perl script to create a tags file from Perl scripts.
+
+ref: Shell script for the K command.
+
+shtags.*: Perl script to create a tags file from a shell script.
+
+vim132: Shell script to edit in 132 column mode on vt100 compatible
+ terminals.
+
+vimm: Shell script to start Vim on a DEC terminal with mouse
+ enabled.
+
+vimspell.*: Shell script for highlighting spelling mistakes.
+
+vim_vs_net.cmd: MS-Windows command file to use Vim with MS Visual Studio 7 and
+ later.
+
+xcmdsrv_client.c: Example for a client program that communicates with a Vim
+ server through the X-Windows interface.
+
+unicode.vim Vim script to generate tables for src/mbyte.c.
+
+[xxd can be found in the src directory]
diff --git a/runtime/tools/blink.c b/runtime/tools/blink.c
new file mode 100644
index 0000000..a782061
--- /dev/null
+++ b/runtime/tools/blink.c
@@ -0,0 +1,24 @@
+/*
+ * An extremely simple program to make the cursor blink in an xterm.
+ * This is useful when the cursor is hard to spot in a highlighted file.
+ * Start in the background: "blink&" Stop by killing it.
+ * Bram Moolenaar 980109 (based on an idea from John Lange).
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+ int
+main(void)
+{
+ while (1)
+ {
+ printf("\e[?25h");
+ fflush(stdout);
+ usleep(400000); /* on time */
+ printf("\e[?25l");
+ fflush(stdout);
+ usleep(250000); /* off time */
+ }
+ return 0;
+}
diff --git a/runtime/tools/ccfilter.1 b/runtime/tools/ccfilter.1
new file mode 100644
index 0000000..92fe624
--- /dev/null
+++ b/runtime/tools/ccfilter.1
@@ -0,0 +1,93 @@
+.TH ccfilter 1 "01-Apr-97"
+.SH NAME
+ccfilter \- a compiler's output filter for vim quickfix
+.SH SYNOPSIS
+ccfilter [
+.B <options>
+]
+.SH DESCRIPTION
+The ccfilter utility "filters" the output of several compilers
+and makers (make/gmake) from several platforms (see NOTES below)
+to a standardized format which easily fits in vim's quickfix
+feature. For further details, see in vim ":help quickfix".
+.PP
+ccfilter reads
+.B 'stdin'
+and outputs to
+.B 'stdout'
+\.
+.PP
+The need for ccfilter is clear, as some compilers have irregular
+and/or multiple line error messages (with the relevant information on
+line 2), which makes it impossible for the errorformat to correctly
+display them !
+
+When working on different platforms, and with different compilers,
+ccfilter eases the utilization of quickfix, due to its standardized
+output, allowing to have in .vimrc a plain
+.br
+.B \ \ \ \ :set\ errorformat=%f:%l:%c:%t:%m
+
+.SH USAGE
+When using ccfilter, one would include the following lines in .vimrc:
+.br
+.B \ \ \ \ :set shellpipe=\\\\|&ccfilter\\\\>
+.br
+.B \ \ \ \ :set errorformat=%f:%l:%c:%t:%m
+
+.SH OPTIONS
+.TP 16
+-c
+Decrement column by one. This may be needed, depending on
+the compiler being used.
+.TP
+-r
+Decrement row by one. This may be needed, depending on
+the compiler being used.
+.TP
+-v
+Verbose (Outputs also invalid lines).
+This option makes ccfilter output also the lines that
+couldn't be correctly parsed. This is used mostly for
+ccfilter debugging.
+.TP
+-o <COMPILER>
+Treat input as <COMPILER>'s output.
+Even when configuring ccfilter to assume a default
+COMPILER, sometimes it's helpful to be able to specify
+the COMPILER used to generate ccfilter's input.
+For example, when cross-compiling on a network from a
+single machine.
+.TP
+-h
+Shows a brief help, describing the configured default COMPILER
+and the valid parameters for COMPILER.
+
+.SH NOTES
+Currently, ccfilter accepts output from several compilers, as
+described below:
+.TP 10
+GCC
+GCC compiler
+.TP
+AIX
+AIX's C compiler
+.TP
+ATT
+AT&T/NCR's High Performance C Compiler
+.TP
+IRIX
+IRIX's MIPS/MIPSpro C compiler
+.TP
+SOLARIS
+SOLARIS's SparcWorks C compiler
+.TP
+HPUX
+HPUX's C compiler
+
+.SH AUTHOR
+.B ccfilter
+was developed by
+.B Pablo Ariel Kohan
+.BR
+.B mailto:pablo@memco.co.il
diff --git a/runtime/tools/ccfilter.c b/runtime/tools/ccfilter.c
new file mode 100644
index 0000000..43489f1
--- /dev/null
+++ b/runtime/tools/ccfilter.c
@@ -0,0 +1,328 @@
+/* ======================================================================= */
+/* Project : VIM */
+/* Module : ccfilter Version: 02.01.01 */
+/* File : ccfilter.c */
+/* Purpose : Filter gmake/cc output into a standardized form */
+/* ======================================================================= */
+/* Created On: 12-Sep-95 20:32 */
+/* Last modification: 03-Feb-98 */
+/* -e option added by Bernd Feige */
+/* ======================================================================= */
+/* Copyright : */
+/* This source file is copyright (c) to Pablo Ariel Kohan */
+/* ======================================================================= */
+#define __CCFILTER_C__
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LINELENGTH 2048
+
+/* Collector(s) */
+char Line[LINELENGTH];
+char Line2[LINELENGTH];
+/* Components */
+char FileName[1024];
+char BasePath[1024];
+char CWD[1024];
+unsigned long Row;
+unsigned long Col;
+char Severity;
+char Reason[LINELENGTH];
+
+#define COMPILER_UNKNOWN 0
+#define COMPILER_GCC 1
+#define COMPILER_AIX 2
+#define COMPILER_ATT 3
+#define COMPILER_IRIX 4
+#define COMPILER_SOLARIS 5
+#define COMPILER_HPUX 6
+
+char *COMPILER_Names[][2] =
+ {
+ /* Name Description */
+ { "N/A", "" },
+ { "GCC", "GCC compiler" },
+ { "AIX", "AIX's C compiler" },
+ { "ATT", "AT&T/NCR's High Performance C Compiler" },
+ { "IRIX", "IRIX's MIPS/MIPSpro C compiler" },
+ { "SOLARIS", "SOLARIS's SparcWorks C compiler" },
+ { "HPUX", "HPUX's C compiler" }
+ };
+#define COMPILER_QTY (sizeof(COMPILER_Names)/sizeof(COMPILER_Names[0]))
+
+#if defined(_GCC)
+# define COMPILER_DEFAULT COMPILER_GCC
+#elif defined(_AIX)
+# define COMPILER_DEFAULT COMPILER_AIX
+#elif defined(_ATT)
+# define COMPILER_DEFAULT COMPILER_ATT
+#elif defined(_IRIX)
+# define COMPILER_DEFAULT COMPILER_IRIX
+#elif defined(_SOLARIS)
+# define COMPILER_DEFAULT COMPILER_SOLARIS
+#elif defined(_HPUX)
+# define COMPILER_DEFAULT COMPILER_HPUX
+#else
+# define COMPILER_DEFAULT COMPILER_UNKNOWN
+#endif
+
+const char USAGE[] =
+"ccfilter v2.1 (c)1994-1997 by Pablo Ariel Kohan\n"
+"Filter Out compiler's output, and converts it to fit VIM\n\n"
+"Usage:\n"
+" ccfilter [<options>]\n"
+"Where: <options> is one or more of:\n"
+" -c Decrement column by one\n"
+" -r Decrement row by one\n"
+" -e Echo stdin to stderr\n"
+" -v Verbose (Outputs also invalid lines)\n"
+" -o <COMPILER> Treat input as <COMPILER>'s output\n"
+" Note: COMPILER may be preceded by an _\n"
+" -h This usage.\n";
+
+
+int ShowUsage( char *szError )
+{
+ int i;
+
+ fprintf( stderr, USAGE );
+
+ fprintf( stderr, "Current default <COMPILER>: %s\n",
+ COMPILER_Names[COMPILER_DEFAULT][0] );
+
+ fprintf( stderr, "Acceptable parameters for <COMPILER> are:\n" );
+ for (i=1; i < COMPILER_QTY; i++)
+ fprintf( stderr, " %-15.15s %s\n",
+ COMPILER_Names[i][0],
+ COMPILER_Names[i][1] );
+ fprintf(stderr, szError);
+ return 0;
+}
+
+char *echogets(char *s, int echo)
+{
+ char * const retval=fgets(s, LINELENGTH, stdin);
+ if (echo!=0 && retval!=NULL) {
+ fputs(retval, stderr);
+ }
+ return retval;
+}
+
+int main( int argc, char *argv[] )
+{ int rv, i, j, ok;
+ int stay;
+ int prefetch;
+ char *p;
+ int dec_col = 0; /* Decrement column value by 1 */
+ int dec_row = 0; /* Decrement row value by 1 */
+ int echo = 0; /* Echo stdin to stderr */
+ int verbose = 0; /* Include Bad Formatted Lines */
+ int CWDlen;
+ int COMPILER = COMPILER_DEFAULT;
+
+ getcwd( CWD, sizeof(CWD) );
+ CWDlen = strlen(CWD);
+
+ for (i=1; i<argc; i++)
+ {
+ if (argv[i][0] != '-')
+ return ShowUsage("");
+ switch ( argv[i][1] )
+ {
+ case 'c':
+ dec_col = 1;
+ break;
+ case 'r':
+ dec_row = 1;
+ break;
+ case 'e':
+ echo = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'o':
+ {
+ if (i+1 >= argc)
+ return ShowUsage("Error: Missing parameter for -o\n");
+ i++;
+ COMPILER = -1;
+ for (j=1; j<COMPILER_QTY; j++)
+ if ( (strcmp(argv[i], COMPILER_Names[j][0]) == 0) ||
+ ( (argv[i][0] == '_') &&
+ (strcmp(&argv[i][1], COMPILER_Names[j][0]) == 0) ) )
+ COMPILER = j;
+ if (COMPILER == -1)
+ return ShowUsage("Error: Invalid COMPILER specified\n");
+ }
+ break;
+ case 'h':
+ return ShowUsage("");
+ default:
+ return ShowUsage("Error: Invalid option\n");
+ }
+ }
+ if (COMPILER == 0)
+ return ShowUsage("Error: COMPILER must be specified in this system\n");
+
+ stay = ( echogets(Line, echo) != NULL );
+ prefetch = 0;
+
+ while( stay )
+ {
+ *FileName = 0;
+ Row = 0;
+ Col = 0;
+ Severity = ' ';
+ *Reason = 0;
+ ok = 0;
+ switch (COMPILER)
+ {
+ case COMPILER_GCC:
+ Severity = 'e';
+#ifdef GOTO_FROM_WHERE_INCLUDED
+ rv = sscanf( Line, "In file included from %[^:]:%lu:",
+ FileName, &Row );
+ if ( rv == 2 )
+ {
+ ok = (echogets(Reason, echo) != NULL);
+ }
+ else
+#endif
+ {
+ if ((rv = sscanf( Line, "%[^:]:%lu: warning: %[^\n]",
+ FileName, &Row, Reason ))==3) {
+ Severity = 'w';
+ } else {
+ rv = sscanf( Line, "%[^:]:%lu: %[^\n]",
+ FileName, &Row, Reason );
+ }
+ ok = ( rv == 3 );
+ }
+ Col = (dec_col ? 1 : 0 );
+ break;
+ case COMPILER_AIX:
+ rv = sscanf( Line, "\"%[^\"]\", line %lu.%lu: %*s (%c) %[^\n]",
+ FileName, &Row, &Col, &Severity, Reason );
+ ok = ( rv == 5 );
+ break;
+ case COMPILER_HPUX:
+ rv = sscanf( Line, "cc: \"%[^\"]\", line %lu: %c%*[^:]: %[^\n]",
+ FileName, &Row, &Severity, Reason );
+ ok = ( rv == 4 );
+ Col = (dec_col ? 1 : 0 );
+ break;
+ case COMPILER_SOLARIS:
+ rv = sscanf( Line, "\"%[^\"]\", line %lu: warning: %[^\n]",
+ FileName, &Row, Reason );
+ Severity = 'w';
+ ok = ( rv == 3 );
+ if ( rv != 3 )
+ {
+ rv = sscanf( Line, "\"%[^\"]\", line %lu: %[^\n]",
+ FileName, &Row, Reason );
+ Severity = 'e';
+ ok = ( rv == 3 );
+ }
+ Col = (dec_col ? 1 : 0 );
+ break;
+ case COMPILER_ATT:
+ rv = sscanf( Line, "%c \"%[^\"]\",L%lu/C%lu%*[^:]:%[^\n]",
+ &Severity, FileName, &Row, &Col, Reason );
+ ok = ( rv == 5 );
+
+ if (rv != 5)
+ { rv = sscanf( Line, "%c \"%[^\"]\",L%lu/C%lu: %[^\n]",
+ &Severity, FileName, &Row, &Col, Reason );
+ ok = ( rv == 5 );
+ }
+
+ if (rv != 5)
+ { rv = sscanf( Line, "%c \"%[^\"]\",L%lu: %[^\n]",
+ &Severity, FileName, &Row, Reason );
+ ok = ( rv == 4 );
+ Col = (dec_col ? 1 : 0 );
+ }
+
+ stay = (echogets(Line2, echo) != NULL);
+ while ( stay && (Line2[0] == '|') )
+ { for (p=&Line2[2]; (*p) && (isspace(*p)); p++);
+ strcat( Reason, ": " );
+ strcat( Reason, p );
+ Line2[0] = 0;
+ stay = (echogets(Line2, echo) != NULL);
+ }
+ prefetch = 1;
+ strcpy( Line, Line2 );
+ break;
+ case COMPILER_IRIX:
+ Col = 1;
+ prefetch = 0;
+ rv = 0;
+ ok = 0;
+ if ( !strncmp(Line, "cfe: ", 5) )
+ { p = &Line[5];
+ Severity = tolower(*p);
+ p = strchr( &Line[5], ':' );
+ if (p == NULL)
+ { ok = 0;
+ }
+ else
+ {
+ rv = sscanf( p+2, "%[^:]: %lu: %[^\n]",
+ FileName, &Row, Reason );
+ if (rv != 3)
+ rv = sscanf( p+2, "%[^,], line %lu: %[^\n]",
+ FileName, &Row, Reason );
+ ok = ( rv == 3 );
+ }
+
+ if (ok)
+ { prefetch = 1;
+ stay = (echogets(Line, echo) != NULL);
+ if (Line[0] == ' ')
+ stay = (echogets(Line2, echo) != NULL);
+ if ( (Line2[0] == ' ') &&
+ ( (Line2[1] == '-') || (Line2[1] == '^') ) )
+ { Col = strlen(Line2)-1;
+ prefetch = 0;
+ }
+ else
+ { strcat( Line, "\n" );
+ strcat( Line, Line2 );
+ }
+ }
+ }
+ break;
+ }
+ if (dec_col) Col--;
+ if (dec_row) Row--;
+ if (!ok)
+ {
+ if ( Line[0] == 'g' )
+ p = &Line[1];
+ else
+ p = &Line[0];
+ ok = sscanf( p, "make[%*d]: Entering directory `%[^']",
+ BasePath );
+ if (verbose)
+ printf( "[%u]?%s\n", (unsigned)ok, Line );
+ }
+ else
+ {
+ for (p=Reason; (*p) && (isspace(*p)); p++);
+ if ( BasePath[CWDlen] == 0 )
+ printf( "%s:%lu:%lu:%c:%s\n", FileName, Row, Col, Severity, p );
+ else
+ {
+ printf( "%s/%s:%lu:%lu:%c:%s\n", &BasePath[CWDlen+1], FileName, Row, Col, Severity, p );
+ }
+ }
+ if (!prefetch)
+ stay = ( echogets(Line, echo) != NULL );
+ }
+ return 0;
+}
diff --git a/runtime/tools/ccfilter_README.txt b/runtime/tools/ccfilter_README.txt
new file mode 100644
index 0000000..ea989f2
--- /dev/null
+++ b/runtime/tools/ccfilter_README.txt
@@ -0,0 +1,101 @@
+READ THIS FIRST
+===============
+
+ccfilter is a C program to filter the output of a few compilers to a common
+QuickFix format. It is provided with Vim to make quickfix useful for more
+compilers.
+
+ccfilter WILL FAIL with long lines (more than 2047 bytes).
+
+
+COMPILING AND INSTALLING:
+=========================
+
+To compile ccfilter, you can just do a plain:
+ cc ccfilter.c -o ccfilter
+Though, it may be wise to have your default compiler defined,
+so you would normally compile it with one of the following:
+ cc -D_GCC ccfilter.c -o ccfilter
+ cc -D_AIX ccfilter.c -o ccfilter
+ cc -D_ATT ccfilter.c -o ccfilter
+ cc -D_IRIX ccfilter.c -o ccfilter
+ cc -D_SOLARIS ccfilter.c -o ccfilter
+ cc -D_HPUX ccfilter.c -o ccfilter
+You can then copy ccfilter to its target destination (i.e: /usr/local/bin).
+The man page ccfilter.1 has to be copied to somewhere in your MANPATH,
+under a man1 directory (i.e: /usr/local/man/man1).
+
+
+SUPPORTED COMPILERS/PORTING NOTES:
+==================================
+
+The supported formats for the different compilers are described below:
+In this section, meta-names are used as place-holders in the line
+formats: <FILE> <ROW> <COL> <SEVERITY> <REASON> <>
+The <> denotes ignored text.
+Line formats are delimited by the ^ (caret) symbol.
+
+0) Special case: "gmake directory change" lines:
+ Lines with a format like:
+ ^gmake[<NUM>]: Entering directory `<DIR>'^
+ are used to follow the directory changes during the make process,
+ providing in the <FILE> part, a relative (if possible) directory
+ path to the erroneous file.
+
+
+1) GCC:
+ Recognized lines are of the format:
+ - ^In file included from <FILE>:<ROW>:^
+ Line following this one is used as <REASON>
+ <SEVERITY> is always 'e' (error)
+ <COL> is always '0'
+
+ - ^<FILE>:<ROW>:<REASON>^
+ <SEVERITY> is always 'e' (error)
+ <COL> is always '0'
+
+
+2) AIX:
+ Recognized lines are of the format:
+ - ^"<FILE>", line <ROW>.<COL>: <> (<SEVERITY>) <REASON>",
+
+
+3) HPUX:
+ Recognized lines are of the format:
+ - ^cc: "<FILE>", line <ROW>: <SEVERITY>: <REASON>^
+ <COL> is always '0'
+
+
+4) SOLARIS:
+ Recognized lines are of the format:
+ - ^"<FILE>", line <ROW>: warning: <REASON>^
+ This assumes <SEVERITY> is "W"
+ <COL> is always '0'
+
+ - ^"<FILE>", line <ROW>: <REASON>^
+ This assumes <SEVERITY> is "E"
+ <COL> is always '0'
+
+
+5) ATT / NCR:
+ Recognized lines are of the format:
+ - ^<SEVERITY> "<FILE>",L<ROW>/C<COL><>:<REASON>^
+ or
+ - ^<SEVERITY> "<FILE>",L<ROW>/C<COL>:<REASON>^
+ Following lines beginning with a pipe (|) are continuation
+ lines, and are therefore appended to the <REASON>
+
+ - ^<SEVERITY> "<FILE>",L<ROW>:<REASON>^
+ <COL> is '0'
+ Following lines beginning with a pipe (|) are continuation
+ lines, and are therefore appended to the <REASON>
+
+
+6) SGI-IRIX:
+ Recognized lines are of the format:
+ - ^cfe: <SEVERITY>: <FILE>: <ROW>: <REASON>^
+ or
+ ^cfe: <SEVERITY>: <FILE>, line <ROW>: <REASON>^
+ Following lines beginning with a dash (-) are "column-bar"
+ that end with a caret in the column of the error. These lines
+ are analyzed to generate the <COL>.
diff --git a/runtime/tools/demoserver.py b/runtime/tools/demoserver.py
new file mode 100644
index 0000000..2667aed
--- /dev/null
+++ b/runtime/tools/demoserver.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+#
+# Server that will accept connections from a Vim channel.
+# Run this server and then in Vim you can open the channel:
+# :let handle = ch_open('localhost:8765')
+#
+# Then Vim can send requests to the server:
+# :let response = ch_sendexpr(handle, 'hello!')
+#
+# And you can control Vim by typing a JSON message here, e.g.:
+# ["ex","echo 'hi there'"]
+#
+# There is no prompt, just type a line and press Enter.
+# To exit cleanly type "quit<Enter>".
+#
+# See ":help channel-demo" in Vim.
+#
+# This requires Python 2.6 or later.
+
+from __future__ import print_function
+import json
+import socket
+import sys
+import threading
+
+try:
+ # Python 3
+ import socketserver
+except ImportError:
+ # Python 2
+ import SocketServer as socketserver
+
+thesocket = None
+
+class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
+
+ def handle(self):
+ print("=== socket opened ===")
+ global thesocket
+ thesocket = self.request
+ while True:
+ try:
+ data = self.request.recv(4096).decode('utf-8')
+ except socket.error:
+ print("=== socket error ===")
+ break
+ if data == '':
+ print("=== socket closed ===")
+ break
+ print("received: {0}".format(data))
+ try:
+ decoded = json.loads(data)
+ except ValueError:
+ print("json decoding failed")
+ decoded = [-1, '']
+
+ # Send a response if the sequence number is positive.
+ # Negative numbers are used for "eval" responses.
+ if decoded[0] >= 0:
+ if decoded[1] == 'hello!':
+ response = "got it"
+ id = decoded[0]
+ elif decoded[1] == 'hello channel!':
+ response = "got that"
+ # response is not to a specific message callback but to the
+ # channel callback, need to use ID zero
+ id = 0
+ else:
+ response = "what?"
+ id = decoded[0]
+ encoded = json.dumps([id, response])
+ print("sending {0}".format(encoded))
+ self.request.sendall(encoded.encode('utf-8'))
+ thesocket = None
+
+class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
+ pass
+
+if __name__ == "__main__":
+ HOST, PORT = "localhost", 8765
+
+ server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
+ ip, port = server.server_address
+
+ # Start a thread with the server -- that thread will then start one
+ # more thread for each request
+ server_thread = threading.Thread(target=server.serve_forever)
+
+ # Exit the server thread when the main thread terminates
+ server_thread.daemon = True
+ server_thread.start()
+ print("Server loop running in thread: ", server_thread.name)
+
+ print("Listening on port {0}".format(PORT))
+ while True:
+ typed = sys.stdin.readline()
+ if "quit" in typed:
+ print("Goodbye!")
+ break
+ if thesocket is None:
+ print("No socket yet")
+ else:
+ print("sending {0}".format(typed))
+ thesocket.sendall(typed.encode('utf-8'))
+
+ server.shutdown()
+ server.server_close()
diff --git a/runtime/tools/efm_filter.pl b/runtime/tools/efm_filter.pl
new file mode 100755
index 0000000..1d1a4f3
--- /dev/null
+++ b/runtime/tools/efm_filter.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/env perl
+#
+# This program works as a filter that reads from stdin, copies to
+# stdout *and* creates an error file that can be read by vim.
+#
+# This program has only been tested on SGI, Irix5.3.
+#
+# Written by Ives Aerts in 1996. This little program is not guaranteed
+# to do (or not do) anything at all and can be freely used for
+# whatever purpose you can think of.
+
+$args = @ARGV;
+
+unless ($args == 1) {
+ die("Usage: vimccparse <output filename>\n");
+}
+
+$filename = @ARGV[0];
+open (OUT, ">$filename") || die ("Can't open file: \"$filename\"");
+
+while (<STDIN>) {
+ print;
+ if ( (/"(.*)", line (\d+): (e)rror\((\d+)\):/)
+ || (/"(.*)", line (\d+): (w)arning\((\d+)\):/) ) {
+ $file=$1;
+ $line=$2;
+ $errortype="\u$3";
+ $errornr=$4;
+ chop($errormsg=<STDIN>);
+ $errormsg =~ s/^\s*//;
+ $sourceline=<STDIN>;
+ $column=index(<STDIN>, "^") - 1;
+
+ print OUT "$file>$line:$column:$errortype:$errornr:$errormsg\n";
+ }
+}
+
+close(OUT);
+exit(0);
diff --git a/runtime/tools/efm_filter.txt b/runtime/tools/efm_filter.txt
new file mode 100644
index 0000000..d3f97f4
--- /dev/null
+++ b/runtime/tools/efm_filter.txt
@@ -0,0 +1,31 @@
+[adopted from a message that Ives posted in the Vim mailing list]
+
+Some compilers produce an error message that cannot be handled with
+'errorformat' in Vim. Following is an example of a Perl script that
+translates one error message into something that Vim understands.
+
+
+The compiler that generates this kind of error messages (4 lines):
+
+"/tmp_mnt/cm/src/apertos/MoU/MetaCore/MetaCore/common/src/MetaCoreImp_M.cc",
+line 50: error(3114):
+ identifier "PRIMITIVE_M" is undefined
+ return(ExecuteCore(PRIMITIVE_M,
+
+You can find a small perl program at the end.
+The way I use it is:
+
+:set errorformat=%f>%l:%c:%t:%n:%m
+:set makeprg=clearmake\ -C\ gnu
+:set shellpipe=2>&1\|\ vimccparse
+
+If somebody thinks this is useful: feel free to do whatever you can think
+of with this code.
+
+-Ives
+____________________________________________________________
+Ives Aerts (SW Developer) Sony Telecom Europe
+ives@sonytel.be St.Stevens Woluwestr. 55
+`Death could create most things, B-1130 Brussels, Belgium
+ except for plumbing.' PHONE : +32 2 724 19 67
+ (Soul Music - T.Pratchett) FAX : +32 2 726 26 86
diff --git a/runtime/tools/efm_perl.pl b/runtime/tools/efm_perl.pl
new file mode 100755
index 0000000..1aab2d4
--- /dev/null
+++ b/runtime/tools/efm_perl.pl
@@ -0,0 +1,153 @@
+#!/usr/bin/perl -w
+
+# vimparse.pl - Reformats the error messages of the Perl interpreter for use
+# with the quickfix mode of Vim
+#
+# Copyright (c) 2001 by Joerg Ziefle <joerg.ziefle@gmx.de>
+# You may use and distribute this software under the same terms as Perl itself.
+#
+# Usage: put one of the two configurations below in your ~/.vimrc (without the
+# description and '# ') and enjoy (be sure to adjust the paths to vimparse.pl
+# before):
+#
+# Program is run interactively with 'perl -w':
+#
+# set makeprg=$HOME/bin/vimparse.pl\ %\ $*
+# set errorformat=%f:%l:%m
+#
+# Program is only compiled with 'perl -wc':
+#
+# set makeprg=$HOME/bin/vimparse.pl\ -c\ %\ $*
+# set errorformat=%f:%l:%m
+#
+# Usage:
+# vimparse.pl [-c] [-f <errorfile>] <programfile> [programargs]
+#
+# -c compile only, don't run (perl -wc)
+# -f write errors to <errorfile>
+#
+# Example usages:
+# * From the command line:
+# vimparse.pl program.pl
+#
+# vimparse.pl -c -f errorfile program.pl
+# Then run vim -q errorfile to edit the errors with Vim.
+#
+# * From Vim:
+# Edit in Vim (and save, if you don't have autowrite on), then
+# type ':mak' or ':mak args' (args being the program arguments)
+# to error check.
+#
+# Version history:
+# 0.2 (04/12/2001):
+# * First public version (sent to Bram)
+# * -c command line option for compiling only
+# * grammatical fix: 'There was 1 error.'
+# * bug fix for multiple arguments
+# * more error checks
+# * documentation (top of file, &usage)
+# * minor code clean ups
+# 0.1 (02/02/2001):
+# * Initial version
+# * Basic functionality
+#
+# Todo:
+# * test on more systems
+# * use portable way to determine the location of perl ('use Config')
+# * include option that shows perldiag messages for each error
+# * allow to pass in program by STDIN
+# * more intuitive behaviour if no error is found (show message)
+#
+# Tested under SunOS 5.7 with Perl 5.6.0. Let me know if it's not working for
+# you.
+
+use strict;
+use Getopt::Std;
+
+use vars qw/$opt_c $opt_f $opt_h/; # needed for Getopt in combination with use strict 'vars'
+
+use constant VERSION => 0.2;
+
+getopts('cf:h');
+
+&usage if $opt_h; # not necessarily needed, but good for further extension
+
+if (defined $opt_f) {
+
+ open FILE, "> $opt_f" or do {
+ warn "Couldn't open $opt_f: $!. Using STDOUT instead.\n";
+ undef $opt_f;
+ };
+
+};
+
+my $handle = (defined $opt_f ? \*FILE : \*STDOUT);
+
+(my $file = shift) or &usage; # display usage if no filename is supplied
+my $args = (@ARGV ? ' ' . join ' ', @ARGV : '');
+
+my @lines = `perl @{[defined $opt_c ? '-c ' : '' ]} -w "$file$args" 2>&1`;
+
+my $errors = 0;
+foreach my $line (@lines) {
+
+ chomp($line);
+ my ($file, $lineno, $message, $rest);
+
+ if ($line =~ /^(.*)\sat\s(.*)\sline\s(\d+)(\.|,\snear\s\".*\")$/) {
+
+ ($message, $file, $lineno, $rest) = ($1, $2, $3, $4);
+ $errors++;
+ $message .= $rest if ($rest =~ s/^,//);
+ print $handle "$file:$lineno:$message\n";
+
+ } else { next };
+
+}
+
+if (defined $opt_f) {
+
+ my $msg;
+ if ($errors == 1) {
+
+ $msg = "There was 1 error.\n";
+
+ } else {
+
+ $msg = "There were $errors errors.\n";
+
+ };
+
+ print STDOUT $msg;
+ close FILE;
+ unlink $opt_f unless $errors;
+
+};
+
+sub usage {
+
+ (local $0 = $0) =~ s/^.*\/([^\/]+)$/$1/; # remove path from name of program
+ print<<EOT;
+Usage:
+ $0 [-c] [-f <errorfile>] <programfile> [programargs]
+
+ -c compile only, don't run (executes 'perl -wc')
+ -f write errors to <errorfile>
+
+Examples:
+ * At the command line:
+ $0 program.pl
+ Displays output on STDOUT.
+
+ $0 -c -f errorfile program.pl
+ Then run 'vim -q errorfile' to edit the errors with Vim.
+
+ * In Vim:
+ Edit in Vim (and save, if you don't have autowrite on), then
+ type ':mak' or ':mak args' (args being the program arguments)
+ to error check.
+EOT
+
+ exit 0;
+
+};
diff --git a/runtime/tools/emoji_list.vim b/runtime/tools/emoji_list.vim
new file mode 100644
index 0000000..d361b7e
--- /dev/null
+++ b/runtime/tools/emoji_list.vim
@@ -0,0 +1,23 @@
+" Script to fill the window with emoji characters, one per line.
+" Source this script: :source %
+
+if &modified
+ new
+else
+ enew
+endif
+
+" Use a compiled Vim9 function for speed
+def DoIt()
+ var lnum = 1
+ for c in range(0x100, 0x1ffff)
+ var cs = nr2char(c)
+ if charclass(cs) == 3
+ setline(lnum, '|' .. cs .. '| ' .. strwidth(cs))
+ lnum += 1
+ endif
+ endfor
+enddef
+
+call DoIt()
+set nomodified
diff --git a/runtime/tools/mve.awk b/runtime/tools/mve.awk
new file mode 100755
index 0000000..396f806
--- /dev/null
+++ b/runtime/tools/mve.awk
@@ -0,0 +1,23 @@
+#!/usr/bin/nawk -f
+#
+# Change "nawk" to "awk" or "gawk" if you get errors.
+#
+# Make Vim Errors
+# Processes errors from cc for use by Vim's quick fix tools
+# specifically it translates the ---------^ notation to a
+# column number
+#
+BEGIN { FS="[:,]" }
+
+/^cfe/ { file=$3
+ msg=$5
+ split($4,s," ")
+ line=s[2]
+}
+
+# You may have to substitute a tab character for the \t here:
+/^[\t-]*\^/ {
+ p=match($0, ".*\\^" )
+ col=RLENGTH-2
+ printf("%s, line %d, col %d : %s\n", file,line,col,msg)
+}
diff --git a/runtime/tools/mve.txt b/runtime/tools/mve.txt
new file mode 100644
index 0000000..8aa5cf6
--- /dev/null
+++ b/runtime/tools/mve.txt
@@ -0,0 +1,20 @@
+[ The mve awk script was posted on the vimdev mailing list ]
+
+From: jimmer@barney.mdhc.mdc.com (J. McGlasson)
+Date: Mon, 31 Mar 1997 13:16:49 -0700 (Mar)
+
+My compiler (SGI MIPSpro C compiler - IRIX 6.4) works like this.
+I have written a script mve (make vim errors), through which I pipe my make
+output, which translates output of the following form:
+
+cfe: Error: syntax.c, line 4: Syntax Error
+ int i[12;
+ ------------^
+
+into:
+
+ cl.c, line 4, col 12 : Syntax Error
+
+(in vim notation: %f, line %l, col %c : %m)
+
+You might be able to tailor this for your compiler's output.
diff --git a/runtime/tools/pltags.pl b/runtime/tools/pltags.pl
new file mode 100755
index 0000000..7a74682
--- /dev/null
+++ b/runtime/tools/pltags.pl
@@ -0,0 +1,300 @@
+#!/usr/bin/env perl
+
+# pltags - create a tags file for Perl code, for use by vi(m)
+#
+# Distributed with Vim <http://www.vim.org/>, latest version always available
+# at <http://www.mscha.com/mscha.html?pltags#tools>
+#
+# Version 2.3, 28 February 2002
+#
+# Written by Michael Schaap <pltags@mscha.com>. Suggestions for improvement
+# are very welcome!
+#
+# This script will not work with Perl 4 or below!
+#
+# Revision history:
+# 1.0 1997? Original version, quickly hacked together
+# 2.0 1999? Completely rewritten, better structured and documented,
+# support for variables, packages, Exuberant Ctags extensions
+# 2.1 Jun 2000 Fixed critical bug (typo in comment) ;-)
+# Support multiple level packages (e.g. Archive::Zip::Member)
+# 2.2 Jul 2001 'Glob' wildcards - especially useful under Windows
+# (thanks to Serge Sivkov and Jason King)
+# Bug fix: reset package name for each file
+# 2.21 Jul 2001 Oops... bug in variable detection (/local../ -> /^local.../)
+# 2.3 Feb 2002 Support variables declared with "our"
+# (thanks to Lutz Mende)
+
+# Complain about undeclared variables
+use strict;
+
+# Used modules
+use Getopt::Long;
+
+# Options with their defaults
+my $do_subs = 1; # --subs, --nosubs include subs in tags file?
+my $do_vars = 1; # --vars, --novars include variables in tags file?
+my $do_pkgs = 1; # --pkgs, --nopkgs include packages in tags file?
+my $do_exts = 1; # --extensions, --noextensions
+ # include Exuberant Ctags extensions
+
+# Global variables
+my $VERSION = "2.21"; # pltags version
+my $status = 0; # GetOptions return value
+my $file = ""; # File being processed
+my @tags = (); # List of produced tags
+my $is_pkg = 0; # Are we tagging a package?
+my $has_subs = 0; # Has this file any subs yet?
+my $package_name = ""; # Name of current package
+my $var_continues = 0; # Variable declaration continues on last line
+my $line = ""; # Current line in file
+my $stmt = ""; # Current Perl statement
+my @vars = (); # List of variables in declaration
+my $var = ""; # Variable in declaration
+my $tagline = ""; # Tag file line
+
+# Create a tag file line and push it on the list of found tags
+sub MakeTag($$$$$)
+{
+ my ($tag, # Tag name
+ $type, # Type of tag
+ $is_static, # Is this a static tag?
+ $file, # File in which tag appears
+ $line) = @_; # Line in which tag appears
+
+ my $tagline = ""; # Created tag line
+
+ # Only process tag if not empty
+ if ($tag)
+ {
+ # Get rid of \n, and escape / and \ in line
+ chomp $line;
+ $line =~ s/\\/\\\\/g;
+ $line =~ s/\//\\\//g;
+
+ # Create a tag line
+ $tagline = "$tag\t$file\t/^$line\$/";
+
+ # If we're told to do so, add extensions
+ if ($do_exts)
+ {
+ $tagline .= ";\"\t$type"
+ . ($is_static ? "\tfile:" : "")
+ . ($package_name ? "\tclass:$package_name" : "");
+ }
+
+ # Push it on the stack
+ push (@tags, $tagline);
+ }
+}
+
+# Parse package name from statement
+sub PackageName($)
+{
+ my ($stmt) = @_; # Statement
+
+ # Look for the argument to "package". Return it if found, else return ""
+ if ($stmt =~ /^package\s+([\w:]+)/)
+ {
+ my $pkgname = $1;
+
+ # Remove any parent package name(s)
+ $pkgname =~ s/.*://;
+ return $pkgname;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+# Parse sub name from statement
+sub SubName($)
+{
+ my ($stmt) = @_; # Statement
+
+ # Look for the argument to "sub". Return it if found, else return ""
+ if ($stmt =~ /^sub\s+([\w:]+)/)
+ {
+ my $subname = $1;
+
+ # Remove any parent package name(s)
+ $subname =~ s/.*://;
+ return $subname;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+# Parse all variable names from statement
+sub VarNames($)
+{
+ my ($stmt) = @_;
+
+ # Remove my or local from statement, if present
+ $stmt =~ s/^(my|our|local)\s+//;
+
+ # Remove any assignment piece
+ $stmt =~ s/\s*=.*//;
+
+ # Now find all variable names, i.e. "words" preceded by $, @ or %
+ @vars = ($stmt =~ /[\$\@\%]([\w:]+)\b/g);
+
+ # Remove any parent package name(s)
+ map(s/.*://, @vars);
+
+ return (@vars);
+}
+
+############### Start ###############
+
+print "\npltags $VERSION by Michael Schaap <mscha\@mscha.com>\n\n";
+
+# Get options
+$status = GetOptions("subs!" => \$do_subs,
+ "vars!" => \$do_vars,
+ "pkgs!" => \$do_pkgs,
+ "extensions!" => \$do_exts);
+
+# Usage if error in options or no arguments given
+unless ($status && @ARGV)
+{
+ print "\n" unless ($status);
+ print " Usage: $0 [options] filename ...\n\n";
+ print " Where options can be:\n";
+ print " --subs (--nosubs) (don't) include sub declarations in tag file\n";
+ print " --vars (--novars) (don't) include variable declarations in tag file\n";
+ print " --pkgs (--nopkgs) (don't) include package declarations in tag file\n";
+ print " --extensions (--noextensions)\n";
+ print " (don't) include Exuberant Ctags / Vim style\n";
+ print " extensions in tag file\n\n";
+ print " Default options: ";
+ print ($do_subs ? "--subs " : "--nosubs ");
+ print ($do_vars ? "--vars " : "--novars ");
+ print ($do_pkgs ? "--pkgs " : "--nopkgs ");
+ print ($do_exts ? "--extensions\n\n" : "--noextensions\n\n");
+ print " Example: $0 *.pl *.pm ../shared/*.pm\n\n";
+ exit;
+}
+
+# Loop through files on command line - 'glob' any wildcards, since Windows
+# doesn't do this for us
+foreach $file (map { glob } @ARGV)
+{
+ # Skip if this is not a file we can open. Also skip tags files and backup
+ # files
+ next unless ((-f $file) && (-r $file) && ($file !~ /tags$/)
+ && ($file !~ /~$/));
+
+ print "Tagging file $file...\n";
+
+ $is_pkg = 0;
+ $package_name = "";
+ $has_subs = 0;
+ $var_continues = 0;
+
+ open (IN, $file) or die "Can't open file '$file': $!";
+
+ # Loop through file
+ foreach $line (<IN>)
+ {
+ # Statement is line with comments and whitespace trimmed
+ ($stmt = $line) =~ s/#.*//;
+ $stmt =~ s/^\s*//;
+ $stmt =~ s/\s*$//;
+
+ # Nothing left? Never mind.
+ next unless ($stmt);
+
+ # This is a variable declaration if one was started on the previous
+ # line, or if this line starts with my or local
+ if ($var_continues or ($stmt =~/^my\b/)
+ or ($stmt =~/^our\b/) or ($stmt =~/^local\b/))
+ {
+ # The declaration continues if the line does not end with ;
+ $var_continues = ($stmt !~ /;$/);
+
+ # Loop through all variable names in the declaration
+ foreach $var (VarNames($stmt))
+ {
+ # Make a tag for this variable unless we're told not to. We
+ # assume that a variable is always static, unless it appears
+ # in a package before any sub. (Not necessarily true, but
+ # it's ok for most purposes and Vim works fine even if it is
+ # incorrect)
+ if ($do_vars)
+ {
+ MakeTag($var, "v", (!$is_pkg or $has_subs), $file, $line);
+ }
+ }
+ }
+
+ # This is a package declaration if the line starts with package
+ elsif ($stmt =~/^package\b/)
+ {
+ # Get name of the package
+ $package_name = PackageName($stmt);
+
+ if ($package_name)
+ {
+ # Remember that we're doing a package
+ $is_pkg = 1;
+
+ # Make a tag for this package unless we're told not to. A
+ # package is never static.
+ if ($do_pkgs)
+ {
+ MakeTag($package_name, "p", 0, $file, $line);
+ }
+ }
+ }
+
+ # This is a sub declaration if the line starts with sub
+ elsif ($stmt =~/^sub\b/)
+ {
+ # Remember that this file has subs
+ $has_subs = 1;
+
+ # Make a tag for this sub unless we're told not to. We assume
+ # that a sub is static, unless it appears in a package. (Not
+ # necessarily true, but it's ok for most purposes and Vim works
+ # fine even if it is incorrect)
+ if ($do_subs)
+ {
+ MakeTag(SubName($stmt), "s", (!$is_pkg), $file, $line);
+ }
+ }
+ }
+ close (IN);
+}
+
+# Do we have any tags? If so, write them to the tags file
+if (@tags)
+{
+ # Add some tag file extensions if we're told to
+ if ($do_exts)
+ {
+ push (@tags, "!_TAG_FILE_FORMAT\t2\t/extended format/");
+ push (@tags, "!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted/");
+ push (@tags, "!_TAG_PROGRAM_AUTHOR\tMichael Schaap\t/mscha\@mscha.com/");
+ push (@tags, "!_TAG_PROGRAM_NAME\tpltags\t//");
+ push (@tags, "!_TAG_PROGRAM_VERSION\t$VERSION\t/supports multiple tags and extended format/");
+ }
+
+ print "\nWriting tags file.\n";
+
+ open (OUT, ">tags") or die "Can't open tags file: $!";
+
+ foreach $tagline (sort @tags)
+ {
+ print OUT "$tagline\n";
+ }
+
+ close (OUT);
+}
+else
+{
+ print "\nNo tags found.\n";
+}
diff --git a/runtime/tools/ref b/runtime/tools/ref
new file mode 100755
index 0000000..77bfc80
--- /dev/null
+++ b/runtime/tools/ref
@@ -0,0 +1,11 @@
+#!/bin/sh
+#
+# ref - Check spelling of the arguments
+#
+# Usage: ref word ..
+#
+# can be used for the K command of Vim
+#
+spell <<EOF
+$*
+EOF
diff --git a/runtime/tools/shtags.1 b/runtime/tools/shtags.1
new file mode 100644
index 0000000..0a13802
--- /dev/null
+++ b/runtime/tools/shtags.1
@@ -0,0 +1,61 @@
+.TH shtags 1 "local Utilities"
+.SH NAME
+shtags \- Create tags for shell scripts
+.SH SYNOPSIS
+.B shtags
+[\fI-mvw\fP] [\fI-t <file>\fP] [\fI-s <shell>\fP] <files>
+.SH DESCRIPTION
+\fBshtags\fP creates a \fBvi(1)\fP tags file for shell scripts - which
+essentially turns your code into a hypertext document. \fBshtags\fP
+attempts to create tags for all function and variable definitions,
+although this is a little difficult, because in most shell languages,
+variables don't need to be explicitly defined, and as such there is
+often no distinct "variable definition". If this is the case,
+\fBshtags\fP simply creates a tag for the first instance of a variable
+which is being set in a simple way, ie: \fIset x = 5\fP.
+.SH OPTIONS
+.IP "\fB-t <file>\fP"
+Name of tags file to create. (default is 'tags')
+.IP "\fB-s <shell>\fP"
+The name of the shell used by the script(s). By default,
+\fBshtags\fP tries to work out which is the appropriate shell for each
+file individually by looking at the first line of each file. This won't
+work however, if the script starts as a bourne shell script and tries
+to be clever about starting the shell it really wants.
+.b
+Currently supported shells are:
+.RS
+.IP \fBsh\fP
+Bourne Shell
+.IP \fBperl\fP
+Perl (versions 4 and 5)
+.IP \fBksh\fP
+Korn Shell
+.IP \fBtclsh\fP
+The TCL shell
+.IP \fBwish\fP
+The TK Windowing shell (same as tclsh)
+.RE
+
+.IP \fB-v\fP
+Include variable definitions (variables mentioned at the start of a line)
+.IP \fB-V\fP
+Print version information.
+.IP \fB-w\fP
+Suppress "duplicate tag" warning messages.
+.IP \fB-x\fP
+Explicitly create a new tags file. Normally new tags are merged with
+the old tags file.
+.PP
+\fBshtags\fP scans the specified files for subroutines and possibly
+variable definitions, and creates a \fBvi\fP style tags file.
+.SH FILES
+.IP \fBtags\fP
+A tags file contains a sorted list of tags, one tag per line. The
+format is the same as that used by \fBvi\fP(1)
+.SH AUTHOR
+Stephen Riehm
+.br
+sr@pc-plus.de
+.SH "SEE ALSO"
+ctags(1), etags(1), perl(1), tclsh(1), wish(1), sh(1), ksh(1).
diff --git a/runtime/tools/shtags.pl b/runtime/tools/shtags.pl
new file mode 100755
index 0000000..49a469a
--- /dev/null
+++ b/runtime/tools/shtags.pl
@@ -0,0 +1,144 @@
+#!/usr/bin/env perl
+#
+# shtags: create a tags file for perl scripts
+#
+# Author: Stephen Riehm
+# Updated by: David Woodfall <dave@dawoodfall.net>
+# Last Changed: 2018/04/02
+#
+
+use Getopt::Std;
+
+# obvious... :-)
+sub usage
+ {
+ print <<_EOUSAGE_ ;
+USAGE: $program [-kvwVx] [-t <file>] <files>
+ -t <file> Name of tags file to create. (default is 'tags')
+ -s <shell> Name of the shell language in the script
+ -v Include variable definitions.
+ (variables mentioned at the start of a line)
+ -V Print version information.
+ -w Suppress "duplicate tag" warnings.
+ -x Explicitly create a new tags file. Normally tags are merged.
+ <files> List of files to scan for tags.
+_EOUSAGE_
+ exit 0
+ }
+
+sub version
+{
+ #
+ # Version information
+ #
+ @id = split( ', ', 'scripts/bin/shtags, /usr/local/, LOCAL_SCRIPTS, 1.2, 18/04/02, 07:37' );
+ $id[0] =~ s,.*/,,;
+ print <<_EOVERS;
+$id[0]: $id[3]
+Last Modified: @id[4,5]
+Component: $id[1]
+Release: $id[2]
+_EOVERS
+ exit( 1 );
+}
+
+#
+# initialisations
+#
+($program = $0) =~ s,.*/,,;
+
+#
+# parse command line
+#
+getopts( "t:s:vVwx" ) || &usage();
+$tags_file = $opt_t || 'tags';
+$explicit = $opt_x;
+$variable_tags = $opt_v;
+$allow_warnings = ! $opt_w;
+&version if $opt_V;
+&usage() unless @ARGV != 0;
+
+# slurp up the existing tags. Some will be replaced, the ones that aren't
+# will be re-written exactly as they were read
+if( ! $explicit && open( TAGS, "< $tags_file" ) )
+ {
+ while( <TAGS> )
+ {
+ /^\S+/;
+ $tags{$&} = $_;
+ }
+ close( TAGS );
+ }
+
+#
+# for each line of every file listed on the command line, look for a
+# 'sub' definition, or, if variables are wanted as well, look for a
+# variable definition at the start of a line
+#
+while( <> )
+ {
+ &check_shell($_), ( $old_file = $ARGV ) if $ARGV ne $old_file;
+ next unless $shell;
+ if( $shell eq "sh" )
+ {
+ next unless /^\s*(((\w+)))\s*\(\s*\)/
+ || ( $variable_tags && /^(((\w+)=))/ );
+ $match = $3;
+ }
+ if( $shell eq "ksh" )
+ {
+ # ksh
+ next unless /^\s*function\s+(((\w+)))/
+ || ( $variable_tags && /^(((\w+)=))/ );
+ $match = $3;
+ }
+ if( $shell eq "perl" )
+ {
+ # perl
+ next unless /^\s*sub\s+(\w+('|::))?(\w+)/
+ || /^\s*(((\w+))):/
+ || ( $variable_tags && /^(([(\s]*[\$\@\%]{1}(\w+).*=))/ );
+ $match = $3;
+ }
+ if( $shell eq "tcl" )
+ {
+ next unless /^\s*proc\s+(((\S+)))/
+ || ( $variable_tags && /^\s*set\s+(((\w+)\s))/ );
+ $match = $3;
+ }
+ chop;
+ warn "$match - duplicate ignored\n"
+ if ( $new{$match}++
+ || !( $tags{$match} = sprintf( "%s\t%s\t?^%s\$?\n", $match, $ARGV, $_ ) ) )
+ && $allow_warnings;
+ }
+
+# write the new tags to the tags file - note that the whole file is rewritten
+open( TAGS, "> $tags_file" );
+foreach( sort( keys %tags ) )
+ {
+ print TAGS "$tags{$_}";
+ }
+close( TAGS );
+
+sub check_shell
+ {
+ local( $_ ) = @_;
+ # read the first line of a script, and work out which shell it is,
+ # unless a shell was specified on the command line
+ #
+ # This routine can't handle clever scripts which start sh and then
+ # use sh to start the shell they really wanted.
+ if( $opt_s )
+ {
+ $shell = $opt_s;
+ }
+ else
+ {
+ $shell = "sh" if /^:$/ || /^#!.*\/bin\/sh/;
+ $shell = "ksh" if /^#!.*\/ksh/;
+ $shell = "perl" if /^#!.*\/perl/;
+ $shell = "tcl" if /^#!.*\/wish/;
+ printf "Using $shell for $ARGV\n";
+ }
+ }
diff --git a/runtime/tools/unicode.vim b/runtime/tools/unicode.vim
new file mode 100644
index 0000000..630a581
--- /dev/null
+++ b/runtime/tools/unicode.vim
@@ -0,0 +1,471 @@
+" Script to extract tables from Unicode .txt files, to be used in src/mbyte.c.
+" The format of the UnicodeData.txt file is explained here:
+" http://www.unicode.org/Public/5.1.0/ucd/UCD.html
+" For the other files see the header.
+"
+" Might need to update the URL to the emoji-data.txt
+" Usage: Vim -S <this-file>
+"
+" Author: Bram Moolenaar
+" Last Update: 2020 Aug 24
+
+" Parse lines of UnicodeData.txt. Creates a list of lists in s:dataprops.
+func! ParseDataToProps()
+ let s:dataprops = []
+ let lnum = 1
+ while lnum <= line('$')
+ let l = split(getline(lnum), '\s*;\s*', 1)
+ if len(l) != 15
+ echoerr 'Found ' . len(l) . ' items in line ' . lnum . ', expected 15'
+ return
+ endif
+ call add(s:dataprops, l)
+ let lnum += 1
+ endwhile
+endfunc
+
+" Parse lines of CaseFolding.txt. Creates a list of lists in s:foldprops.
+func! ParseFoldProps()
+ let s:foldprops = []
+ let lnum = 1
+ while lnum <= line('$')
+ let line = getline(lnum)
+ if line !~ '^#' && line !~ '^\s*$'
+ let l = split(line, '\s*;\s*', 1)
+ if len(l) != 4
+ echoerr 'Found ' . len(l) . ' items in line ' . lnum . ', expected 4'
+ return
+ endif
+ call add(s:foldprops, l)
+ endif
+ let lnum += 1
+ endwhile
+endfunc
+
+" Parse lines of EastAsianWidth.txt. Creates a list of lists in s:widthprops.
+func! ParseWidthProps()
+ let s:widthprops = []
+ let lnum = 1
+ while lnum <= line('$')
+ let line = getline(lnum)
+ if line !~ '^#' && line !~ '^\s*$'
+ let l = split(line, '\s*;\s*', 1)
+ if len(l) != 2
+ echoerr 'Found ' . len(l) . ' items in line ' . lnum . ', expected 2'
+ return
+ endif
+ call add(s:widthprops, l)
+ endif
+ let lnum += 1
+ endwhile
+endfunc
+
+" Build the toLower or toUpper table in a new buffer.
+" Uses s:dataprops.
+func! BuildCaseTable(name, index)
+ let start = -1
+ let end = -1
+ let step = 0
+ let add = -1
+ let ranges = []
+ for p in s:dataprops
+ if p[a:index] != ''
+ let n = ('0x' . p[0]) + 0
+ let nl = ('0x' . p[a:index]) + 0
+ if start >= 0 && add == nl - n && (step == 0 || n - end == step)
+ " continue with same range.
+ let step = n - end
+ let end = n
+ else
+ if start >= 0
+ " produce previous range
+ call Range(ranges, start, end, step, add)
+ endif
+ let start = n
+ let end = n
+ let step = 0
+ let add = nl - n
+ endif
+ endif
+ endfor
+ if start >= 0
+ call Range(ranges, start, end, step, add)
+ endif
+
+ " New buffer to put the result in.
+ new
+ exe "file to" . a:name
+ call setline(1, "static convertStruct to" . a:name . "[] =")
+ call setline(2, "{")
+ call append('$', ranges)
+ call setline('$', getline('$')[:-2]) " remove last comma
+ call setline(line('$') + 1, "};")
+ wincmd p
+endfunc
+
+" Build the foldCase table in a new buffer.
+" Uses s:foldprops.
+func! BuildFoldTable()
+ let start = -1
+ let end = -1
+ let step = 0
+ let add = -1
+ let ranges = []
+ for p in s:foldprops
+ if p[1] == 'C' || p[1] == 'S'
+ let n = ('0x' . p[0]) + 0
+ let nl = ('0x' . p[2]) + 0
+ if start >= 0 && add == nl - n && (step == 0 || n - end == step)
+ " continue with same range.
+ let step = n - end
+ let end = n
+ else
+ if start >= 0
+ " produce previous range
+ call Range(ranges, start, end, step, add)
+ endif
+ let start = n
+ let end = n
+ let step = 0
+ let add = nl - n
+ endif
+ endif
+ endfor
+ if start >= 0
+ call Range(ranges, start, end, step, add)
+ endif
+
+ " New buffer to put the result in.
+ new
+ file foldCase
+ call setline(1, "static convertStruct foldCase[] =")
+ call setline(2, "{")
+ call append('$', ranges)
+ call setline('$', getline('$')[:-2]) " remove last comma
+ call setline(line('$') + 1, "};")
+ wincmd p
+endfunc
+
+func! Range(ranges, start, end, step, add)
+ let s = printf("\t{0x%x,0x%x,%d,%d},", a:start, a:end, a:step == 0 ? -1 : a:step, a:add)
+ call add(a:ranges, s)
+endfunc
+
+" Build the combining table.
+" Uses s:dataprops.
+func! BuildCombiningTable()
+ let start = -1
+ let end = -1
+ let ranges = []
+ for p in s:dataprops
+ " The 'Mc' property was removed, it does take up space.
+ if p[2] == 'Mn' || p[2] == 'Me'
+ let n = ('0x' . p[0]) + 0
+ if start >= 0 && end + 1 == n
+ " continue with same range.
+ let end = n
+ else
+ if start >= 0
+ " produce previous range
+ call add(ranges, printf("\t{0x%04x, 0x%04x},", start, end))
+ endif
+ let start = n
+ let end = n
+ endif
+ endif
+ endfor
+ if start >= 0
+ call add(ranges, printf("\t{0x%04x, 0x%04x},", start, end))
+ endif
+
+ " New buffer to put the result in.
+ new
+ file combining
+ call setline(1, " static struct interval combining[] =")
+ call setline(2, " {")
+ call append('$', ranges)
+ call setline('$', getline('$')[:-2]) " remove last comma
+ call setline(line('$') + 1, " };")
+ wincmd p
+endfunc
+
+" Build the double width or ambiguous width table in a new buffer.
+" Uses s:widthprops and s:dataprops.
+func! BuildWidthTable(pattern, tableName)
+ let start = -1
+ let end = -1
+ let ranges = []
+ let dataidx = 0
+ " Account for indentation differences between ambiguous and doublewidth
+ " table in mbyte.c
+ if a:pattern == 'A'
+ let spc = ' '
+ else
+ let spc = "\t"
+ endif
+ for p in s:widthprops
+ if p[1][0] =~ a:pattern
+ if p[0] =~ '\.\.'
+ " It is a range. we don't check for composing char then.
+ let rng = split(p[0], '\.\.')
+ if len(rng) != 2
+ echoerr "Cannot parse range: '" . p[0] . "' in width table"
+ endif
+ let n = ('0x' . rng[0]) + 0
+ let n_last = ('0x' . rng[1]) + 0
+ else
+ let n = ('0x' . p[0]) + 0
+ let n_last = n
+ endif
+ " Find this char in the data table.
+ while 1
+ let dn = ('0x' . s:dataprops[dataidx][0]) + 0
+ if dn >= n
+ break
+ endif
+ let dataidx += 1
+ endwhile
+ if dn != n && n_last == n
+ echoerr "Cannot find character " . n . " in data table"
+ endif
+ " Only use the char when it's not a composing char.
+ " But use all chars from a range.
+ let dp = s:dataprops[dataidx]
+ if n_last > n || (dp[2] != 'Mn' && dp[2] != 'Mc' && dp[2] != 'Me')
+ if start >= 0 && end + 1 == n
+ " continue with same range.
+ else
+ if start >= 0
+ " produce previous range
+ call add(ranges, printf("%s{0x%04x, 0x%04x},", spc, start, end))
+ if a:pattern == 'A'
+ call add(s:ambitable, [start, end])
+ else
+ call add(s:doubletable, [start, end])
+ endif
+ endif
+ let start = n
+ endif
+ let end = n_last
+ endif
+ endif
+ endfor
+ if start >= 0
+ call add(ranges, printf("%s{0x%04x, 0x%04x},", spc, start, end))
+ if a:pattern == 'A'
+ call add(s:ambitable, [start, end])
+ else
+ call add(s:doubletable, [start, end])
+ endif
+ endif
+
+ " New buffer to put the result in.
+ new
+ exe "file " . a:tableName
+ if a:pattern == 'A'
+ call setline(1, "static struct interval " . a:tableName . "[] =")
+ call setline(2, "{")
+ else
+ call setline(1, " static struct interval " . a:tableName . "[] =")
+ call setline(2, " {")
+ endif
+ call append('$', ranges)
+ call setline('$', getline('$')[:-2]) " remove last comma
+ if a:pattern == 'A'
+ call setline(line('$') + 1, "};")
+ else
+ call setline(line('$') + 1, " };")
+ endif
+ wincmd p
+endfunc
+
+
+" Get characters from a list of lines in form "12ab .." or "12ab..56cd ..."
+" and put them in dictionary "chardict"
+func AddLinesToCharDict(lines, chardict)
+ for line in a:lines
+ let tokens = split(line, '\.\.')
+ let first = str2nr(tokens[0], 16)
+ if len(tokens) == 1
+ let last = first
+ else
+ let last = str2nr(tokens[1], 16)
+ endif
+ for nr in range(first, last)
+ let a:chardict[nr] = 1
+ endfor
+ endfor
+endfunc
+
+func Test_AddLinesToCharDict()
+ let dict = {}
+ call AddLinesToCharDict([
+ \ '1234 blah blah',
+ \ '1235 blah blah',
+ \ '12a0..12a2 blah blah',
+ \ '12a1 blah blah',
+ \ ], dict)
+ call assert_equal({0x1234: 1, 0x1235: 1,
+ \ 0x12a0: 1, 0x12a1: 1, 0x12a2: 1,
+ \ }, dict)
+ if v:errors != []
+ echoerr 'AddLinesToCharDict' v:errors
+ return 1
+ endif
+ return 0
+endfunc
+
+
+func CharDictToPairList(chardict)
+ let result = []
+ let keys = keys(a:chardict)->map('str2nr(v:val)')->sort('N')
+ let low = keys[0]
+ let high = keys[0]
+ for key in keys
+ if key > high + 1
+ call add(result, [low, high])
+ let low = key
+ let high = key
+ else
+ let high = key
+ endif
+ endfor
+ call add(result, [low, high])
+ return result
+endfunc
+
+func Test_CharDictToPairList()
+ let dict = {0x1020: 1, 0x1021: 1, 0x1022: 1,
+ \ 0x1024: 1,
+ \ 0x2022: 1,
+ \ 0x2024: 1, 0x2025: 1}
+ call assert_equal([
+ \ [0x1020, 0x1022],
+ \ [0x1024, 0x1024],
+ \ [0x2022, 0x2022],
+ \ [0x2024, 0x2025],
+ \ ], CharDictToPairList(dict))
+ if v:errors != []
+ echoerr 'CharDictToPairList' v:errors
+ return 1
+ endif
+ return 0
+endfunc
+
+
+" Build the amoji width table in a new buffer.
+func BuildEmojiTable()
+ " First make the table for all emojis.
+ let pattern = '; Emoji\s\+#\s'
+ let lines = map(filter(filter(getline(1, '$'), 'v:val=~"^[1-9]"'), 'v:val=~pattern'), 'matchstr(v:val,"^\\S\\+")')
+
+ " Make a dictionary with an entry for each character.
+ let chardict = {}
+ call AddLinesToCharDict(lines, chardict)
+ let pairlist = CharDictToPairList(chardict)
+ let allranges = map(pairlist, 'printf(" {0x%04x, 0x%04x},", v:val[0], v:val[1])')
+
+ " New buffer to put the result in.
+ new
+ exe 'file emoji_all'
+ call setline(1, "static struct interval emoji_all[] =")
+ call setline(2, "{")
+ call append('$', allranges)
+ call setline('$', getline('$')[:-2]) " remove last comma
+ call setline(line('$') + 1, "};")
+ wincmd p
+
+ " Make the table for wide emojis.
+ let pattern = '; Emoji_\(Presentation\|Modifier_Base\)\s\+#\s'
+ let lines = map(filter(filter(getline(1, '$'), 'v:val=~"^[1-9]"'), 'v:val=~pattern'), 'matchstr(v:val,"^\\S\\+")')
+
+ " Make a dictionary with an entry for each character.
+ let chardict = {}
+ call AddLinesToCharDict(lines, chardict)
+
+ " exclude characters that are in the "ambiguous" or "doublewidth" table
+ for ambi in s:ambitable
+ for nr in range(ambi[0], ambi[1])
+ if has_key(chardict, nr)
+ call remove(chardict, nr)
+ endif
+ endfor
+ endfor
+
+ for wide in s:doubletable
+ for nr in range(wide[0], wide[1])
+ if has_key(chardict, nr)
+ call remove(chardict, nr)
+ endif
+ endfor
+ endfor
+
+ let pairlist = CharDictToPairList(chardict)
+ let wide_ranges = map(pairlist, 'printf("\t{0x%04x, 0x%04x},", v:val[0], v:val[1])')
+
+ " New buffer to put the result in.
+ new
+ exe 'file emoji_wide'
+ call setline(1, " static struct interval emoji_wide[] =")
+ call setline(2, " {")
+ call append('$', wide_ranges)
+ call setline('$', getline('$')[:-2]) " remove last comma
+ call setline(line('$') + 1, " };")
+ wincmd p
+endfunc
+
+" First test a few things
+let v:errors = []
+if Test_AddLinesToCharDict() || Test_CharDictToPairList()
+ finish
+endif
+
+
+" Try to avoid hitting E36
+set equalalways
+
+" Edit the Unicode text file. Requires the netrw plugin.
+edit http://unicode.org/Public/UNIDATA/UnicodeData.txt
+
+" Parse each line, create a list of lists.
+call ParseDataToProps()
+
+" Build the toLower table.
+call BuildCaseTable("Lower", 13)
+
+" Build the toUpper table.
+call BuildCaseTable("Upper", 12)
+
+" Build the ranges of composing chars.
+call BuildCombiningTable()
+
+" Edit the case folding text file. Requires the netrw plugin.
+edit http://www.unicode.org/Public/UNIDATA/CaseFolding.txt
+
+" Parse each line, create a list of lists.
+call ParseFoldProps()
+
+" Build the foldCase table.
+call BuildFoldTable()
+
+" Edit the width text file. Requires the netrw plugin.
+edit http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
+
+" Parse each line, create a list of lists.
+call ParseWidthProps()
+
+" Build the double width table.
+let s:doubletable = []
+call BuildWidthTable('[WF]', 'doublewidth')
+
+" Build the ambiguous width table.
+let s:ambitable = []
+call BuildWidthTable('A', 'ambiguous')
+
+" Edit the emoji text file. Requires the netrw plugin.
+" commented out, because it drops too many characters
+"edit https://unicode.org/Public/15.0.0/ucd/emoji/emoji-data.txt
+"
+"" Build the emoji table. Ver. 1.0 - 6.0
+"" Must come after the "ambiguous" and "doublewidth" tables
+"call BuildEmojiTable()
diff --git a/runtime/tools/vim132 b/runtime/tools/vim132
new file mode 100755
index 0000000..29ea4ce
--- /dev/null
+++ b/runtime/tools/vim132
@@ -0,0 +1,13 @@
+#!/bin/csh
+#
+# Shell script for use with UNIX
+# Starts up Vim with the terminal in 132 column mode
+# Only works on VT-100 terminals and lookalikes
+# You need to have a termcap entry "vt100-w". Same as vt100 but 132 columns.
+#
+set oldterm=$term
+echo "[?3h"
+setenv TERM vt100-w
+vim $*
+set term=$oldterm
+echo "[?3l"
diff --git a/runtime/tools/vim_vs_net.cmd b/runtime/tools/vim_vs_net.cmd
new file mode 100644
index 0000000..335236c
--- /dev/null
+++ b/runtime/tools/vim_vs_net.cmd
@@ -0,0 +1,23 @@
+@rem
+@rem To use this with Visual Studio .Net
+@rem Tools->External Tools...
+@rem Add
+@rem Title - Vim
+@rem Command - d:\files\util\vim_vs_net.cmd
+@rem Arguments - +$(CurLine) $(ItemPath)
+@rem Init Dir - Empty
+@rem
+@rem Courtesy of Brian Sturk
+@rem
+@rem --remote-silent +%1 is a command +954, move ahead 954 lines
+@rem --remote-silent %2 full path to file
+@rem In Vim
+@rem :h --remote-silent for more details
+@rem
+@rem --servername VS_NET
+@rem This will create a new instance of vim called VS_NET. So if you open
+@rem multiple files from VS, they will use the same instance of Vim.
+@rem This allows you to have multiple copies of Vim running, but you can
+@rem control which one has VS files in it.
+@rem
+start /b gvim.exe --servername VS_NET --remote-silent "%1" "%2"
diff --git a/runtime/tools/vimm b/runtime/tools/vimm
new file mode 100755
index 0000000..7b84cb2
--- /dev/null
+++ b/runtime/tools/vimm
@@ -0,0 +1,6 @@
+#!/bin/sh
+# enable DEC locator input model on remote terminal
+printf "\033[1;2'z\033[1;3'{\c"
+vim "$@"
+# disable DEC locator input model on remote terminal
+printf "\033[2;4'{\033[0'z\c"
diff --git a/runtime/tools/vimspell.sh b/runtime/tools/vimspell.sh
new file mode 100755
index 0000000..d336fe6
--- /dev/null
+++ b/runtime/tools/vimspell.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Spell a file & generate the syntax statements necessary to
+# highlight in vim. Based on a program from Krishna Gadepalli
+# <krishna@stdavids.picker.com>.
+#
+# I use the following mappings (in .vimrc):
+#
+# noremap <F8> :so `vimspell.sh %`<CR><CR>
+# noremap <F7> :syntax clear SpellErrors<CR>
+#
+# Neil Schemenauer <nascheme@ucalgary.ca>
+# March 1999
+# updated 2008 Jul 17 by Bram
+#
+# Safe method for the temp file by Javier Fernández-Sanguino_Peña
+
+INFILE=$1
+tmp="${TMPDIR-/tmp}"
+OUTFILE=`mktemp -t vimspellXXXXXX || tempfile -p vimspell || echo none`
+# If the standard commands failed then create the file
+# since we cannot create a directory (we cannot remove it on exit)
+# create a file in the safest way possible.
+if test "$OUTFILE" = none; then
+ OUTFILE=$tmp/vimspell$$
+ [ -e $OUTFILE ] && { echo "Cannot use temporary file $OUTFILE, it already exists!"; exit 1 ; }
+ (umask 077; touch $OUTFILE)
+fi
+# Note the copy of vimspell cannot be deleted on exit since it is
+# used by vim, otherwise it should do this:
+# trap "rm -f $OUTFILE" 0 1 2 3 9 11 13 15
+
+
+#
+# local spellings
+#
+LOCAL_DICT=${LOCAL_DICT-$HOME/local/lib/local_dict}
+
+if [ -f $LOCAL_DICT ]
+then
+ SPELL_ARGS="+$LOCAL_DICT"
+fi
+
+spell $SPELL_ARGS $INFILE | sort -u |
+awk '
+ {
+ printf "syntax match SpellErrors \"\\<%s\\>\"\n", $0 ;
+ }
+
+END {
+ printf "highlight link SpellErrors ErrorMsg\n\n" ;
+ }
+' > $OUTFILE
+echo "!rm $OUTFILE" >> $OUTFILE
+echo $OUTFILE
diff --git a/runtime/tools/vimspell.txt b/runtime/tools/vimspell.txt
new file mode 100644
index 0000000..2842af7
--- /dev/null
+++ b/runtime/tools/vimspell.txt
@@ -0,0 +1,22 @@
+vimspell.sh
+===========
+
+This is a simple script to spell check a file and generate the syntax
+statements necessary to highlight the errors in vim. It is based on a
+similar program by Krishna Gadepalli <krishna@stdavids.picker.com>.
+
+To use this script, first place it in a directory in your path. Next,
+you should add some convenient key mappings. I use the following (in
+.vimrc):
+
+ noremap <F8> :so `vimspell.sh %`<CR><CR>
+ noremap <F7> :syntax clear SpellErrors<CR>
+
+This program requires the old Unix "spell" command. On my Debian
+system, "spell" is a wrapper around "ispell". For better security,
+you should uncomment the line in the script that uses "tempfile" to
+create a temporary file. As all systems don't have "tempfile" the
+insecure "pid method" is used.
+
+
+ Neil Schemenauer <nascheme@ucalgary.ca>
diff --git a/runtime/tools/xcmdsrv_client.c b/runtime/tools/xcmdsrv_client.c
new file mode 100644
index 0000000..e1aea10
--- /dev/null
+++ b/runtime/tools/xcmdsrv_client.c
@@ -0,0 +1,578 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ * X-Windows communication by Flemming Madsen
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ *
+ * Client for sending commands to an '+xcmdsrv' enabled vim.
+ * This is mostly a de-Vimified version of if_xcmdsrv.c in vim.
+ * See that file for a protocol specification.
+ *
+ * You can make a test program with a Makefile like:
+ * xcmdsrv_client: xcmdsrv_client.c
+ * cc -o $@ -g -DMAIN -I/usr/X11R6/include -L/usr/X11R6/lib $< -lX11
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_SELECT
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#else
+#include <sys/poll.h>
+#endif
+#include <X11/Intrinsic.h>
+#include <X11/Xatom.h>
+
+/* Client API */
+char * sendToVim(Display *dpy, char *name, char *cmd, int asKeys, int *code);
+
+#ifdef MAIN
+/* A sample program */
+main(int argc, char **argv)
+{
+ char *res;
+ int code;
+
+ if (argc == 4)
+ {
+ if ((res = sendToVim(XOpenDisplay(NULL), argv[2], argv[3],
+ argv[1][0] != 'e', &code)) != NULL)
+ {
+ if (code)
+ printf("Error code returned: %d\n", code);
+ puts(res);
+ }
+ exit(0);
+ }
+ else
+ fprintf(stderr, "Usage: %s {k|e} <server> <command>", argv[0]);
+
+ exit(1);
+}
+#endif
+
+/*
+ * Maximum size property that can be read at one time by
+ * this module:
+ */
+
+#define MAX_PROP_WORDS 100000
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int x_error_check(Display *dpy, XErrorEvent *error_event);
+static int AppendPropCarefully(Display *display,
+ Window window, Atom property, char *value, int length);
+static Window LookupName(Display *dpy, char *name,
+ int delete, char **loose);
+static int SendInit(Display *dpy);
+static char *SendEventProc(Display *dpy, XEvent *eventPtr,
+ int expect, int *code);
+static int IsSerialName(char *name);
+
+/* Private variables */
+static Atom registryProperty = None;
+static Atom commProperty = None;
+static Window commWindow = None;
+static int got_x_error = FALSE;
+
+
+/*
+ * sendToVim --
+ * Send to an instance of Vim via the X display.
+ *
+ * Results:
+ * A string with the result or NULL. Caller must free if non-NULL
+ */
+
+ char *
+sendToVim(
+ Display *dpy, /* Where to send. */
+ char *name, /* Where to send. */
+ char *cmd, /* What to send. */
+ int asKeys, /* Interpret as keystrokes or expr ? */
+ int *code) /* Return code. 0 => OK */
+{
+ Window w;
+ Atom *plist;
+ XErrorHandler old_handler;
+#define STATIC_SPACE 500
+ char *property, staticSpace[STATIC_SPACE];
+ int length;
+ int res;
+ static int serial = 0; /* Running count of sent commands.
+ * Used to give each command a
+ * different serial number. */
+ XEvent event;
+ XPropertyEvent *e = (XPropertyEvent *)&event;
+ time_t start;
+ char *result;
+ char *loosename = NULL;
+
+ if (commProperty == None && dpy != NULL)
+ {
+ if (SendInit(dpy) < 0)
+ return NULL;
+ }
+
+ /*
+ * Bind the server name to a communication window.
+ *
+ * Find any survivor with a serialno attached to the name if the
+ * original registrant of the wanted name is no longer present.
+ *
+ * Delete any lingering names from dead editors.
+ */
+
+ old_handler = XSetErrorHandler(x_error_check);
+ while (TRUE)
+ {
+ got_x_error = FALSE;
+ w = LookupName(dpy, name, 0, &loosename);
+ /* Check that the window is hot */
+ if (w != None)
+ {
+ plist = XListProperties(dpy, w, &res);
+ XSync(dpy, False);
+ if (plist != NULL)
+ XFree(plist);
+ if (got_x_error)
+ {
+ LookupName(dpy, loosename ? loosename : name,
+ /*DELETE=*/TRUE, NULL);
+ continue;
+ }
+ }
+ break;
+ }
+ if (w == None)
+ {
+ fprintf(stderr, "no registered server named %s\n", name);
+ return NULL;
+ }
+ else if (loosename != NULL)
+ name = loosename;
+
+ /*
+ * Send the command to target interpreter by appending it to the
+ * comm window in the communication window.
+ */
+
+ length = strlen(name) + strlen(cmd) + 10;
+ if (length <= STATIC_SPACE)
+ property = staticSpace;
+ else
+ property = (char *) malloc((unsigned) length);
+
+ serial++;
+ sprintf(property, "%c%c%c-n %s%c-s %s",
+ 0, asKeys ? 'k' : 'c', 0, name, 0, cmd);
+ if (name == loosename)
+ free(loosename);
+ if (!asKeys)
+ {
+ /* Add a back reference to our comm window */
+ sprintf(property + length, "%c-r %x %d", 0, (uint) commWindow, serial);
+ length += strlen(property + length + 1) + 1;
+ }
+
+ res = AppendPropCarefully(dpy, w, commProperty, property, length + 1);
+ if (length > STATIC_SPACE)
+ free(property);
+ if (res < 0)
+ {
+ fprintf(stderr, "Failed to send command to the destination program\n");
+ return NULL;
+ }
+
+ if (asKeys) /* There is no answer for this - Keys are sent async */
+ return NULL;
+
+
+ /*
+ * Enter a loop processing X events & pooling chars until we see the result
+ */
+
+#define SEND_MSEC_POLL 50
+
+ time(&start);
+ while ((time((time_t *) 0) - start) < 60)
+ {
+ /* Look out for the answer */
+#ifndef HAVE_SELECT
+ struct pollfd fds;
+
+ fds.fd = ConnectionNumber(dpy);
+ fds.events = POLLIN;
+ if (poll(&fds, 1, SEND_MSEC_POLL) < 0)
+ break;
+#else
+ fd_set fds;
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = SEND_MSEC_POLL * 1000;
+ FD_ZERO(&fds);
+ FD_SET(ConnectionNumber(dpy), &fds);
+ if (select(ConnectionNumber(dpy) + 1, &fds, NULL, NULL, &tv) < 0)
+ break;
+#endif
+ while (XEventsQueued(dpy, QueuedAfterReading) > 0)
+ {
+ XNextEvent(dpy, &event);
+ if (event.type == PropertyNotify && e->window == commWindow)
+ if ((result = SendEventProc(dpy, &event, serial, code)) != NULL)
+ return result;
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * SendInit --
+ * This procedure is called to initialize the
+ * communication channels for sending commands and
+ * receiving results.
+ */
+
+ static int
+SendInit(Display *dpy)
+{
+ XErrorHandler old_handler;
+
+ /*
+ * Create the window used for communication, and set up an
+ * event handler for it.
+ */
+ old_handler = XSetErrorHandler(x_error_check);
+ got_x_error = FALSE;
+
+ commProperty = XInternAtom(dpy, "Comm", False);
+ /* Change this back to "InterpRegistry" to talk to tk processes */
+ registryProperty = XInternAtom(dpy, "VimRegistry", False);
+
+ if (commWindow == None)
+ {
+ commWindow =
+ XCreateSimpleWindow(dpy, XDefaultRootWindow(dpy),
+ getpid(), 0, 10, 10, 0,
+ WhitePixel(dpy, DefaultScreen(dpy)),
+ WhitePixel(dpy, DefaultScreen(dpy)));
+ XSelectInput(dpy, commWindow, PropertyChangeMask);
+ }
+
+ XSync(dpy, False);
+ (void) XSetErrorHandler(old_handler);
+
+ return got_x_error ? -1 : 0;
+}
+
+/*
+ * LookupName --
+ * Given an interpreter name, see if the name exists in
+ * the interpreter registry for a particular display.
+ *
+ * Results:
+ * If the given name is registered, return the ID of
+ * the window associated with the name. If the name
+ * isn't registered, then return 0.
+ */
+
+ static Window
+LookupName(
+ Display *dpy, /* Display whose registry to check. */
+ char *name, /* Name of an interpreter. */
+ int delete, /* If non-zero, delete info about name. */
+ char **loose) /* Do another search matching -999 if not found
+ Return result here if a match is found */
+{
+ unsigned char *regProp, *entry;
+ unsigned char *p;
+ int result, actualFormat;
+ unsigned long numItems, bytesAfter;
+ Atom actualType;
+ Window returnValue;
+
+ /*
+ * Read the registry property.
+ */
+
+ regProp = NULL;
+ result = XGetWindowProperty(dpy, RootWindow(dpy, 0), registryProperty, 0,
+ MAX_PROP_WORDS, False, XA_STRING, &actualType,
+ &actualFormat, &numItems, &bytesAfter,
+ &regProp);
+
+ if (actualType == None)
+ return 0;
+
+ /*
+ * If the property is improperly formed, then delete it.
+ */
+
+ if ((result != Success) || (actualFormat != 8) || (actualType != XA_STRING))
+ {
+ if (regProp != NULL)
+ XFree(regProp);
+ XDeleteProperty(dpy, RootWindow(dpy, 0), registryProperty);
+ return 0;
+ }
+
+ /*
+ * Scan the property for the desired name.
+ */
+
+ returnValue = None;
+ entry = NULL; /* Not needed, but eliminates compiler warning. */
+ for (p = regProp; (p - regProp) < numItems; )
+ {
+ entry = p;
+ while ((*p != 0) && (!isspace(*p)))
+ p++;
+ if ((*p != 0) && (strcasecmp(name, p + 1) == 0))
+ {
+ sscanf(entry, "%x", (uint*) &returnValue);
+ break;
+ }
+ while (*p != 0)
+ p++;
+ p++;
+ }
+
+ if (loose != NULL && returnValue == None && !IsSerialName(name))
+ {
+ for (p = regProp; (p - regProp) < numItems; )
+ {
+ entry = p;
+ while ((*p != 0) && (!isspace(*p)))
+ p++;
+ if ((*p != 0) && IsSerialName(p + 1)
+ && (strncmp(name, p + 1, strlen(name)) == 0))
+ {
+ sscanf(entry, "%x", (uint*) &returnValue);
+ *loose = strdup(p + 1);
+ break;
+ }
+ while (*p != 0)
+ p++;
+ p++;
+ }
+ }
+
+ /*
+ * Delete the property, if that is desired (copy down the
+ * remainder of the registry property to overlay the deleted
+ * info, then rewrite the property).
+ */
+
+ if ((delete) && (returnValue != None))
+ {
+ int count;
+
+ while (*p != 0)
+ p++;
+ p++;
+ count = numItems - (p-regProp);
+ if (count > 0)
+ memcpy(entry, p, count);
+ XChangeProperty(dpy, RootWindow(dpy, 0), registryProperty, XA_STRING,
+ 8, PropModeReplace, regProp,
+ (int) (numItems - (p-entry)));
+ XSync(dpy, False);
+ }
+
+ XFree(regProp);
+ return returnValue;
+}
+
+ static char *
+SendEventProc(
+ Display *dpy,
+ XEvent *eventPtr, /* Information about event. */
+ int expected, /* The one were waiting for */
+ int *code) /* Return code. 0 => OK */
+{
+ unsigned char *propInfo;
+ unsigned char *p;
+ int result, actualFormat;
+ int retCode;
+ unsigned long numItems, bytesAfter;
+ Atom actualType;
+
+ if ((eventPtr->xproperty.atom != commProperty)
+ || (eventPtr->xproperty.state != PropertyNewValue))
+ {
+ return;
+ }
+
+ /*
+ * Read the comm property and delete it.
+ */
+
+ propInfo = NULL;
+ result = XGetWindowProperty(dpy, commWindow, commProperty, 0,
+ MAX_PROP_WORDS, True, XA_STRING, &actualType,
+ &actualFormat, &numItems, &bytesAfter,
+ &propInfo);
+
+ /*
+ * If the property doesn't exist or is improperly formed
+ * then ignore it.
+ */
+
+ if ((result != Success) || (actualType != XA_STRING)
+ || (actualFormat != 8))
+ {
+ if (propInfo != NULL)
+ {
+ XFree(propInfo);
+ }
+ return;
+ }
+
+ /*
+ * Several commands and results could arrive in the property at
+ * one time; each iteration through the outer loop handles a
+ * single command or result.
+ */
+
+ for (p = propInfo; (p - propInfo) < numItems; )
+ {
+ /*
+ * Ignore leading NULs; each command or result starts with a
+ * NUL so that no matter how badly formed a preceding command
+ * is, we'll be able to tell that a new command/result is
+ * starting.
+ */
+
+ if (*p == 0)
+ {
+ p++;
+ continue;
+ }
+
+ if ((*p == 'r') && (p[1] == 0))
+ {
+ int serial, gotSerial;
+ char *res;
+
+ /*
+ * This is a reply to some command that we sent out. Iterate
+ * over all of its options. Stop when we reach the end of the
+ * property or something that doesn't look like an option.
+ */
+
+ p += 2;
+ gotSerial = 0;
+ res = "";
+ retCode = 0;
+ while (((p-propInfo) < numItems) && (*p == '-'))
+ {
+ switch (p[1])
+ {
+ case 'r':
+ if (p[2] == ' ')
+ res = p + 3;
+ break;
+ case 's':
+ if (sscanf(p + 2, " %d", &serial) == 1)
+ gotSerial = 1;
+ break;
+ case 'c':
+ if (sscanf(p + 2, " %d", &retCode) != 1)
+ retCode = 0;
+ break;
+ }
+ while (*p != 0)
+ p++;
+ p++;
+ }
+
+ if (!gotSerial)
+ continue;
+
+ if (code != NULL)
+ *code = retCode;
+ return serial == expected ? strdup(res) : NULL;
+ }
+ else
+ {
+ /*
+ * Didn't recognize this thing. Just skip through the next
+ * null character and try again.
+ * Also, throw away commands that we can't process anyway.
+ */
+
+ while (*p != 0)
+ p++;
+ p++;
+ }
+ }
+ XFree(propInfo);
+}
+
+/*
+ * AppendPropCarefully --
+ *
+ * Append a given property to a given window, but set up
+ * an X error handler so that if the append fails this
+ * procedure can return an error code rather than having
+ * Xlib panic.
+ *
+ * Return:
+ * 0 on OK - -1 on error
+ *--------------------------------------------------------------
+ */
+
+ static int
+AppendPropCarefully(
+ Display *dpy, /* Display on which to operate. */
+ Window window, /* Window whose property is to
+ * be modified. */
+ Atom property, /* Name of property. */
+ char *value, /* Characters to append to property. */
+ int length) /* How much to append */
+{
+ XErrorHandler old_handler;
+
+ old_handler = XSetErrorHandler(x_error_check);
+ got_x_error = FALSE;
+ XChangeProperty(dpy, window, property, XA_STRING, 8,
+ PropModeAppend, value, length);
+ XSync(dpy, False);
+ (void) XSetErrorHandler(old_handler);
+ return got_x_error ? -1 : 0;
+}
+
+
+/*
+ * Another X Error handler, just used to check for errors.
+ */
+/* ARGSUSED */
+ static int
+x_error_check(Display *dpy, XErrorEvent *error_event)
+{
+ got_x_error = TRUE;
+ return 0;
+}
+
+/*
+ * Check if "str" looks like it had a serial number appended.
+ * Actually just checks if the name ends in a digit.
+ */
+ static int
+IsSerialName(char *str)
+{
+ int len = strlen(str);
+
+ return (len > 1 && isdigit(str[len - 1]));
+}