updated for version 7.0001
diff --git a/runtime/tools/README.txt b/runtime/tools/README.txt
new file mode 100644
index 0000000..3133f13
--- /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.
+
+tcltags:	Shell + Awk script to create a tags file from a TCL file.
+
+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.
+
+[xxd (and tee for OS/2) 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..1ffd848
--- /dev/null
+++ b/runtime/tools/blink.c
@@ -0,0 +1,21 @@
+/*
+ * 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>
+
+main()
+{
+	while (1)
+	{
+		printf("\e[?25h");
+		fflush(stdout);
+		usleep(400000);		/* on time */
+		printf("\e[?25l");
+		fflush(stdout);
+		usleep(250000);		/* off time */
+	}
+}
diff --git a/runtime/tools/ccfilter.1 b/runtime/tools/ccfilter.1
new file mode 100644
index 0000000..e3de38d
--- /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 it's 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..2703339
--- /dev/null
+++ b/runtime/tools/ccfilter.c
@@ -0,0 +1,326 @@
+/* ======================================================================= */
+/*  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 %[^:]:%u:",
+			       FileName, &Row );
+	    if ( rv == 2 )
+	      {
+		ok = (echogets(Reason, echo) != NULL);
+	      }
+	    else
+#endif
+	      {
+		if ((rv = sscanf( Line, "%[^:]:%u: warning: %[^\n]",
+				   FileName, &Row, Reason ))==3) {
+		 Severity = 'w';
+		} else {
+		rv = sscanf( Line, "%[^:]:%u: %[^\n]",
+				   FileName, &Row, Reason );
+		}
+		ok = ( rv == 3 );
+	      }
+	    Col = (dec_col ? 1 : 0 );
+	    break;
+	  case COMPILER_AIX:
+	    rv = sscanf( Line, "\"%[^\"]\", line %u.%u: %*s (%c) %[^\n]",
+			       FileName, &Row, &Col, &Severity, Reason );
+	    ok = ( rv == 5 );
+	    break;
+	  case COMPILER_HPUX:
+	    rv = sscanf( Line, "cc: \"%[^\"]\", line %u: %c%*[^:]: %[^\n]",
+			       FileName, &Row, &Severity, Reason );
+	    ok = ( rv == 4 );
+	    Col = (dec_col ? 1 : 0 );
+	    break;
+	  case COMPILER_SOLARIS:
+	    rv = sscanf( Line, "\"%[^\"]\", line %u: warning: %[^\n]",
+			       FileName, &Row, Reason );
+	    Severity = 'w';
+	    ok = ( rv == 3 );
+	    if ( rv != 3 )
+	      {
+		rv = sscanf( Line, "\"%[^\"]\", line %u: %[^\n]",
+				   FileName, &Row, Reason );
+		Severity = 'e';
+		ok = ( rv == 3 );
+	      }
+	    Col = (dec_col ? 1 : 0 );
+	    break;
+	  case COMPILER_ATT:
+	    rv	 = sscanf( Line, "%c \"%[^\"]\",L%u/C%u%*[^:]:%[^\n]",
+				 &Severity, FileName, &Row, &Col, Reason );
+	    ok = ( rv == 5 );
+
+	    if (rv != 5)
+	      { rv   = sscanf( Line, "%c \"%[^\"]\",L%u/C%u: %[^\n]",
+				     &Severity, FileName, &Row, &Col, Reason );
+		ok = ( rv == 5 );
+	      }
+
+	    if (rv != 5)
+	      { rv  = sscanf( Line, "%c \"%[^\"]\",L%u: %[^\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, "%[^:]: %u: %[^\n]",
+				 FileName, &Row, Reason );
+		    if (rv != 3)
+		      rv = sscanf( p+2, "%[^,], line %u: %[^\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", ok, Line );
+	}
+       else
+	{
+	  for (p=Reason; (*p) && (isspace(*p)); p++);
+	  if ( BasePath[CWDlen] == 0 )
+	      printf( "%s:%u:%u:%c:%s\n", FileName, Row, Col, Severity, p );
+	  else
+	    {
+	      printf( "%s/%s:%u:%u:%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..ff658c9
--- /dev/null
+++ b/runtime/tools/ccfilter_README.txt
@@ -0,0 +1,91 @@
+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 it's 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/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..570d6e7
--- /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 (©) 2001 by Jörg 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/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..314df88
--- /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 wont
+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..48dcdc7
--- /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
+# Last Changed:	96/11/27 19:46:06
+#
+# "@(#) shtags 1.1 by S. Riehm"
+#
+
+# 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.1, 96/11/27, 19:46:06' );
+    $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,.*/,,;
+require 'getopts.pl';
+
+#
+# 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 aswell, 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/tcltags b/runtime/tools/tcltags
new file mode 100755
index 0000000..9524152
--- /dev/null
+++ b/runtime/tools/tcltags
@@ -0,0 +1,159 @@
+#!/bin/sh
+# vim:ts=4:
+# Generates a tag file for TCL code. Slow, but gets the job done.
+#
+# Written by Darren Hiebert <darren@hiebert.com>
+
+program_name=`basename $0`
+program_version="0.3"
+program_author="Darren Hiebert"
+author_email="darren@hiebert.com"
+tmp_tagfile=/tmp/${program_name}.$$
+
+usage="\
+Usage: $program_name [-au] [-{f|o} tagfile] [--format=n] file(s)
+  -a          append to current tag file
+  -f tagfile  specify output tag file name (default=tags)
+  -o          alternative for -f
+  -u          unsorted
+  --format=n  specify tag file format (default=2)
+  --help      print this help message
+"
+
+# defaults
+#
+append=0
+format=2
+sorted=1
+tagfile=tags
+filelist=
+
+# read options
+#
+getparam()
+{
+	if [ -n "$1" ]; then
+		# set variable to word passed in
+		echo "$2='$1'; opt="
+	else
+		# set variable to next word on command line
+		echo "$2="'$1'"; shift"
+	fi
+}
+
+finished=0
+while [ $# -gt 0  -a  $finished -eq 0 ]
+do
+    case "$1" in
+		--*)
+			opt=`echo "$1" | cut -c 3-`
+			shift
+			opt_name=`echo "$opt" | awk -F= '{print $1}'`
+			opt_value=`echo "$opt" | awk -F= '{print $2}'`
+			case "$opt_name" in
+				format) case "$opt_value" in
+							1|2) format=$opt_value;;
+							*) echo "--$opt: unsupported value" >&2; exit 1;;
+						esac
+						;;
+				help)	echo "$usage"; exit 0;;
+				*)		echo "$opt_name: unsupported option" >&2; exit 1;;
+			esac
+			;;
+		-*)
+			# chop off leading '-'
+			opt=`echo "$1" | cut -c 2-`
+			shift
+			while [ -n "$opt" ]
+			do
+				opt_char=`echo "$opt" | cut -c 1`
+				opt=`echo "$opt" | cut -c 2-`
+				case "$opt_char" in
+					a) append=1;;
+					u) sorted=0;;
+					o|f) eval `getparam "$opt" tagfile`;;
+					*) echo "$opt: unsupported option" >&2; exit 1;;
+				esac
+			done
+			;;
+		*) filelist="$*"; break;;
+    esac
+done
+
+if [ -z "$filelist" ] ;then
+    echo "$usage" >&2; exit 1
+fi
+
+# awk program for generating tags
+#
+ext_flags=""
+ttype=""
+if [ $format -eq 2 ] ;then
+    ext_flags=';\"	%s'
+    ttype=", type"
+fi
+awkprg='
+function trim_comment(string) {
+    comment = index(string, "#")
+    if (comment != 0)
+	string = substr(string, 0, comment-1)
+    return string
+}
+function maketag(tagname, pattern, type, line_end) {
+    gsub(/\\/, "\\\\", pattern)
+    gsub(/\//, "\\/", pattern)
+    if (line_end)
+	terminator="$"
+    else
+	terminator=""
+    printf("%s\t%s\t/^%s%s/'"$ext_flags"'\n", \
+		tagname, FILENAME, pattern, terminator'"$ttype"')
+}
+$1 == "proc"  &&  $3 ~ /^{/  {
+    pattern = substr($0, 0, index($0, "{"))
+    maketag($2, pattern, "f", 0)
+}
+/^set[ \t]/  &&  $2 !~ /\(/ {
+    pattern = substr($0, 0, index($0, $2) + length($2))
+    maketag($2, pattern, "v", 0)
+}
+/^array[ \t]*set[ \t]/  &&  $3 !~ /\(/ {
+    pattern = substr($0, 0, index($0, $3) + length($3))
+    maketag($3, pattern, "v", 0)
+}'
+
+# add or correct the pseudo tags
+#
+if [ "$tagfile" != "-" ] ;then
+    if [ $append -eq 1 ]; then
+	# ensure existing sort flag is correct
+	sed -e "/^!_TAG_FILE_SORTED/s/	[01]	/	$sorted	/" \
+	    -e "/^!_TAG_FILE_FORMAT/s/	1	/	$format	/" \
+	    $tagfile > $tmp_tagfile
+    else
+	echo -ne "\
+!_TAG_FILE_FORMAT	$format	/extended format; --format=1 will not append ;\" to lines/
+!_TAG_FILE_SORTED	$sorted	/0=unsorted, 1=sorted/
+!_TAG_PROGRAM_AUTHOR	$program_author	/$author_email/
+!_TAG_PROGRAM_NAME	$program_name	//
+!_TAG_PROGRAM_VERSION	$program_version	//
+" > $tmp_tagfile
+    fi
+fi
+
+# generate tags
+#
+awk "$awkprg" $filelist >> $tmp_tagfile
+
+if [ $sorted -eq 1 ] ;then
+    sort -u -o $tmp_tagfile $tmp_tagfile
+fi
+
+if [ "$tagfile" = '-' ] ;then
+    cat $tmp_tagfile
+else
+    cp $tmp_tagfile $tagfile
+fi
+rm $tmp_tagfile
+
+exit 0
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..6307d95
--- /dev/null
+++ b/runtime/tools/vim_vs_net.cmd
@@ -0,0 +1,24 @@
+@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 Coutesy 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 mor 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..042c4a0
--- /dev/null
+++ b/runtime/tools/vimm
@@ -0,0 +1,6 @@
+#!/bin/sh
+# enable DEC locator input model on remote terminal
+echo  "\033[1;2'z\033[1;3'{\c"
+vim "$@"
+# disable DEC locator input model on remote terminal
+echo  "\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..4207222
--- /dev/null
+++ b/runtime/tools/vimspell.sh
@@ -0,0 +1,41 @@
+#!/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
+
+INFILE=$1
+OUTFILE=/tmp/vimspell.$$
+# if you have "tempfile", use the following line
+#OUTFILE=`tempfile`
+
+#
+# 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..a0e6211
--- /dev/null
+++ b/runtime/tools/xcmdsrv_client.c
@@ -0,0 +1,584 @@
+/* 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>
+
+#define __ARGS(x) x
+
+/* Client API */
+char * sendToVim __ARGS((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 __ARGS((Display *dpy, XErrorEvent *error_event));
+static int	AppendPropCarefully __ARGS((Display *display,
+		    Window window, Atom property, char *value, int length));
+static Window	LookupName __ARGS((Display *dpy, char *name,
+		    int delete, char **loose));
+static int	SendInit __ARGS((Display *dpy));
+static char	*SendEventProc __ARGS((Display *dpy, XEvent *eventPtr,
+				      int expect, int *code));
+static int	IsSerialName __ARGS((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(dpy, name, cmd, asKeys, code)
+    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(dpy)
+    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(dpy, name, delete, loose)
+    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(dpy, eventPtr, expected, code)
+    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 cant 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(dpy, window, property, value, length)
+    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(dpy, error_event)
+    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(str)
+    char   *str;
+{
+    int len = strlen(str);
+
+    return (len > 1 && isdigit(str[len - 1]));
+}